首頁 > 後端開發 > php教程 > 「小白」帶你們了解有關於Nginx的模組與工作原理吧! ! !

「小白」帶你們了解有關於Nginx的模組與工作原理吧! ! !

慕斯
發布: 2023-04-10 06:28:02
轉載
3317 人瀏覽過

NGINX以高效能的負載平衡器,緩存,和web伺服器聞名,驅動了全球超過40% 最繁忙的網站,因此

有一定的參考價值,有需要的朋友可以參考一下,希望對大家有幫助

「小白」帶你們了解有關於Nginx的模組與工作原理吧! ! !

一.  Nginx的模組與工作原理


Nginx由內核和模組組成,其中,內核的設計非常微小和簡潔,完成的工作也非常簡單,僅僅通過查找配置文件將客戶端請求映射到一個location block(location是Nginx配置中的一個指令,用於URL匹配),而在這個location中所配置的每個指令將會啟動不同的模組去完成相應的工作。

Nginx的模組從結構上分為核心模組、基礎模組和第三方模組:

#核心模組:HTTP模組、EVENT模組和MAIL模組

基礎模組:HTTP Access模組、HTTP FastCGI模組、HTTP Proxy模組和HTTP Rewrite模組,

第三方模組:HTTP Upstream Request Hash模組、Notice模組和HTTP Access Key模組。

使用者根據自己的需求開發的模組都屬於第三方模組。正是有了這麼多模組的支撐,Nginx的功能才會如此強大。

Nginx的模組從功能上分為如下三類。

Handlers(處理器模組)。此類模組直接處理請求,並進行輸出內容和修改headers資訊等操作。 Handlers處理器模組一般只能有一個。

Filters (過濾器模組)。此類模組主要對其他處理器模組輸出的內容進行修改操作,最後由Nginx輸出。

Proxies (代理類別模組)。這類模組是Nginx的HTTP Upstream之類的模組,這些模組主要與後端一些服務例如FastCGI等進行交互,實現服務代理和負載平衡等功能。

圖1-1展示了Nginx模組常規的HTTP請求和回應的過程。

      

#Nginx本身所做的工作實際上很少,當它接到一個HTTP請求時,它只是透過查找設定檔將此一請求會對應到一個location block,而此location中所配置的各個指令則會啟動不同的模組去完成工作,因此模組可以看做Nginx真正的勞動工作者。通常一個location中的指令會涉及一個handler模​​組和多個filter模組(當然,多個location可以重複使用同一個模組)。 handler模​​組負責處理請求,完成回應內容的生成,而filter模組則對回應內容進行處理。

Nginx的模組直接被編譯進Nginx,因此屬於靜態編譯方式。啟動Nginx後,Nginx的模組會自動加載,不像Apache,首先將模組編譯為一個so文件,然後在設定檔中指定是否進行載入。在解析設定檔時,Nginx的每個模組都有可能去處理某個請求,但是同一個處理請求只能由一個模組來完成。

 

 

二.  Nginx的行程模型


在運作方式上,Nginx分為單一工作流程和多工作進程兩種模式。在單一工作進程模式下,除主進程外,還有一個工作進程,工作進程是單執行緒的;在多工作進程模式下,每個工作進程包含多個執行緒。 Nginx預設為單一工作進程模式。

Nginx在啟動後,會有一個master進程和多個worker進程。

1、master進程:管理進程

  master進程主要用來管理worker進程,具體包括以下4個主要功能:
        (1)接收來自外部的訊號。
        (2)向各worker進程發出訊號。
        (3)監控woker進程的運作狀態。
        (4)當woker進程退出後(異常情況下),會自動重新啟動新的woker進程。

使用者互動接口:master程序充當整個進程組與使用者的交互接口,同時對進程進行監護。它不需要處理網路事件,不負責業務的執行,只會透過管理worker進程來實現重啟服務、平滑升級、更換日誌檔案、設定檔即時生效等功能。

重啟work進程:我們要控制nginx,只需要透過kill向master進程發送訊號就行了。例如kill -HUP pid,則是告訴nginx,從容地重啟nginx,我們一般用這個訊號來重啟nginx,或重新載入配置,因為是從容地重啟,因此服務是不中斷的。

master進程在接收到HUP訊號後是怎麼做的呢?

1)、首先master進程在接到訊號後,會先重新載入設定文件,然後再啟動新的worker進程,並向所有舊的worker進程發送訊號,告訴他們可以光榮退休了。

2)、新的worker在啟動後,就開始接收新的請求,而舊的worker在收到來自master的訊號後,就不再接收新的請求,並且在當前進程中的所有未處理完的請求處理完成後,再退出。

直接給master程序發送訊號,這是比較傳統的操作方式,nginx在0.8版本之後,引入了一系列命令列參數,來方便我們管理。 例如,./nginx -s reload,就是來重啟nginx,./nginx -s stop,就是來停止nginx的運作。 如何做到的呢?我們還是拿reload來說,我們看到,執行指令時,我們是啟動一個新的nginx進程,而新的nginx進程在解析到reload參數後,就知道我們的目的是控制nginx來重新載入設定檔了,它會向master進程發送訊號,然後接下來的動作,就跟我們直接向master進程發送訊號一樣了。

2、worker進程:處理請求

而而基本的網路事件,則放在worker進程中來處理了。多個worker進程之間是對等的,他們同等競爭來自客戶端的請求,各進程相互之間是獨立的。一個請求,只可能在一個worker進程中處理,一個worker進程,不可能處理其它進程的請求。 worker進程的數量是可以設定的,一般我們會設定與機器cpu核數一致,這裡面的原因與nginx的進程模型以及事件處理模型是分不開的。

worker進程之間是平等的,每個進程,處理請求的機會也是一樣的。當我們提供80埠的http服務時,一個連接請求過來,每個行程都有可能處理這個連接,怎麼做到的呢?

 Nginx採用非同步非阻塞的方式來處理網路事件,類似Libevent,具體流程如下:

1)接收請求:首先,每個worker進程都是從master行程fork過來,在master行程建立好需要listen的socket(listenfd)之後,再fork出多個worker行程。所有worker進程的listenfd會在新連線到來時變得可讀,每個work進程都可以去accept這個socket(listenfd)。當一個client連線到來時,所有accept的work流程都會被通知,但只有一個行程可以accept成功,而其它的則會accept失敗。為確保只有一個進程處理該連接,Nginx提供了一把共享鎖accept_mutex來保證同一時刻只有一個work進程在accept連接。所有worker程序在註冊listenfd讀取事件前搶accept_mutex,搶到互斥鎖的那個程序註冊listenfd讀取事件,在讀取事件裡呼叫accept接受該連線。

2)處理請求:當一個worker進程在accept這個連線之後,就開始讀取請求,解析請求,處理請求,產生資料後,再回傳給客戶端,最後才斷開連接,這樣一個完整的請求就是這樣的了。

我們可以看到,一個請求,完全由worker進程來處理,而且只在一個worker進程中處理。 worker進程之間是平等的,每個進程,處理請求的機會也是一樣的。

nginx的進程模型,可以由下圖來表示:

三.   Nginx為啥效能高-多進程IO模型


      參考http://mp.weixin.qq.com/s?__biz=MjM5NTg2NTU0Ng==&mid=407889757&idx=3&sn=cfa8a70a5fd2a674a910766780808202020202020pSG Hfve#rd

1 、nginx採用多進程模型好處

      首先,對於每個worker進程來說,獨立的進程,不需要加鎖,所以省掉了鎖帶來的開銷,同時在程式設計以及問題查找時,也會方便很多。

      其次,採用獨立的進程,可以讓互相之間不會影響,一個進程退出後,其它進程還在工作,服務不會中斷,master進程則很快啟動新的worker進程。當然,worker進程的異常退出,肯定是程式有bug了,異常退出,會導致目前worker上的所有請求失敗,不過不會影響到所有請求,所以降低了風險。

2、nginx多重行程事件模型:非同步非阻塞


##雖然nginx採用多worker的方式來處理請求,每個worker裡面只有一個主線程,那能夠處理的並發數很有限啊,多少個worker就能處理多少個並發,何來高並發呢?非也,這就是nginx的高明之處,nginx採用了非同步非阻塞的方式來處理請求,也就是說,nginx是可​​以同時處理成千上萬個請求的。 一個worker進程可以同時處理的請求數只受限於記憶體大小,而且在架構設計上,不同的worker進程之間處理並發請求時幾乎沒有同步鎖的限制,worker進程通常不會進入睡眠狀態,因此,當Nginx上的進程數與CPU核心數相等時(最好每一個worker進程都綁定特定的CPU核心),進程間切換的代價是最小的。

       而apache的常用運作方式(apache也有非同步非阻塞版本,但因為與自備某些模組衝突,所以不常用),每個進程在一個時刻只處理一個請求因此,當並發數上到幾千時,就同時有幾千的進程在處理請求了。這對作業系統來說,是個不小的挑戰,行程帶來的記憶體佔用非常大,行程的上下文切換帶來的cpu開銷很大,自然效能就上不去了,而這些開銷完全是沒有意義的。

     

 

         為什麼nginx可以使用非同步非阻塞的方式來處理呢,或非同步非阻塞到底是怎麼回事?請參閱:使用libevent 和libev 提高網路應用效能-I/O模型演進變化史

         我們先回到原點,看看一個請求的完整過程:首先,請求過來,要建立連接,然後再接收數據,接收數據後,再發送數據。

         具體到系統底層,就是讀寫事件,而當讀寫事件沒有準備好時,必然不可操作,如果不用非阻塞的方式來調用,那就得阻塞調用了,事件沒有準備好,那就只能等了,等事件準備好了,你再繼續吧。阻塞呼叫會進入核心等待,cpu就會讓出去給別人用了,對單線程的worker來說,顯然不合適,當網路事件越多時,大家都在等待呢,cpu空閒下來沒人用,cpu利用率自然上不去了,更別談高並發了。好吧,你說加進程數,這跟apache的執行緒模型有什麼差別,注意,不要增加無謂的上下文切換。所以,在nginx裡面,最忌諱阻塞的系統呼叫了。不要阻塞,那就非阻塞嘍。非阻塞就是,事件還沒準備好,馬上回到EAGAIN,告訴你,事件還沒準備好呢,你慌什麼,過會再來吧。好吧,你過一會,再來檢查一下事件,直到事件準備好了為止,在這期間,你就可以先去做其它事情,然後再來看看事件好了沒。雖然不阻塞了,但你得不時地過來檢查一下事件的狀態,你可以做更多的事情了,但帶來的開銷也是不小的。

關於IO模型:http://blog.csdn.net/hguisu/article/details/7453390

     #  nginx支援的事件模型如下(nginx的wiki):

       Nginx支援下列處理連線的方法(I/O重複使用方法),這些方法可以透過use指令指定。

  • select– 標準方法。如果目前平台沒有更有效的方法,它是編譯時預設的方法。你可以使用設定參數 –with-select_module 和 –without-select_module 來啟用或停用這個模組。
  • poll– 標準方法。如果目前平台沒有更有效的方法,它是編譯時預設的方法。你可以使用設定參數 –with-poll_module 和 –without-poll_module 來啟用或停用這個模組。
  • kqueue– 高效率的方法,使用於FreeBSD 4.1 , OpenBSD 2.9 , NetBSD 2.0 和MacOS X. 使用雙處理器的MacOS X系統使用kqueue可能會造成內核崩潰。
  • epoll 高效率的方法,使用Linux核心2.6版本及以後的系統。在某些發行版本中,如SuSE 8.2, 有讓2.4版本的核心支援epoll的補丁。
  • rtsig 可執行的即時訊號,使用於Linux核心版本2.2.19以後的系統。預設整個系統中不能出現大於1024個POSIX即時(排隊)訊號。這種情況 對於高負載的伺服器來說是低效的;所以有必要透過調節核心參數 /proc/sys/kernel/rtsig-max 來增加佇列的大小。可是從Linux核心版本2.6.6-mm2開始, 這個參數就不再使用了,而且對於每個行程有一個獨立的訊號佇列,這個佇列的大小可以用 RLIMIT_SIGPENDING 參數調整。當這個佇列過於擁塞,nginx就放棄它並且開始使用 poll 方法來處理連線直到恢復正常。
  • /dev/poll – 高效率的方法,使用於Solaris 7 11/99 , HP/UX 11.22 (eventport), IRIX 6.5.15 和Tru64 UNIX 5.1A .
  • eventport – 高效率的方法,使用於Solaris 10. 為了防止核心崩潰的問題, 有必要安裝這個安全性修補程式。

        在linux下面,只有epoll是高效率的方法

        下面再來看看epoll是如何有效的
     改進的poll。要使用epoll只需要這三個系統呼叫:epoll_create(2), epoll_ctl(2), epoll_wait(2)。它是在2.5.44核心中被引進的(epoll(4) is a new API introduced in Linux kernel 2.5.44),在2.6核心中廣泛應用。

        epoll的優點

  • 支援一個行程開啟大數目的socket描述子(FD)

      的是一個行程所開啟的FD是有一定限制的,由FD_SETSIZE設置,預設值是2048。對於那些需要支援的上萬連線數目的IM伺服器來說顯 然太少了。這時候你一是可以選擇修改這個巨集然後重新編譯內核,不過資料也同時指出這樣會帶來網路效率的下降,二是可以選擇多進程的解決方案(傳統的Apache方案),不過雖然linux上面創建進程的代價比較小,但仍舊是不可忽視的,加上進程間資料同步遠比不上執行緒間同步的高效,所以也不是完美的方案。不過epoll則沒有這個限制,它所支援的FD上限是最大可以開啟檔案的數目,這個數字一般遠大於2048,舉個例子,在1GB記憶體的機器上大約是10萬左右,具體數目可以cat/proc /sys/fs/file-max察看,一般來說這個數目和系統記憶體關係很大。

  • IO效率不隨FD數目增加而線性下降

         傳統的select/poll另一個致命弱點就是當你擁有一個很大的socket集合,不過由於網路延遲,任一時間只有部分的socket是」活躍」的,但是select/poll每次調用都會線性掃描全部的集合,導致效率呈現線性下降。但epoll不存在這個問題,它只會對」活躍」的socket進行操 作—這是因為在內核實作中epoll是根據每個fd上面的callback函數實現的。那麼,只有」活躍」的socket才會主動的去呼叫 callback函數,其他idle狀態socket則不會,在這點上,epoll實現了一個」偽」AIO,因為這時候推動力在os核心。在某些 benchmark中,如果所有的socket基本上都是活躍的—比如一個高速LAN環境,epoll並不比select/poll有什麼效率,相 反,如果過多使用epoll_ctl,效率相比還有稍微的下降。但一旦使用idle connections模擬WAN環境,epoll的效率就遠在select/poll之上了。

  • 使用mmap加速核心與使用者空間的訊息傳遞。

        這 點其實牽涉到epoll的特定實現了。無論是select,poll還是epoll都需要內核把FD訊息通知給用戶空間,如何避免不必要的記憶體拷貝就很 重要,在這點上,epoll是透過核心於用戶空間mmap同一塊記憶體實現的。而如果你想我一樣從2.5核心就關注epoll的話,一定不會忘記手工 mmap這一步的。

  • 核心微調

         這其實不算epoll的優點了,而是整個linux平台的優點。也許你可以懷疑linux平台,但你無法迴避linux平台賦予你微調核心的能力。例如,核心TCP/IP協 議棧使用記憶體池來管理sk_buff結構,那麼可以在運行時期動態調整這個記憶體pool(skb_head_pool)的大小— 透過echo XXXX>/proc/sys/net/core/hot_list_length完成。再例如listen函數的第2個參數(TCP完成3次握手 的資料包佇列長度),也可以根據你平台記憶體大小動態調整。更甚至在一個資料包面數目巨大但同時每個資料包本身大小卻很小的特殊系統上嘗試最新的NAPI網卡驅動架構。

    (epoll內容,參考epoll_互動百科)

      推薦設定worker的數量為cpu的核數,這裡就很容易理解了,更多的worker數,只會導致進程來競爭cpu資源了,從而帶來不必要的上下文切換。而且,nginx為了更好的利用多核心特性,提供了cpu親緣性的綁定選項,我們可以將某一個行程綁定在某一個核心上,這樣就不會因為行程的切換帶來cache的失效。像這種小的優化在nginx中非常常見,同時也說明了nginx作者的苦心孤詣。例如,nginx在做4個位元組的字串比較時,會將4個字元轉換成一個int型,再作比較,以減少cpu的指令數等等。

程式碼來總結nginx的事件處理模型:

 

while (true) {
    for t in run_tasks:
        t.handler();
    update_time(&now);
    timeout = ETERNITY;
    for t in wait_tasks: /* sorted already */
        if (t.time <= now) {
            t.timeout_handler();
        } else {
            timeout = t.time - now;
            break;
        }
    nevents = poll_function(events, timeout);
    for i in nevents:
        task t;
        if (events[i].type == READ) {
            t.handler = read_handler;
        } else { /* events[i].type == WRITE */
            t.handler = write_handler;
        }
        run_tasks_add(t);
}
登入後複製

 

 

四.  Nginx FastCGI運作原理


1、什麼是FastCGI

##FastCGI是一個可伸縮地、高速地在HTTP server和動態腳本語言間通訊的介面。多數流行的HTTP server都支援FastCGI,包括Apache、Nginx和lighttpd等。同時,FastCGI也被許多腳本語言支持,其中就有PHP。

FastCGI是從CGI發展改進而來的。傳統CGI介面方式的主要缺點是效能很差,因為每次HTTP伺服器遇到動態程式時都需要重新啟動腳本解析器來執行解析,然後將結果傳回HTTP伺服器。這在處理高並發訪問時幾乎是不可用的。另外傳統的CGI介面方式安全性也很差,現在已經很少使用了。

FastCGI介面方式採用C/S結構,可以將HTTP伺服器和腳本解析伺服器分開,同時在腳本解析伺服器上啟動一個或多個腳本解析守護程式。當HTTP伺服器每次遇到動態程式時,可以直接交付給FastCGI進程來執行,然後將得到的結果傳回瀏覽器。這種方式可以讓HTTP伺服器專一地處理靜態請求或將動態腳本伺服器的結果傳回給客戶端,這在很大程度上提高了整個應用系統的效能。

2、Nginx FastCGI運作原理

#Nginx不支援對外部程式的直接呼叫或解析,所有的外部程式(包括PHP)必須透過FastCGI介面來呼叫。 FastCGI介面在Linux下是socket(這個socket可以是檔案socket,也可以是ip socket)。

wrapper:為了呼叫CGI程序,還需要一個FastCGI的wrapper(wrapper可以理解為用於啟動另一個程式的程式),這個wrapper綁定在某個固定socket上,如端口或者文件socket。當Nginx將CGI請求發送給這個socket的時候,透過FastCGI接口,wrapper接收到請求,然後Fork(派生)出一個新的線程,這個線程調用解釋器或者外部程序處理腳本並讀取返回數據;接著, wrapper再將傳回的資料透過FastCGI接口,沿著固定的socket傳遞給Nginx;最後,Nginx將傳回的資料(html頁面或圖片)傳送給客戶端。這就是Nginx FastCGI的整個運作過程,如圖1-3所示。

       

      所以,我們先需要一個wrapper,而這個wrapper需要完成的工作:

  1. 透過呼叫fastcgi(函式庫)的函數透過socket和ningx通訊(讀寫socket是fastcgi內部實作的功能,對wrapper是非透明的)
  2. 調度thread,進行fork和kill
  3. 和application(php)進行通訊

3、spawn-fcgi與PHP-FPM

       FastCGI介面方式在腳本解析伺服器上啟動一個或是多個守護程式對動態腳本進行解析,這些行程就是FastCGI進程管理器,或稱為FastCGI引擎。 spawn-fcgi與PHP-FPM就是支援PHP的兩個FastCGI進程管理器。 因此HTTPServer完全解放出來,可以更好地進行回應和並發處理。

       spawn-fcgi與PHP-FPM的異同:
       1)spawn-fcgi是HTTP伺服器lighttpd的一部分,目前已獨立成為一個項目,一般與lighttpd使用來配合使用來配合使用支援PHP。但是ligttpd的spwan-fcgi在高並發存取的時候,會出現記憶體洩漏甚至自動重啟FastCGI的問題。即:PHP腳本處理器當機,這個時候如果使用者造訪的話,可能就會出現白頁(即PHP不能被解析或出錯)。

       2)Nginx是個輕量級的HTTP server,必須藉助第三方的FastCGI處理器才可以對PHP進行解析,因此其實這樣看來nginx 是非常靈活的,它可以和任何第三方提供解析的處理器實現連接從而實現對PHP的解析(#在nginx.conf中很容易設定)nginx也可以使用spwan-fcgi(需要一同安裝lighttpd,但是需要為nginx避開端口,一些較早的blog有這方面安裝的教程),但由於spawn-fcgi具有上述所述的使用者逐漸發現的缺陷,現在慢慢減少使用nginx spawn-fcgi組合了。

       由於spawn-fcgi的缺陷,現在出現了第三方(目前已經加入到PHP core)的PHP的FastCGI處理器PHP-FPM,它和spawn-fcgi比較起來有以下優點:

       由於它是作為PHP的patch補丁來開發的,安裝的時候需要和php源碼一起編譯,也就是說編譯到php core中了,因此在性能方面要優秀一些;

同時它在處理高並發方面也優於spawn-fcgi,至少不會自動重啟fastcgi處理器。因此,建議使用Nginx PHP/PHP-FPM這個組合對PHP進行解析。

      相對Spawn-FCGI,PHP-FPM在CPU和記憶體方面的控制都更勝一籌,而且前者很容易崩潰,必須用crontab進行監控,而PHP-FPM則沒有這種煩惱。
       FastCGI 的主要優點是把動態語言和HTTP Server分開來,所以Nginx與PHP/PHP-FPM經常被部署在不同的伺服器上,以分擔前端Nginx伺服器的壓力,使Nginx專一處理靜態請求和轉送動態請求,而PHP/PHP-FPM伺服器專一解析PHP動態請求。

4、Nginx PHP-FPM

      PHP-FPM是管理FastCGI的一個管理器,它作為PHP的插件存在,在安裝PHP要想使用PHP-FPM時在舊php的舊版(php5.3.3之前)就需要把PHP-FPM以補丁的形式安裝到PHP中,而且PHP要與PHP-FPM版本一致,這是必須的)

   PHP-FPM其實是PHP原始碼的補丁,旨在將FastCGI進程管理整合進PHP套件中。必須將它patch到你的PHP原始碼中,在編譯安裝PHP後才可以使用。
   PHP5.3.3已經整合php-fpm了,不再是第三方的套件了。 PHP-FPM提供了更好的PHP進程管理方式,可以有效控制記憶體和進程、可以平滑重載PHP配置,比spawn-fcgi有更多優點,所以被PHP官方收錄了。在./configure的時候帶 –enable-fpm參數即可開啟PHP-FPM。

      fastcgi已經在php5.3.5的core中了,不必在configure時加入 --enable-fastcgi了。舊版如php5.2的需要加此項。

      當我們安裝Nginx和PHP-FPM完後,設定資訊:

     ##PHP-FPM的預設設定php-fpm.conf:

     listen_address  127.0.0.1:9000 #這個表示php的fastcgi程序監聽的ip位址以及連接埠

      

start_servers

      

start_servers

#> #      max_spare_servers

     

# Nginx設定執行php: 編輯nginx.conf加入下列語句:

## Nginx設定執行php: 編輯nginx.conf##      
location ~ \.php$ {

            root html;   
            fastcgi_pass 127.0.0.1:9000; 指定了fastcgi進程偵聽的端口、 cal/nginx/html$fastcgi_script_name;
    }

#    Nginx透過location指令,將所有以php為後綴的檔案交給127.0.0.1:9000來處理,而這裡的IP位址和連接埠就是FastCGI進程監聽的IP位址和端口。

     其整體工作流程:

     其整體工作流程:     1)、FastCGI進程管理器php-fpm自體初始化,啟動主進程php-fpm與啟動

start_servers

個CGI子進程。            

主程式php-fpm主要是管理fastcgi子進程,監聽9000埠。

           #fastcgi子程序

等待來自Web Server的連線。      2)、當客戶端請求到達Web Server Nginx是時,Nginx透過location指令,將所有以php為後綴的檔案交給127.0.0.1:9000來處理,即Nginx透過location指令,將所有以php為後綴的檔案交給127.0.0.1:9000來處理。

      3)FastCGI進程管理器PHP-FPM選擇並連接到一個子進程CGI解釋器。 Web server將CGI環境變數和標準輸入傳送到FastCGI子進程。

      4)、FastCGI子進程完成處理後將標準輸出和錯誤訊息從相同連線傳回Web Server。當FastCGI子程序關閉連線時,請求便告處理完成。

      5)、FastCGI子進程接著等待並處理來自FastCGI進程管理器(執行在 WebServer中)的下一個連線。

 五.   Nginx PHP正確設定


一般web都做統一入口:把PHP請求都傳送到同一個檔案上,然後在此檔案裡透過解析“REQUEST_URI”來實現路由。

Nginx設定檔分成好很多塊,常見的從外到內依序是「http」、「server」、「location」等等,缺省的繼承關係是從外到內,也就是說內層塊會自動取得外層塊的值作為預設值。

例如:######
server {
    listen 80;
    server_name foo.com;
    root /path;
    location / {
        index index.html index.htm index.php;
        if (!-e $request_filename) {
            rewrite . /index.php last;
        }
    }
    location ~ \.php$ {
        include fastcgi_params;
        fastcgi_param SCRIPT_FILENAME /path$fastcgi_script_name;
        fastcgi_pass 127.0.0.1:9000;
        fastcgi_index index.php;
    }
}
登入後複製

1) 不应该在location 模块定义index

一旦未来需要加入新的「location」,必然会出现重复定义的「index」指令,这是因为多个「location」是平级的关系,不存在继承,此时应该在「server」里定义「index」,借助继承关系,「index」指令在所有的「location」中都能生效。

2) 使用try_files

接下来看看「if」指令,说它是大家误解最深的Nginx指令毫不为过:

if (!-e $request_filename) {
rewrite . /index.php last;
}

很多人喜欢用「if」指令做一系列的检查,不过这实际上是「try_files」指令的职责:

try_files $uri $uri/ /index.php;

除此以外,初学者往往会认为「if」指令是内核级的指令,但是实际上它是rewrite模块的一部分,加上Nginx配置实际上是声明式的,而非过程式的,所以当其和非rewrite模块的指令混用时,结果可能会非你所愿。

3)fastcgi_params」配置文件

include fastcgi_params;

Nginx有两份fastcgi配置文件,分别是「fastcgi_params」和「fastcgi.conf」,它们没有太大的差异,唯一的区别是后者比前者多了一行「SCRIPT_FILENAME」的定义:

fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;

注意:$document_root 和 $fastcgi_script_name 之间没有 /。

原本Nginx只有「fastcgi_params」,后来发现很多人在定义「SCRIPT_FILENAME」时使用了硬编码的方式,于是为了规范用法便引入了「fastcgi.conf」。

不过这样的话就产生一个疑问:为什么一定要引入一个新的配置文件,而不是修改旧的配置文件?这是因为「fastcgi_param」指令是数组型的,和普通指令相同的是:内层替换外层;和普通指令不同的是:当在同级多次使用的时候,是新增而不是替换。换句话说,如果在同级定义两次「SCRIPT_FILENAME」,那么它们都会被发送到后端,这可能会导致一些潜在的问题,为了避免此类情况,便引入了一个新的配置文件。

此外,我们还需要考虑一个安全问题:在PHP开启「cgi.fix_pathinfo」的情况下,PHP可能会把错误的文件类型当作PHP文件来解析。如果Nginx和PHP安装在同一台服务器上的话,那么最简单的解决方法是用「try_files」指令做一次过滤:

try_files $uri =404;

依照前面的分析,给出一份改良后的版本,是不是比开始的版本清爽了很多:

server {
    listen 80;
    server_name foo.com;
    root /path;
    index index.html index.htm index.php;
    location / {
        try_files $uri $uri/ /index.php;
    }
    location ~ \.php$ {
       try_files $uri =404;
       include fastcgi.conf;
       fastcgi_pass 127.0.0.1:9000;
   }
}
登入後複製

六. Nginx优化


1. 编译安装过程优化

1).减小Nginx编译后的文件大小

在编译Nginx时,默认以debug模式进行,而在debug模式下会插入很多跟踪和ASSERT之类的信息,编译完成后,一个Nginx要有好几兆字节。而在编译前取消Nginx的debug模式,编译完成后Nginx只有几百千字节。因此可以在编译之前,修改相关源码,取消debug模式。具体方法如下:

在Nginx源码文件被解压后,找到源码目录下的auto/cc/gcc文件,在其中找到如下几行:

 # debug  
CFLAGS=”$CFLAGS -g”
登入後複製

注释掉或删掉这两行,即可取消debug模式。

2.为特定的CPU指定CPU类型编译优化

在编译Nginx时,默认的GCC编译参数是“-O”,要优化GCC编译,可以使用以下两个参数:

  1. --with-cc-opt='-O3' 
  2. ##--with-cpu -opt=CPU  #為特定的 CPU 編譯,有效的值包括: pentium, pentiumpro, pentium3, # pentium4, athlon, opteron, amd64, sparc32, sparc64, ppc64 
要確定CPU類型,可以透過以下指令:

#cat /proc/cpuinfo |# grep "model name" 

2. 利用TCMalloc優化Nginx的效能

TCMalloc的全名為Thread-Caching Malloc,是Google開發的開源工具google-perftools中的一個成員。與標準的glibc庫的Malloc相比,TCMalloc庫在記憶體分配效率和速度上要高得多,這在很大程度上提高了伺服器在高並發情況下的效能,從而降低了系統的負載。以下簡單介紹如何為Nginx加入TCMalloc庫支援。

要安裝TCMalloc函式庫,需要安裝libunwind(32位元作業系統不需要安裝)和google-perftools兩個軟體包,libunwind函式庫為基於64位元CPU和作業系統的程式提供了基本函式呼叫鏈和函數呼叫暫存器功能。以下介紹利用TCMalloc優化Nginx的具體操作流程。

1).安裝libunwind函式庫

可以從http://download.savannah.gnu.org/releases/libunwind下載對應的libunwind版本,這裡下載的是libunwind-0.99-alpha.tar.gz。安裝過程如下

#tar zxvf libunwind-0.99-alpha.tar.gz  

# cd libunwind-0.99-alpha/  

#CFLAGS=-fPIC ./configure  
#make CFLA -fPIC  
#make CFLAGS=-fPIC install

#2).安裝google-perftools

可以從http://google-perftools.googlecode.com下載對應的google-perftools版本,這裡下載的是google-perftools-1.8.tar.gz。安裝流程如下:

[root@localhost home]#tar zxvf google-perftools-1.8.tar.gz  

[root@localhost home]#cd google-perftools-1.8/  

[root @localhost google-perftools-1.8]# ./configure  
[root@localhost google-perftools-1.8]#make && make install  
[root@localhost google-perftools-1.8]#echo "/usr/
local/lib" > /etc/ld.so.conf.d/usr_local_lib.conf  
[root@localhost google-perftools-1.8]# ldconfig

#至此,google-perftools安裝完成。

3).重新編譯Nginx

為了讓Nginx支援google-perftools,需要在安裝過程中加入「–with-google_perftools_module」選項重新編譯Nginx。安裝程式碼如下:

[root@localhostnginx-0.7.65]#./configure \  

>--with-google_perftools_module --with-http_stub_status_module  --prefix=/opt/nginx  

[root@localhost nginx-0.7.65]#make  
[root@localhost nginx-0.7.65]#make install

到這裡Nginx安裝完成。

4).為google-perftools新增執行緒目錄

建立一個執行緒目錄,這裡將檔案放在/tmp/tcmalloc下。操作如下:

[root@localhost home]#mkdir /tmp/tcmalloc  

[root@localhost home]#chmod 0777 /tmp/tcmalloc


5).修改Nginx主設定檔

修改nginx.conf文件,在pid這行的下方加入以下程式碼:

#pid        logs/nginx.pid;  

# google_perftools_profiles /tmp/ tcmalloc;


接著,重啟Nginx即可完成google-perftools的載入。

6).驗證運行狀態

為了驗證google-perftools已經正常加載,可透過以下命令查看:

[root@ localhost home]# lsof -n | grep tcmalloc  
nginx      2395 nobody   9w  REG    8,8        2396 nobody   11w REG   8,8       0    1599443/tmp /tcmalloc.2396  
nginx      2397 nobody   13w REG  8,8        0      15w REG  8,8     0    1599442 /tmp/tcmalloc.2398

#由於在Nginx設定檔中設定worker_processes的值為4,因此開啟了4個Nginx線程,每個線程會有一行記錄。每個線程檔案後面的數字值就是啟動的Nginx的pid值。

至此,利用TCMalloc優化Nginx的操作完成。

3.Nginx核心參數最佳化

核心參數的最佳化,主要是在Linux系統中針對Nginx應用而進行的系統核心參數最佳化。

下面給出一個最佳化實例以供參考。

net.ipv4.tcp_max_tw_buckets = 6000

net.ipv4.ip_local_port_range = 1024 65000  

net.ipv4.tcp_tw_recycle = 1000  
net.ipv4.tcp_tw_recycle = 11##net.v4. 。 .tcp_synack_retries = 1
net.ipv4.tcp_syn_retries = 1
net.ipv4.tcp_fin_timeout = 1
# net.ipv4.tcp_keepalive_time = 30

 
##>上面的核心參數值加入/etc/sysctl.conf檔案中,然後執行下列指令使其生效:



[root@ localhost home]#/sbin/sysctl -p 

下面將實例中選項的意義進行介紹:

TCP參數設定:

net .ipv4.tcp_max_tw_buckets :

#選項用來設定timewait的數量,預設是180 000,這裡設為6000。

net.ipv4.ip_local_port_range:

選項是用來設定允許系統開啟的連接埠範圍。在高並發情況否則連接埠號碼會不夠用。當NGINX充當代理時,每個到上游伺服器的連線都使用短暫或臨時連接埠。

net.ipv4.tcp_tw_recycle:#選項用於設定啟用timewait快速回收.

net.ipv4.tcp_tw_reuse:

選項用於設定開啟重複使用,允許將TIME-WAIT sockets重新用於新的TCP連線。 net.ipv4.tcp_syncookies:

#選項用於設定開啟SYN Cookies,當出現SYN等待佇列溢位時,啟用cookies進行處理。

net.ipv4.tcp_max_orphans:

選項用於設定係統中最多有多少個TCP套接字不被關聯到任何一個使用者檔案句柄上。如果超過這個數字,孤立連接將立即被重設並列印警告訊息。這個限制只是為了防止簡單的DoS攻擊。不能過度依賴這個限制甚至人為減小這個值,更多的情況下應該會增加這個值。

net.ipv4.tcp_max_syn_backlog:

選項用於記錄那些尚未收到客戶端確認訊息的連線請求的最大值。對於有128MB記憶體的系統而言,此參數的預設值是1024,對小記憶體的系統則是128。

net.ipv4.tcp_synack_retries參數的值決定了在核心放棄連線之前發送SYN ACK套件的數量。 net.ipv4.tcp_syn_retries選項表示在核心放棄建立連線之前發送SYN包的數量。

net.ipv4.tcp_fin_timeout選項決定了套接字保持在FIN-WAIT-2狀態的時間。預設值是60秒。正確設定這個值非常重要,有時即使一個負載很小的Web伺服器,也會出現大量的死套接字而產生記憶體溢出的風險。

net.ipv4.tcp_syn_retries選項表示在核心放棄建立連線之前發送SYN套件的數量。

如果發送端要求關閉套接字,net.ipv4.tcp_fin_timeout選項決定了套接字保持在FIN-WAIT-2狀態的時間。接收端可以出錯並永遠不關閉連接,甚至是意外宕機。

net.ipv4.tcp_fin_timeout的預設值是60秒。要注意的是,即使一個負載很小的Web伺服器,也會出現因為大量的死套接字而產生記憶體溢出的風險。 FIN-WAIT-2的危險性比FIN-WAIT-1要小,因為它最多只能消耗1.5KB的內存,但是其生存期長些。

net.ipv4.tcp_keepalive_time選項表示當keepalive啟用的時候,TCP發送keepalive訊息的頻度。預設值是2(單位是小時)。

 

緩衝區佇列:

#net.core.somaxconn:選項的預設值是128, 這個參數用來調節系統同時發起的tcp連線數,在高並發的請求中,預設的值可能會導致連結逾時或重傳,因此,需要結合並發請求數值來調節此值。

 由NGINX可接受的數目決定。預設值通常很低,但可以接受,因為NGINX 接收連線非常快,但如果網站流量大時,就應該增加這個值。核心日誌中的錯誤訊息會提醒這個值太小了,把值改大,直到錯誤提示消失。
注意: 如果設定這個值大於512,相應地也要改變NGINX listen指令的backlog參數。

net.core.netdev_max_backlog:#選項表示當每個網路介面接收封包的速率比核心處理這些封包的速率快時,允許發送到佇列的資料包的最大數目。

 

4.PHP-FPM的最佳化

如果您高負載網站使用PHP-FPM管理FastCGI,這些技巧也許對您有用:

1)增加FastCGI進程數

把PHP FastCGI子進程數調到100或以上,在4G記憶體的伺服器上200就可以建議通過壓力測試取得最佳值。

2)增加PHP-FPM開啟檔案描述子的限制

#標籤rlimit_files用於設定PHP-FPM對開啟檔案描述符的限制,預設值為1024 。這個標籤的值必須和Linux核心開啟檔案數關聯起來,例如,要將此值設為65 535,就必須在Linux命令列執行「ulimit -HSn 65536」。

       然後 #增加PHP-FPM開啟檔案描述子的限制:
     # vi /path/to/php-fpm.conf
#   #  # vi /path/to/php-fpm.conf
  #  找到找到 vi /path/to/php-fpm.conf
1024”
把1024改為4096或更高
.
重啟PHP-FPM.

#       ulimit -n 調整為65536甚至更大。如何調這個參數,可以參考網路上的一些文章。命令列下執行 ulimit -n 65536即可修改。如果不能修改,需要設定  /etc/security/limits.conf,加入

#* hard nofile##65536

* soft nofile 

65536

         3)上增加max_requests

##  個children最多處理多少個請求後便會被關閉,預設的設定是500。

     500

######5.nginx.conf的參數最佳化## #######nginx要開啟的行程數 一般等於cpu的總核數其實一般情況下開4個或8個就可以。 ######    每個nginx進程消耗的記憶體10兆的模樣#########worker_cpu_affinity######     只適用於linux,使用此選項可以綁定worker進程和CPU(2.4核心的機器用不了)###

    假如是8 cpu 分配如下:
    worker_cpu_affinity 00000001 00000010 00000100 00001000 00010000

##00101   

nginx可以使用多個個worker進程,原因如下:to use SMP  to decrease latency when workers blockend on disk I/O  to limit number of connections per process when select() /poll() is

used The worker_processes and worker_connections from the event sections

allows you to calculate maxclients value: k max_clients = worker_processes * worker_nections worker#nections_files>

#    每個nginx進程開啟檔案描述子最大數目配置要和系統的單一進程開啟檔案數一致,linux 2.6核心下開啟檔案開啟數為65535,worker_rlimit_nofile就對應應該填寫65535 nginx調度時分配請求到進程並不是那麼的均衡,假如超過會回傳502錯誤。我在這裡寫的大一點

use epoll

    Nginx使用了最新的epoll(Linux 2.6核心)和kqueue(freebsd)網路I/O模型,而Apache則使用的是傳統的select模型。

處理大量的連接的讀寫,Apache所採用的select網路I/O模型非常低效。在高並發伺服器中,輪詢I/O是最耗時間的操作 目前Linux下能夠承受高並發

    存取的Squid、Memcached都採用的是epoll網路I/O模型。

worker_processes 

    NGINX工作進程數(預設值是1)。在大多數情況下,一個CPU核心運行一個工作進程最好,建議將這個指令設定成自動就可以。有時可能會想增加這個值,例如當工作進程需要做大量的磁碟I/O。

worker_connections 65535;

worker_connections 65535;

   每個工作程序允許最大的同時連接數(Maxclient = work_processes * worker_connections)

#keepalive_timeout 75

##    keepalive逾時時間

    這裡要注意官方的一句話: The parameters can differ from each other. Line Keep-Alive:

timeout=time understands Mozilla and Konqueror. MSIE itself shuts

keep-alive connection approximately after 60 seconds.

##keep-alive connection approximately after 60 seconds.

 

#client_header_buffer_size 16k

arge_fclient_header_buffers 4

客戶請求頭緩衝大小 
nginx預設會用client_header_buffer_size這個buffer來讀取header值,如果header過大,它會使用large_client_header_buffers來讀取

如果設定過小HTTP頭/Cookie過大會報400 錯誤nginx 400 bad request 求行如果超過buffer,就會報HTTP 414錯誤(URI Too Long) nginx接受最長的HTTP頭部大小必須比其中一個buffer大,否則就會報400的HTTP錯誤(Bad Request)。

open_file_cache max 102400


使用欄位:http, server, location 這個指令指定快取是否啟用,如果啟用,將記錄檔案以下資訊: ·開啟的檔案描述符,大小資訊與修改時間. ·存在的目錄資訊. ·在搜尋檔案過程中的錯誤訊息-- 沒有這個檔案,無法正確讀取,參考 open_file_cache_errors 指令選項:

·max - 指定快取的最大數目,如果快取溢位,最長使用過的檔案(LRU)會被移除 範例: open_file_cache max=1000 inactive=20s; open_file_cache_valid 30s; open_file_cache_min_uses 2; open_file_cache_errors on;#pache_語法:open_file_cache_errors on | off 默認值:open_file_cache_errors off 使用字段:http, server, location 這個指令指定是否在搜索一個文件是記錄cache錯誤.

open_file_cache_min_uses

語法:open_file_cache_min_uses number 預設值:open_file_cache_min_uses 1 使用欄位:http, server, location 這個指令指定了在open_file_cache指令無效的參數中一定的時間範圍內可以使用的最小檔案數,如果使用更大的值,檔案描述符在cache中總是打開狀態.

open_file_cache_valid

語法:open_file_cache_valid time 預設值:open_file_cache_valid 60 使用欄位:http, server, location 這個指令指定了何時需要檢查open_file_cache中快取項目的有效訊息.

開啟gzip

gzip on;

gzip_min_length 1k;
gzip_buffers 4 16k;

gzip_http_version 1.0;

gzip_comp_level 2; plain application/x-javascript text/css

application/xml;
gzip_vary on;

快取靜態檔案:

location ~* ^. \.(swf |gif|png|jpg|js|css)$ {

    root /usr/local/ku6/ktv/show.ku6.com/;

    expires 1m;

}

}

# }

# }


回應緩衝區:

例如我們Nginx Tomcat 代理程式存取JS無法完全加載,這幾個參數影響:

proxy_buffer_size 128k;
proxy_buffers   32 128k;
proxy_busy_buffers_size 128k;

Nginx在代理程式了對應服務後或根據我們設定的UpStream和location來取得對應的文件,首先要取得對應服務後或根據我們設定的UpStream和location來取得對應的文件,首先是解析到nginx的記憶體或暫存檔案目錄中,然後由nginx再來回應。那麼當proxy_buffers和proxy_buffer_size以及proxy_busy_buffers_size 都太小時,會將內容根據nginx的配置產生到臨時文件中,但是臨時文件的大小也有預設值。所以當這四個值都過小時就會導致部分檔案只載入一部分。所以要根據我們的伺服器狀況適當的調整proxy_buffers和proxy_buffer_size以及proxy_busy_buffers_size、proxy_temp_file_write_size。具體幾個參數的詳細如下:

proxy_buffers   32 128k;  設置了32個緩存區,每個的大小是128k

proxy_buffer_size 128k; 每個緩存區的大小是128k,當兩個值不一致時沒有找到具體哪個有效,建議和上面的設定一致。

proxy_busy_buffers_size 128k;設定使用中的快取區的大小,控制傳輸至客戶端的快取的最大

proxy_temp_file_write_size 快取檔案的大小

 

##C ##6.最佳化存取日誌

    記錄每個請求會消耗CPU和I/O週期,降低此效果的方式是緩衝存取日誌。使用緩衝,而不是每個日誌記錄都單獨執行寫入操作,NGINX會緩衝一連串的日誌記錄,使用單一操作把它們一起寫到檔案中。

    要啟用存取日誌的快取,就牽涉到在access_log指令中buffer=size這個參數。當緩衝區達到size值時,NGINX會把緩衝區的內容寫到日誌中。讓NGINX在指定的一段時間後寫緩存,就包含flush=time參數。當兩個參數都設定了,當下個日誌條目超出緩衝區值或緩衝區中日誌條目存留時間超過設定的時間值,NGINX都會將條目寫入日誌檔案。當工作進程重新開啟它的日誌檔案或退出時,也會記錄下來。若要完全停用存取日誌記錄的功能,將access_log 指令設定成off參數。  

7.限流
你可以設定多個限制,防止使用者消耗太多的資源,避免影響系統效能和使用者體驗及安全。以下是相關的指示:
limit_conn and limit_conn_zone:NGINX接受客戶連線的數量限制,例如單一IP位址的連線。設定這些指令可以防止單一使用者開啟太多的連接,消耗超出自己的資源。
limit_rate:傳輸到客戶端回應速度的限制(每個開啟多個連線的客戶消耗更多的頻寬)。設定這個限制防止系統過載,確保所有客戶端更均勻的服務品質。
limit_req and limit_req_zone:NGINX處理請求的速度限制,與limit_rate有相同的功能。可以提高安全性,尤其是對登入頁面,透過對使用者限制請求速率設定一個合理的值,避免太慢的程式覆蓋你的應用程式請求(例如DDoS攻擊)。
max_conns:上遊配置區塊中伺服器指令參數。在上游伺服器群組中單一伺服器可接受最大並發數量。使用這個限制防止上游伺服器過載。設定值為0(預設值)表示沒有限制。

queue (NGINX Plus) :建立一個佇列,用來存放在上游伺服器中超出他們最大max_cons限制數量的請求。這個指令可以設定佇列請求的最大值,也可以選擇設定在錯誤返回之前最大等待時間(預設值是60秒)。如果忽略這個指令,請求就不會放入佇列。


七.   錯誤排除

1、Nginx 502 Bad Gateway:



當網關或代理程式工作的伺服器嘗試執行請求時,從上游伺服器接收到無效的回應。

常見原因:

1、後端服務掛了的情況,直接502 (nginx error日誌:connect() failed (111: Connection refused) )

2、後端服務在重啟

實例:將後端服務關掉,然後向nginx發送請求後端接口,nginx日誌可以看到502錯誤。 ######如果nginx php出現502, 錯誤分析:######php-cgi行程數不夠用、php執行時間長(mysql慢)、或是php-cgi行程死掉,都會出現502錯誤###

一般來說Nginx 502 Bad Gateway和php-fpm.conf的設定有關,而Nginx 504 Gateway Time-out則是與nginx.conf的設定有關

#1)、檢視目前的PHP FastCGI進程數是否夠用:

netstat -anpo | grep "php-cgi" | wc -l

  如果實際使用的“FastCGI進程數”接近預設的“FastCGI進程數” ,那麼,說明「FastCGI進程數」不夠用,需要增大。

2)、部分PHP程式的執行時間超過了Nginx的等待時間,可以適當增加

     nginx.conf設定檔中FastCGI的timeout時間,例如:

# http {
    ......
    fastcgi_connect_timeout 300;
    fastcgi_send_timeout 300;
    fast#read_read_send_timeout 300;
    fast#read_read_timeout 300;# 

##2、504 Gateway Timeout :

#nginx

當網關或代理程式工作的伺服器嘗試執行請求時,未能及時從上游伺服器(URI標識出的伺服器,例如HTTP、FTP、LDAP)收到回應。 常見原因: 此介面太耗時,後端服務接收到請求,開始執行,未能在設定時間返回資料給nginx

後端伺服器整體負載太高,接受到請求之後,由於線程繁忙,未能安排給請求的接口,導致未能在設定時間返回資料給nginx



2、413 Request Entity Too Large

   
#    解決:增加client_max_body_size

    client_max_body_size:指令指定允許客戶端連線的最大指令請求實體大小,它出現在請求頭部的Content-Length字段. 如果請求大於指定的值,客戶端將收到一個"Request Entity Too Large" (413)錯誤. 記住,瀏覽器並不知道怎樣顯示這個錯誤.

    php.ini中增大

post_max_size 和upload_max_filesize


3、 Ngnix error.log出現:

##3、 Ngnix error.log出現: upstream sent too big header while reading response header from upstream錯誤

1)如果是nginx反向代理

   proxy是nginx作為client轉送時使用的,如果header過大,超出了預設的1k,就會引發上述的upstream sent too big header (說白了就是nginx把外部請求給後端server,後端server返回的header  太大nginx處理不過來太大nginx處理不過來就導致了。

  server {

        listen       80;

#      header_buffers 4 16k;

        location / {

          #新增此第3行 

        buffers   32 32k;
           proxy_busy_buffers_size 128k;

#           proxy_set_header Host $host;
#            proxy_set_header Xreal- _set_header X-Forwarded-For  $proxy_add_x_forwarded_for;

    }

}        

#}        

2) 如果是nginx PHPcgi 

  錯誤帶有 upstream: "fastcgi://127.0.0.1:9000"。就該 

  多加:

  fastcgi_buffer_size 128k;
  fastcgi_buffers 4 128k;

server {

        listen       80;
        server_name  ddd.com;
        index index.html#6. ize 128k;
        large_client_header_buffers 4 128k;
        proxy_buffer_size 64k;
        proxy_buffers 8 64k;         fastcgi_buffer_size 128k;
      # fastc

          ......

漏洞介紹:nginx是一款高效能的網頁伺服器,使用非常廣泛,其不僅經常被用作反向代理,也可以非常好的支援PHP的運作。 80sec發現其中存在一個較為嚴重的安全問題,預設可能導致伺服器錯誤的將任何類型的檔案以PHP的方式進行解析,這將導致嚴重的安全性問題,使得惡意的攻擊者可能攻陷支援php的nginx伺服器.

漏洞分析:nginx預設以cgi的方式支援php的運行,譬如在設定檔當中可以以

location ~ .php$ { root html; fastcgi_pass 127.0 .0.1:9000;

fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME /scripts$fastcgi_script_name;

include fastcgi_params;
}
<!-- --> 的方式支持對php的解析, location對請求進行選擇的時候會使用URI環境變數進行選擇,其中傳遞到後端Fastcgi的關鍵變數SCRIPT_FILENAME由nginx產生的$fastcgi_script_name決定,而透過分析可以看到$fastcgi_script_name是直接由URI環境變數控制的,這裡就是產生問題的點。而為了較好的支援PATH_INFO的提取,在PHP的設定選項裡存在cgi.fix_pathinfo選項,其目的是為了從SCRIPT_FILENAME裡取出真正的腳本名稱。 <br> 那麼假設存在一個http://www.80sec.com/80sec.jpg,我們以以下的方式去訪問<br><br>http://www.80sec.com/80sec.jpg/80sec. php<br> 將會得到一個URI<br><br>/80sec.jpg/80sec.php

經過location指令,該請求將會交給後端的fastcgi處理,nginx為其設定環境變數SCRIPT_FILENAME,內容為

/scripts/80sec.jpg/80sec.php


而在其他的webserver如lighttpd當中,我們發現其中的SCRIPT_FILENAME被正確的設定為
/scripts/80sec.jpg
所以不存在此問題。
後端的fastcgi在接受到該選項時,會根據fix_pathinfo配置決定是否對SCRIPT_FILENAME進行額外的處理,一般情況下如果不對fix_pathinfo進行設定將影響使用PATH_INFO進行路由選擇的應用,所以該選項一般配置開啟。 Php透過該選項之後會尋找其中真正的腳本檔案名字,尋找的方式也是檢視檔案是否存在,而這個時候就會分離出SCRIPT_FILENAME和PATH_INFO分別為
/scripts/80sec.jpg和80sec.php
最後,以/scripts/80sec.jpg作為此請求需要執行的腳本,攻擊者就可以實作讓nginx以php來解析任何類型的檔案了。
POC: 造訪一個nginx來支援php的站點,在一個任何資源的檔案如robots.txt後面加上/80sec.php,這個時候你可以看到如下的差異:
造訪http://www.80sec.com/robots.txt

HTTP/1.1 200 OK Server: nginx/0.6.32
Date: Thu, 20 May 2010 10 :05:30 GMT

Content-Type: text/plain

Content-Length: 18

Last-Modified: Thu, 20 May 2010 06:26:34 GMT

Connection: keep-alive
Keep-Alive: timeout=20
Accept-Ranges: bytes<br><br># 訪問訪問 http://www.80sec.com/robots.txt/80sec.php<br><br><br>HTTP/1.1 200 OK<br> Server: nginx/0.6.32<br> Date: Thu, 20 May 2010 10:06:49 GMT<br> Content-Type: text/html Transfer- Encoding: chunked
Connection: keep-alive
Keep-Alive: timeout=20
X-Powered-By: PHP/5.2.6
<br># 其中的Content-Type的變化說明了後端負責解析的變化,該網站就可能存在漏洞。 <br><br>漏洞廠商:http://www.nginx.org<br><br>解決方案:<br><br>我們已經嘗試聯絡官方,但先前你可以透過以下的方式來減少損失

關閉cgi.fix_pathinfo為0

if ( $fastcgi_script_name ~ ..*/.*php ) {

return 403; }

PS: 鳴謝laruence大牛在分析過程中給予的幫助

 

以上是「小白」帶你們了解有關於Nginx的模組與工作原理吧! ! !的詳細內容。更多資訊請關注PHP中文網其他相關文章!

相關標籤:
來源:csdn.net
本網站聲明
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn
最新問題
熱門教學
更多>
最新下載
更多>
網站特效
網站源碼
網站素材
前端模板