您好,登錄后才能下訂單哦!
小編給大家分享一下Nginx的模塊與工作原理是什么,相信大部分人都還不怎么了解,因此分享這篇文章給大家參考一下,希望大家閱讀完這篇文章后大有收獲,下面讓我們一起去了解一下吧!
Nginx由內(nèi)核和模塊組成,其中,內(nèi)核的設(shè)計非常微小和簡潔,完成的工作也非常簡單,僅僅通過查找配置文件將客戶端請求映射到一個location block(location是Nginx配置中的一個指令,用于URL匹配),而在這個location中所配置的每個指令將會啟動不同的模塊去完成相應(yīng)的工作。
Nginx的模塊從結(jié)構(gòu)上分為核心模塊、基礎(chǔ)模塊和第三方模塊:
核心模塊:HTTP模塊、EVENT模塊和MAIL模塊
基礎(chǔ)模塊:HTTP Access模塊、HTTP FastCGI模塊、HTTP Proxy模塊和HTTP Rewrite模塊,
第三方模塊:HTTP Upstream Request Hash模塊、Notice模塊和HTTP Access Key模塊。
用戶根據(jù)自己的需要開發(fā)的模塊都屬于第三方模塊。正是有了這么多模塊的支撐,Nginx的功能才會如此強大。
Nginx的模塊從功能上分為如下三類。
Handlers(處理器模塊)。此類模塊直接處理請求,并進行輸出內(nèi)容和修改headers信息等操作。Handlers處理器模塊一般只能有一個。
Filters (過濾器模塊)。此類模塊主要對其他處理器模塊輸出的內(nèi)容進行修改操作,最后由Nginx輸出。
Proxies (代理類模塊)。此類模塊是Nginx的HTTP Upstream之類的模塊,這些模塊主要與后端一些服務(wù)比如FastCGI等進行交互,實現(xiàn)服務(wù)代理和負(fù)載均衡等功能。
圖1-1展示了Nginx模塊常規(guī)的HTTP請求和響應(yīng)的過程。
Nginx本身做的工作實際很少,當(dāng)它接到一個HTTP請求時,它僅僅是通過查找配置文件將此次請求映射到一個location block,而此location中所配置的各個指令則會啟動不同的模塊去完成工作,因此模塊可以看做Nginx真正的勞動工作者。通常一個location中的指令會涉及一個handler模塊和多個filter模塊(當(dāng)然,多個location可以復(fù)用同一個模塊)。handler模塊負(fù)責(zé)處理請求,完成響應(yīng)內(nèi)容的生成,而filter模塊對響應(yīng)內(nèi)容進行處理。
Nginx的模塊直接被編譯進Nginx,因此屬于靜態(tài)編譯方式。啟動Nginx后,Nginx的模塊被自動加載,不像Apache,首先將模塊編譯為一個so文件,然后在配置文件中指定是否進行加載。在解析配置文件時,Nginx的每個模塊都有可能去處理某個請求,但是同一個處理請求只能由一個模塊來完成。
在工作方式上,Nginx分為單工作進程和多工作進程兩種模式。在單工作進程模式下,除主進程外,還有一個工作進程,工作進程是單線程的;在多工作進程模式下,每個工作進程包含多個線程。Nginx默認(rèn)為單工作進程模式。
Nginx在啟動后,會有一個master進程和多個worker進程。
master進程主要用來管理worker進程,具體包括如下4個主要功能:
(1)接收來自外界的信號。
(2)向各worker進程發(fā)送信號。
(3)監(jiān)控woker進程的運行狀態(tài)。
(4)當(dāng)woker進程退出后(異常情況下),會自動重新啟動新的woker進程。
用戶交互接口:master進程充當(dāng)整個進程組與用戶的交互接口,同時對進程進行監(jiān)護。它不需要處理網(wǎng)絡(luò)事件,不負(fù)責(zé)業(yè)務(wù)的執(zhí)行,只會通過管理worker進程來實現(xiàn)重啟服務(wù)、平滑升級、更換日志文件、配置文件實時生效等功能。
重啟work進程:我們要控制nginx,只需要通過kill向master進程發(fā)送信號就行了。比如kill -HUP pid,則是告訴nginx,從容地重啟nginx,我們一般用這個信號來重啟nginx,或重新加載配置,因為是從容地重啟,因此服務(wù)是不中斷的。
master進程在接收到HUP信號后是怎么做的呢?
1)、首先master進程在接到信號后,會先重新加載配置文件,然后再啟動新的worker進程,并向所有老的worker進程發(fā)送信號,告訴他們可以光榮退休了。
2)、新的worker在啟動后,就開始接收新的請求,而老的worker在收到來自master的信號后,就不再接收新的請求,并且在當(dāng)前進程中的所有未處理完的請求處理完成后,再退出。
直接給master進程發(fā)送信號,這是比較傳統(tǒng)的操作方式,nginx在0.8版本之后,引入了一系列命令行參數(shù),來方便我們管理。比如,./nginx -s reload,就是來重啟nginx,./nginx -s stop,就是來停止nginx的運行。如何做到的呢?我們還是拿reload來說,我們看到,執(zhí)行命令時,我們是啟動一個新的nginx進程,而新的nginx進程在解析到reload參數(shù)后,就知道我們的目的是控制nginx來重新加載配置文件了,它會向master進程發(fā)送信號,然后接下來的動作,就和我們直接向master進程發(fā)送信號一樣了。
而基本的網(wǎng)絡(luò)事件,則是放在worker進程中來處理了。多個worker進程之間是對等的,他們同等競爭來自客戶端的請求,各進程互相之間是獨立的。一個請求,只可能在一個worker進程中處理,一個worker進程,不可能處理其它進程的請求。worker進程的個數(shù)是可以設(shè)置的,一般我們會設(shè)置與機器cpu核數(shù)一致,這里面的原因與nginx的進程模型以及事件處理模型是分不開的。
worker進程之間是平等的,每個進程,處理請求的機會也是一樣的。當(dāng)我們提供80端口的http服務(wù)時,一個連接請求過來,每個進程都有可能處理這個連接,怎么做到的呢?
Nginx采用異步非阻塞的方式來處理網(wǎng)絡(luò)事件,類似于Libevent,具體過程如下:
1)接收請求:首先,每個worker進程都是從master進程fork過來,在master進程建立好需要listen的socket(listenfd)之后,然后再fork出多個worker進程。所有worker進程的listenfd會在新連接到來時變得可讀,每個work進程都可以去accept這個socket(listenfd)。當(dāng)一個client連接到來時,所有accept的work進程都會受到通知,但只有一個進程可以accept成功,其它的則會accept失敗。為保證只有一個進程處理該連接,Nginx提供了一把共享鎖accept_mutex來保證同一時刻只有一個work進程在accept連接。所有worker進程在注冊listenfd讀事件前搶accept_mutex,搶到互斥鎖的那個進程注冊listenfd讀事件,在讀事件里調(diào)用accept接受該連接。
2)處理請求:當(dāng)一個worker進程在accept這個連接之后,就開始讀取請求,解析請求,處理請求,產(chǎn)生數(shù)據(jù)后,再返回給客戶端,最后才斷開連接,這樣一個完整的請求就是這樣的了。
我們可以看到,一個請求,完全由worker進程來處理,而且只在一個worker進程中處理。worker進程之間是平等的,每個進程,處理請求的機會也是一樣的。
nginx的進程模型,可以由下圖來表示:
參考http://mp.weixin.qq.com/s?__biz=MjM5NTg2NTU0Ng==&mid=407889757&idx=3&sn=cfa8a70a5fd2a674a91076f67808273c&scene=23&srcid=0401aeJQEraSG6uvLj69Hfve#rd
首先,對于每個worker進程來說,獨立的進程,不需要加鎖,所以省掉了鎖帶來的開銷,同時在編程以及問題查找時,也會方便很多。
其次,采用獨立的進程,可以讓互相之間不會影響,一個進程退出后,其它進程還在工作,服務(wù)不會中斷,master進程則很快啟動新的worker進程。當(dāng)然,worker進程的異常退出,肯定是程序有bug了,異常退出,會導(dǎo)致當(dāng)前worker上的所有請求失敗,不過不會影響到所有請求,所以降低了風(fēng)險。
雖然nginx采用多worker的方式來處理請求,每個worker里面只有一個主線程,那能夠處理的并發(fā)數(shù)很有限啊,多少個worker就能處理多少個并發(fā),何來高并發(fā)呢?非也,這就是nginx的高明之處,nginx采用了異步非阻塞的方式來處理請求,也就是說,nginx是可以同時處理成千上萬個請求的。一個worker進程可以同時處理的請求數(shù)只受限于內(nèi)存大小,而且在架構(gòu)設(shè)計上,不同的worker進程之間處理并發(fā)請求時幾乎沒有同步鎖的限制,worker進程通常不會進入睡眠狀態(tài),因此,當(dāng)Nginx上的進程數(shù)與CPU核心數(shù)相等時(最好每一個worker進程都綁定特定的CPU核心),進程間切換的代價是最小的。
而apache的常用工作方式(apache也有異步非阻塞版本,但因其與自帶某些模塊沖突,所以不常用),每個進程在一個時刻只處理一個請求,因此,當(dāng)并發(fā)數(shù)上到幾千時,就同時有幾千的進程在處理請求了。這對操作系統(tǒng)來說,是個不小的挑戰(zhàn),進程帶來的內(nèi)存占用非常大,進程的上下文切換帶來的cpu開銷很大,自然性能就上不去了,而這些開銷完全是沒有意義的。
為什么nginx可以采用異步非阻塞的方式來處理呢,或者異步非阻塞到底是怎么回事呢?具體請看:使用 libevent 和 libev 提高網(wǎng)絡(luò)應(yīng)用性能——I/O模型演進變化史
我們先回到原點,看看一個請求的完整過程:首先,請求過來,要建立連接,然后再接收數(shù)據(jù),接收數(shù)據(jù)后,再發(fā)送數(shù)據(jù)。
具體到系統(tǒng)底層,就是讀寫事件,而當(dāng)讀寫事件沒有準(zhǔn)備好時,必然不可操作,如果不用非阻塞的方式來調(diào)用,那就得阻塞調(diào)用了,事件沒有準(zhǔn)備好,那就只能等了,等事件準(zhǔn)備好了,你再繼續(xù)吧。阻塞調(diào)用會進入內(nèi)核等待,cpu就會讓出去給別人用了,對單線程的worker來說,顯然不合適,當(dāng)網(wǎng)絡(luò)事件越多時,大家都在等待呢,cpu空閑下來沒人用,cpu利用率自然上不去了,更別談高并發(fā)了。好吧,你說加進程數(shù),這跟apache的線程模型有什么區(qū)別,注意,別增加無謂的上下文切換。所以,在nginx里面,最忌諱阻塞的系統(tǒng)調(diào)用了。不要阻塞,那就非阻塞嘍。非阻塞就是,事件沒有準(zhǔn)備好,馬上返回EAGAIN,告訴你,事件還沒準(zhǔn)備好呢,你慌什么,過會再來吧。好吧,你過一會,再來檢查一下事件,直到事件準(zhǔn)備好了為止,在這期間,你就可以先去做其它事情,然后再來看看事件好了沒。雖然不阻塞了,但你得不時地過來檢查一下事件的狀態(tài),你可以做更多的事情了,但帶來的開銷也是不小的。
關(guān)于IO模型:http://blog.csdn.net/hguisu/article/details/7453390
nginx支持的事件模型如下(nginx的wiki):
Nginx支持如下處理連接的方法(I/O復(fù)用方法),這些方法可以通過use指令指定。
select– 標(biāo)準(zhǔn)方法。 如果當(dāng)前平臺沒有更有效的方法,它是編譯時默認(rèn)的方法。你可以使用配置參數(shù) –with-select_module 和 –without-select_module 來啟用或禁用這個模塊。
poll– 標(biāo)準(zhǔn)方法。 如果當(dāng)前平臺沒有更有效的方法,它是編譯時默認(rèn)的方法。你可以使用配置參數(shù) –with-poll_module 和 –without-poll_module 來啟用或禁用這個模塊。
kqueue– 高效的方法,使用于 FreeBSD 4.1+, OpenBSD 2.9+, NetBSD 2.0 和 MacOS X. 使用雙處理器的MacOS X系統(tǒng)使用kqueue可能會造成內(nèi)核崩潰。
epoll – 高效的方法,使用于Linux內(nèi)核2.6版本及以后的系統(tǒng)。在某些發(fā)行版本中,如SuSE 8.2, 有讓2.4版本的內(nèi)核支持epoll的補丁。
rtsig – 可執(zhí)行的實時信號,使用于Linux內(nèi)核版本2.2.19以后的系統(tǒng)。默認(rèn)情況下整個系統(tǒng)中不能出現(xiàn)大于1024個POSIX實時(排隊)信號。這種情況 對于高負(fù)載的服務(wù)器來說是低效的;所以有必要通過調(diào)節(jié)內(nèi)核參數(shù) /proc/sys/kernel/rtsig-max 來增加隊列的大小??墒菑腖inux內(nèi)核版本2.6.6-mm2開始, 這個參數(shù)就不再使用了,并且對于每個進程有一個獨立的信號隊列,這個隊列的大小可以用 RLIMIT_SIGPENDING 參數(shù)調(diào)節(jié)。當(dāng)這個隊列過于擁塞,nginx就放棄它并且開始使用 poll 方法來處理連接直到恢復(fù)正常。
/dev/poll – 高效的方法,使用于 Solaris 7 11/99+, HP/UX 11.22+ (eventport), IRIX 6.5.15+ 和 Tru64 UNIX 5.1A+.
eventport – 高效的方法,使用于 Solaris 10. 為了防止出現(xiàn)內(nèi)核崩潰的問題, 有必要安裝這個 安全補丁。
在linux下面,只有epoll是高效的方法
下面再來看看epoll到底是如何高效的
Epoll是Linux內(nèi)核為處理大批量句柄而作了改進的poll。 要使用epoll只需要這三個系統(tǒng)調(diào)用:epoll_create(2), epoll_ctl(2), epoll_wait(2)。它是在2.5.44內(nèi)核中被引進的(epoll(4) is a new API introduced in Linux kernel 2.5.44),在2.6內(nèi)核中得到廣泛應(yīng)用。
epoll的優(yōu)點
支持一個進程打開大數(shù)目的socket描述符(FD)
select 最不能忍受的是一個進程所打開的FD是有一定限制的,由FD_SETSIZE設(shè)置,默認(rèn)值是2048。對于那些需要支持的上萬連接數(shù)目的IM服務(wù)器來說顯 然太少了。這時候你一是可以選擇修改這個宏然后重新編譯內(nèi)核,不過資料也同時指出這樣會帶來網(wǎng)絡(luò)效率的下降,二是可以選擇多進程的解決方案(傳統(tǒng)的 Apache方案),不過雖然linux上面創(chuàng)建進程的代價比較小,但仍舊是不可忽視的,加上進程間數(shù)據(jù)同步遠(yuǎn)比不上線程間同步的高效,所以也不是一種完 美的方案。不過 epoll則沒有這個限制,它所支持的FD上限是最大可以打開文件的數(shù)目,這個數(shù)字一般遠(yuǎn)大于2048,舉個例子,在1GB內(nèi)存的機器上大約是10萬左 右,具體數(shù)目可以cat /proc/sys/fs/file-max察看,一般來說這個數(shù)目和系統(tǒng)內(nèi)存關(guān)系很大。
IO效率不隨FD數(shù)目增加而線性下降
傳統(tǒng)的select/poll另一個致命弱點就是當(dāng)你擁有一個很大的socket集合,不過由于網(wǎng)絡(luò)延時,任一時間只有部分的socket是”活躍”的,但 是select/poll每次調(diào)用都會線性掃描全部的集合,導(dǎo)致效率呈現(xiàn)線性下降。但是epoll不存在這個問題,它只會對”活躍”的socket進行操 作—這是因為在內(nèi)核實現(xiàn)中epoll是根據(jù)每個fd上面的callback函數(shù)實現(xiàn)的。那么,只有”活躍”的socket才會主動的去調(diào)用 callback函數(shù),其他idle狀態(tài)socket則不會,在這點上,epoll實現(xiàn)了一個”偽”AIO,因為這時候推動力在os內(nèi)核。在一些 benchmark中,如果所有的socket基本上都是活躍的—比如一個高速LAN環(huán)境,epoll并不比select/poll有什么效率,相 反,如果過多使用epoll_ctl,效率相比還有稍微的下降。但是一旦使用idle connections模擬WAN環(huán)境,epoll的效率就遠(yuǎn)在select/poll之上了。
使用mmap加速內(nèi)核與用戶空間的消息傳遞。
這 點實際上涉及到epoll的具體實現(xiàn)了。無論是select,poll還是epoll都需要內(nèi)核把FD消息通知給用戶空間,如何避免不必要的內(nèi)存拷貝就很 重要,在這點上,epoll是通過內(nèi)核于用戶空間mmap同一塊內(nèi)存實現(xiàn)的。而如果你想我一樣從2.5內(nèi)核就關(guān)注epoll的話,一定不會忘記手工 mmap這一步的。
內(nèi)核微調(diào)
這一點其實不算epoll的優(yōu)點了,而是整個linux平臺的優(yōu)點。也許你可以懷疑linux平臺,但是你無法回避linux平臺賦予你微調(diào)內(nèi)核的能力。比如,內(nèi)核TCP/IP協(xié) 議棧使用內(nèi)存池管理sk_buff結(jié)構(gòu),那么可以在運行時期動態(tài)調(diào)整這個內(nèi)存pool(skb_head_pool)的大小— 通過echo XXXX>/proc/sys/net/core/hot_list_length完成。再比如listen函數(shù)的第2個參數(shù)(TCP完成3次握手 的數(shù)據(jù)包隊列長度),也可以根據(jù)你平臺內(nèi)存大小動態(tài)調(diào)整。更甚至在一個數(shù)據(jù)包面數(shù)目巨大但同時每個數(shù)據(jù)包本身大小卻很小的特殊系統(tǒng)上嘗試最新的NAPI網(wǎng)卡驅(qū)動架構(gòu)。
(epoll內(nèi)容,參考epoll_互動百科)
推薦設(shè)置worker的個數(shù)為cpu的核數(shù),在這里就很容易理解了,更多的worker數(shù),只會導(dǎo)致進程來競爭cpu資源了,從而帶來不必要的上下文切換。而且,nginx為了更好的利用多核特性,提供了cpu親緣性的綁定選項,我們可以將某一個進程綁定在某一個核上,這樣就不會因為進程的切換帶來cache的失效。像這種小的優(yōu)化在nginx中非常常見,同時也說明了nginx作者的苦心孤詣。比如,nginx在做4個字節(jié)的字符串比較時,會將4個字符轉(zhuǎn)換成一個int型,再作比較,以減少cpu的指令數(shù)等等。
代碼來總結(jié)一下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); }
FastCGI是一個可伸縮地、高速地在HTTP server和動態(tài)腳本語言間通信的接口。多數(shù)流行的HTTP server都支持FastCGI,包括Apache、Nginx和lighttpd等。同時,F(xiàn)astCGI也被許多腳本語言支持,其中就有PHP。
FastCGI是從CGI發(fā)展改進而來的。傳統(tǒng)CGI接口方式的主要缺點是性能很差,因為每次HTTP服務(wù)器遇到動態(tài)程序時都需要重新啟動腳本解析器來執(zhí)行解析,然后將結(jié)果返回給HTTP服務(wù)器。這在處理高并發(fā)訪問時幾乎是不可用的。另外傳統(tǒng)的CGI接口方式安全性也很差,現(xiàn)在已經(jīng)很少使用了。
FastCGI接口方式采用C/S結(jié)構(gòu),可以將HTTP服務(wù)器和腳本解析服務(wù)器分開,同時在腳本解析服務(wù)器上啟動一個或者多個腳本解析守護進程。當(dāng)HTTP服務(wù)器每次遇到動態(tài)程序時,可以將其直接交付給FastCGI進程來執(zhí)行,然后將得到的結(jié)果返回給瀏覽器。這種方式可以讓HTTP服務(wù)器專一地處理靜態(tài)請求或者將動態(tài)腳本服務(wù)器的結(jié)果返回給客戶端,這在很大程度上提高了整個應(yīng)用系統(tǒng)的性能。
Nginx不支持對外部程序的直接調(diào)用或者解析,所有的外部程序(包括PHP)必須通過FastCGI接口來調(diào)用。FastCGI接口在Linux下是socket(這個socket可以是文件socket,也可以是ip socket)。
wrapper:為了調(diào)用CGI程序,還需要一個FastCGI的wrapper(wrapper可以理解為用于啟動另一個程序的程序),這個wrapper綁定在某個固定socket上,如端口或者文件socket。當(dāng)Nginx將CGI請求發(fā)送給這個socket的時候,通過FastCGI接口,wrapper接收到請求,然后Fork(派生)出一個新的線程,這個線程調(diào)用解釋器或者外部程序處理腳本并讀取返回數(shù)據(jù);接著,wrapper再將返回的數(shù)據(jù)通過FastCGI接口,沿著固定的socket傳遞給Nginx;最后,Nginx將返回的數(shù)據(jù)(html頁面或者圖片)發(fā)送給客戶端。這就是Nginx+FastCGI的整個運作過程,如圖1-3所示。
所以,我們首先需要一個wrapper,這個wrapper需要完成的工作:
通過調(diào)用fastcgi(庫)的函數(shù)通過socket和ningx通信(讀寫socket是fastcgi內(nèi)部實現(xiàn)的功能,對wrapper是非透明的)
調(diào)度thread,進行fork和kill
和application(php)進行通信
FastCGI接口方式在腳本解析服務(wù)器上啟動一個或者多個守護進程對動態(tài)腳本進行解析,這些進程就是FastCGI進程管理器,或者稱為FastCGI引擎。 spawn-fcgi與PHP-FPM就是支持PHP的兩個FastCGI進程管理器。因此HTTPServer完全解放出來,可以更好地進行響應(yīng)和并發(fā)處理。
spawn-fcgi與PHP-FPM的異同:
1)spawn-fcgi是HTTP服務(wù)器lighttpd的一部分,目前已經(jīng)獨立成為一個項目,一般與lighttpd配合使用來支持PHP。但是ligttpd的spwan-fcgi在高并發(fā)訪問的時候,會出現(xiàn)內(nèi)存泄漏甚至自動重啟FastCGI的問題。即:PHP腳本處理器當(dāng)機,這個時候如果用戶訪問的話,可能就會出現(xiàn)白頁(即PHP不能被解析或者出錯)。
2)Nginx是個輕量級的HTTP server,必須借助第三方的FastCGI處理器才可以對PHP進行解析,因此其實這樣看來nginx是非常靈活的,它可以和任何第三方提供解析的處理器實現(xiàn)連接從而實現(xiàn)對PHP的解析(在nginx.conf中很容易設(shè)置)。nginx也可以使用spwan-fcgi(需要一同安裝lighttpd,但是需要為nginx避開端口,一些較早的blog有這方面安裝的教程),但是由于spawn-fcgi具有上面所述的用戶逐漸發(fā)現(xiàn)的缺陷,現(xiàn)在慢慢減少用nginx+spawn-fcgi組合了。
由于spawn-fcgi的缺陷,現(xiàn)在出現(xiàn)了第三方(目前已經(jīng)加入到PHP core中)的PHP的FastCGI處理器PHP-FPM,它和spawn-fcgi比較起來有如下優(yōu)點:
由于它是作為PHP的patch補丁來開發(fā)的,安裝的時候需要和php源碼一起編譯,也就是說編譯到php core中了,因此在性能方面要優(yōu)秀一些;
同時它在處理高并發(fā)方面也優(yōu)于spawn-fcgi,至少不會自動重啟fastcgi處理器。因此,推薦使用Nginx+PHP/PHP-FPM這個組合對PHP進行解析。
相對Spawn-FCGI,PHP-FPM在CPU和內(nèi)存方面的控制都更勝一籌,而且前者很容易崩潰,必須用crontab進行監(jiān)控,而PHP-FPM則沒有這種煩惱。
FastCGI 的主要優(yōu)點是把動態(tài)語言和HTTP Server分離開來,所以Nginx與PHP/PHP-FPM經(jīng)常被部署在不同的服務(wù)器上,以分擔(dān)前端Nginx服務(wù)器的壓力,使Nginx專一處理靜態(tài)請求和轉(zhuǎn)發(fā)動態(tài)請求,而PHP/PHP-FPM服務(wù)器專一解析PHP動態(tài)請求。
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已經(jīng)集成php-fpm了,不再是第三方的包了。PHP-FPM提供了更好的PHP進程管理方式,可以有效控制內(nèi)存和進程、可以平滑重載PHP配置,比spawn-fcgi具有更多優(yōu)點,所以被PHP官方收錄了。在./configure的時候帶 –enable-fpm參數(shù)即可開啟PHP-FPM。
fastcgi已經(jīng)在php5.3.5的core中了,不必在configure時添加 --enable-fastcgi了。老版本如php5.2的需要加此項。
當(dāng)我們安裝Nginx和PHP-FPM完后,配置信息:
PHP-FPM的默認(rèn)配置php-fpm.conf:
listen_address 127.0.0.1:9000 #這個表示php的fastcgi進程監(jiān)聽的ip地址以及端口
start_servers
min_spare_servers
max_spare_servers
Nginx配置運行php: 編輯nginx.conf加入如下語句:
location ~ \.php$ {
root html;
fastcgi_pass 127.0.0.1:9000; 指定了fastcgi進程偵聽的端口,nginx就是通過這里與php交互的
fastcgi_index index.php;
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME /usr/local/nginx/html$fastcgi_script_name;
}
Nginx通過location指令,將所有以php為后綴的文件都交給127.0.0.1:9000來處理,而這里的IP地址和端口就是FastCGI進程監(jiān)聽的IP地址和端口。
其整體工作流程:
1)、FastCGI進程管理器php-fpm自身初始化,啟動主進程php-fpm和啟動start_servers個CGI 子進程。
主進程php-fpm主要是管理fastcgi子進程,監(jiān)聽9000端口。
fastcgi子進程等待來自Web Server的連接。
2)、當(dāng)客戶端請求到達(dá)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環(huán)境變量和標(biāo)準(zhǔn)輸入發(fā)送到FastCGI子進程。
4)、FastCGI子進程完成處理后將標(biāo)準(zhǔn)輸出和錯誤信息從同一連接返回Web Server。當(dāng)FastCGI子進程關(guān)閉連接時,請求便告處理完成。
5)、FastCGI子進程接著等待并處理來自FastCGI進程管理器(運行在 WebServer中)的下一個連接。
一般web都做統(tǒng)一入口:把PHP請求都發(fā)送到同一個文件上,然后在此文件里通過解析「REQUEST_URI」實現(xiàn)路由。
Nginx配置文件分為好多塊,常見的從外到內(nèi)依次是「http」、「server」、「location」等等,缺省的繼承關(guān)系是從外到內(nèi),也就是說內(nèi)層塊會自動獲取外層塊的值作為缺省值。
例如:
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; } }
一旦未來需要加入新的「location」,必然會出現(xiàn)重復(fù)定義的「index」指令,這是因為多個「location」是平級的關(guān)系,不存在繼承,此時應(yīng)該在「server」里定義「index」,借助繼承關(guān)系,「index」指令在所有的「location」中都能生效。
接下來看看「if」指令,說它是大家誤解最深的Nginx指令毫不為過:
if (!-e $request_filename) {
rewrite . /index.php last;
}
很多人喜歡用「if」指令做一系列的檢查,不過這實際上是「try_files」指令的職責(zé):
try_files $uri $uri/ /index.php;
除此以外,初學(xué)者往往會認(rèn)為「if」指令是內(nèi)核級的指令,但是實際上它是rewrite模塊的一部分,加上Nginx配置實際上是聲明式的,而非過程式的,所以當(dāng)其和非rewrite模塊的指令混用時,結(jié)果可能會非你所愿。
include fastcgi_params;
Nginx有兩份fastcgi配置文件,分別是「fastcgi_params」和「fastcgi.conf」,它們沒有太大的差異,唯一的區(qū)別是后者比前者多了一行「SCRIPT_FILENAME」的定義:
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
注意:$document_root 和 $fastcgi_script_name 之間沒有 /。
原本Nginx只有「fastcgi_params」,后來發(fā)現(xiàn)很多人在定義「SCRIPT_FILENAME」時使用了硬編碼的方式,于是為了規(guī)范用法便引入了「fastcgi.conf」。
不過這樣的話就產(chǎn)生一個疑問:為什么一定要引入一個新的配置文件,而不是修改舊的配置文件?這是因為「fastcgi_param」指令是數(shù)組型的,和普通指令相同的是:內(nèi)層替換外層;和普通指令不同的是:當(dāng)在同級多次使用的時候,是新增而不是替換。換句話說,如果在同級定義兩次「SCRIPT_FILENAME」,那么它們都會被發(fā)送到后端,這可能會導(dǎo)致一些潛在的問題,為了避免此類情況,便引入了一個新的配置文件。
此外,我們還需要考慮一個安全問題:在PHP開啟「cgi.fix_pathinfo」的情況下,PHP可能會把錯誤的文件類型當(dāng)作PHP文件來解析。如果Nginx和PHP安裝在同一臺服務(wù)器上的話,那么最簡單的解決方法是用「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; } }
1).減小Nginx編譯后的文件大小
在編譯Nginx時,默認(rèn)以debug模式進行,而在debug模式下會插入很多跟蹤和ASSERT之類的信息,編譯完成后,一個Nginx要有好幾兆字節(jié)。而在編譯前取消Nginx的debug模式,編譯完成后Nginx只有幾百千字節(jié)。因此可以在編譯之前,修改相關(guān)源碼,取消debug模式。具體方法如下:
在Nginx源碼文件被解壓后,找到源碼目錄下的auto/cc/gcc文件,在其中找到如下幾行:
# debug CFLAGS=”$CFLAGS -g”
注釋掉或刪掉這兩行,即可取消debug模式。
2.為特定的CPU指定CPU類型編譯優(yōu)化
在編譯Nginx時,默認(rèn)的GCC編譯參數(shù)是“-O”,要優(yōu)化GCC編譯,可以使用以下兩個參數(shù):
--with-cc-opt='-O3'
--with-cpu-opt=CPU #為特定的 CPU 編譯,有效的值包括:
pentium, pentiumpro, pentium3, # pentium4, athlon, opteron, amd64, sparc32, sparc64, ppc64
要確定CPU類型,可以通過如下命令: #cat /proc/cpuinfo | grep "model name"
TCMalloc的全稱為Thread-Caching Malloc,是谷歌開發(fā)的開源工具google-perftools中的一個成員。與標(biāo)準(zhǔn)的glibc庫的Malloc相比,TCMalloc庫在內(nèi)存分配效率和速度上要高很多,這在很大程度上提高了服務(wù)器在高并發(fā)情況下的性能,從而降低了系統(tǒng)的負(fù)載。下面簡單介紹如何為Nginx添加TCMalloc庫支持。
要安裝TCMalloc庫,需要安裝libunwind(32位操作系統(tǒng)不需要安裝)和google-perftools兩個軟件包,libunwind庫為基于64位CPU和操作系統(tǒng)的程序提供了基本函數(shù)調(diào)用鏈和函數(shù)調(diào)用寄存器功能。下面介紹利用TCMalloc優(yōu)化Nginx的具體操作過程。
1).安裝libunwind庫
可以從http://download.savannah.gnu.org/releases/libunwind下載相應(yīng)的libunwind版本,這里下載的是libunwind-0.99-alpha.tar.gz。安裝過程如下
#tar zxvf libunwind-0.99-alpha.tar.gz
# cd libunwind-0.99-alpha/
#CFLAGS=-fPIC ./configure
#make CFLAGS=-fPIC
#make CFLAGS=-fPIC install
2).安裝google-perftools
可以從http://google-perftools.googlecode.com下載相應(yīng)的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添加線程目錄
創(chuàng)建一個線程目錄,這里將文件放在/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).驗證運行狀態(tài)
為了驗證google-perftools已經(jīng)正常加載,可通過如下命令查看:
[root@ localhost home]# lsof -n | grep tcmalloc
nginx 2395 nobody 9w REG 8,8 0 1599440 /tmp/tcmalloc.2395
nginx 2396 nobody 11w REG 8,8 0 1599443 /tmp/tcmalloc.2396
nginx 2397 nobody 13w REG 8,8 0 1599441 /tmp/tcmalloc.2397
nginx 2398 nobody 15w REG 8,8 0 1599442 /tmp/tcmalloc.2398
由于在Nginx配置文件中設(shè)置worker_processes的值為4,因此開啟了4個Nginx線程,每個線程會有一行記錄。每個線程文件后面的數(shù)字值就是啟動的Nginx的pid值。
至此,利用TCMalloc優(yōu)化Nginx的操作完成。
內(nèi)核參數(shù)的優(yōu)化,主要是在Linux系統(tǒng)中針對Nginx應(yīng)用而進行的系統(tǒng)內(nèi)核參數(shù)優(yōu)化。
下面給出一個優(yōu)化實例以供參考。
net.ipv4.tcp_max_tw_buckets = 6000
net.ipv4.ip_local_port_range = 1024 65000
net.ipv4.tcp_tw_recycle = 1
net.ipv4.tcp_tw_reuse = 1
net.ipv4.tcp_syncookies = 1
net.core.somaxconn = 262144
net.core.netdev_max_backlog = 262144
net.ipv4.tcp_max_orphans = 262144
net.ipv4.tcp_max_syn_backlog = 262144
net.ipv4.tcp_synack_retries = 1
net.ipv4.tcp_syn_retries = 1
net.ipv4.tcp_fin_timeout = 1
net.ipv4.tcp_keepalive_time = 30
將上面的內(nèi)核參數(shù)值加入/etc/sysctl.conf文件中,然后執(zhí)行如下命令使之生效:
[root@ localhost home]#/sbin/sysctl -p
下面對實例中選項的含義進行介紹:
TCP參數(shù)設(shè)置:
net.ipv4.tcp_max_tw_buckets :選項用來設(shè)定timewait的數(shù)量,默認(rèn)是180 000,這里設(shè)為6000。
net.ipv4.ip_local_port_range:選項用來設(shè)定允許系統(tǒng)打開的端口范圍。在高并發(fā)情況否則端口號會不夠用。當(dāng)NGINX充當(dāng)代理時,每個到上游服務(wù)器的連接都使用一個短暫或臨時端口。
net.ipv4.tcp_tw_recycle:選項用于設(shè)置啟用timewait快速回收.
net.ipv4.tcp_tw_reuse:選項用于設(shè)置開啟重用,允許將TIME-WAIT sockets重新用于新的TCP連接。
net.ipv4.tcp_syncookies:選項用于設(shè)置開啟SYN Cookies,當(dāng)出現(xiàn)SYN等待隊列溢出時,啟用cookies進行處理。
net.ipv4.tcp_max_orphans:選項用于設(shè)定系統(tǒng)中最多有多少個TCP套接字不被關(guān)聯(lián)到任何一個用戶文件句柄上。如果超過這個數(shù)字,孤立連接將立即被復(fù)位并打印出警告信息。這個限制只是為了防止簡單的DoS攻擊。不能過分依靠這個限制甚至人為減小這個值,更多的情況下應(yīng)該增加這個值。
net.ipv4.tcp_max_syn_backlog:選項用于記錄那些尚未收到客戶端確認(rèn)信息的連接請求的最大值。對于有128MB內(nèi)存的系統(tǒng)而言,此參數(shù)的默認(rèn)值是1024,對小內(nèi)存的系統(tǒng)則是128。
net.ipv4.tcp_synack_retries參數(shù)的值決定了內(nèi)核放棄連接之前發(fā)送SYN+ACK包的數(shù)量。
net.ipv4.tcp_syn_retries選項表示在內(nèi)核放棄建立連接之前發(fā)送SYN包的數(shù)量。
net.ipv4.tcp_fin_timeout選項決定了套接字保持在FIN-WAIT-2狀態(tài)的時間。默認(rèn)值是60秒。正確設(shè)置這個值非常重要,有時即使一個負(fù)載很小的Web服務(wù)器,也會出現(xiàn)大量的死套接字而產(chǎn)生內(nèi)存溢出的風(fēng)險。
net.ipv4.tcp_syn_retries選項表示在內(nèi)核放棄建立連接之前發(fā)送SYN包的數(shù)量。
如果發(fā)送端要求關(guān)閉套接字,net.ipv4.tcp_fin_timeout選項決定了套接字保持在FIN-WAIT-2狀態(tài)的時間。接收端可以出錯并永遠(yuǎn)不關(guān)閉連接,甚至意外宕機。
net.ipv4.tcp_fin_timeout的默認(rèn)值是60秒。需要注意的是,即使一個負(fù)載很小的Web服務(wù)器,也會出現(xiàn)因為大量的死套接字而產(chǎn)生內(nèi)存溢出的風(fēng)險。FIN-WAIT-2的危險性比FIN-WAIT-1要小,因為它最多只能消耗1.5KB的內(nèi)存,但是其生存期長些。
net.ipv4.tcp_keepalive_time選項表示當(dāng)keepalive啟用的時候,TCP發(fā)送keepalive消息的頻度。默認(rèn)值是2(單位是小時)。
緩沖區(qū)隊列:
net.core.somaxconn:選項的默認(rèn)值是128, 這個參數(shù)用于調(diào)節(jié)系統(tǒng)同時發(fā)起的tcp連接數(shù),在高并發(fā)的請求中,默認(rèn)的值可能會導(dǎo)致鏈接超時或者重傳,因此,需要結(jié)合并發(fā)請求數(shù)來調(diào)節(jié)此值。
由NGINX可接受的數(shù)目決定。默認(rèn)值通常很低,但可以接受,因為NGINX 接收連接非常快,但如果網(wǎng)站流量大時,就應(yīng)該增加這個值。內(nèi)核日志中的錯誤消息會提醒這個值太小了,把值改大,直到錯誤提示消失。
注意: 如果設(shè)置這個值大于512,相應(yīng)地也要改變NGINX listen指令的backlog參數(shù)。
net.core.netdev_max_backlog:選項表示當(dāng)每個網(wǎng)絡(luò)接口接收數(shù)據(jù)包的速率比內(nèi)核處理這些包的速率快時,允許發(fā)送到隊列的數(shù)據(jù)包的最大數(shù)目。
如果您高負(fù)載網(wǎng)站使用PHP-FPM管理FastCGI,這些技巧也許對您有用:
1)增加FastCGI進程數(shù)
把PHP FastCGI子進程數(shù)調(diào)到100或以上,在4G內(nèi)存的服務(wù)器上200就可以建議通過壓力測試獲取最佳值。
2)增加 PHP-FPM打開文件描述符的限制
標(biāo)簽rlimit_files用于設(shè)置PHP-FPM對打開文件描述符的限制,默認(rèn)值為1024。這個標(biāo)簽的值必須和Linux內(nèi)核打開文件數(shù)關(guān)聯(lián)起來,例如,要將此值設(shè)置為65 535,就必須在Linux命令行執(zhí)行“ulimit -HSn 65536”。
然后 增加 PHP-FPM打開文件描述符的限制:
# vi /path/to/php-fpm.conf
找到“<valuename="rlimit_files">1024</value>”
把1024更改為 4096或者更高.
重啟 PHP-FPM.
ulimit -n 要調(diào)整為65536甚至更大。如何調(diào)這個參數(shù),可以參考網(wǎng)上的一些文章。命令行下執(zhí)行 ulimit -n 65536即可修改。如果不能修改,需要設(shè)置 /etc/security/limits.conf,加入
* hard nofile65536
* soft nofile 65536
3)適當(dāng)增加max_requests
標(biāo)簽max_requests指明了每個children最多處理多少個請求后便會被關(guān)閉,默認(rèn)的設(shè)置是500。
<value name="max_requests"> 500 </value>
nginx要開啟的進程數(shù) 一般等于cpu的總核數(shù) 其實一般情況下開4個或8個就可以。
每個nginx進程消耗的內(nèi)存10兆的模樣
worker_cpu_affinity
僅適用于linux,使用該選項可以綁定worker進程和CPU(2.4內(nèi)核的機器用不了)
假如是8 cpu 分配如下:
worker_cpu_affinity 00000001 00000010 00000100 00001000 00010000
00100000 01000000 10000000
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_connections
worker_rlimit_nofile 102400;
每個nginx進程打開文件描述符最大數(shù)目 配置要和系統(tǒng)的單進程打開文件數(shù)一致,linux 2.6內(nèi)核下開啟文件打開數(shù)為65535,worker_rlimit_nofile就相應(yīng)應(yīng)該填寫65535 nginx調(diào)度時分配請求到進程并不是那么的均衡,假如超過會返回502錯誤。我這里寫的大一點
use epoll
Nginx使用了最新的epoll(Linux 2.6內(nèi)核)和kqueue(freebsd)網(wǎng)絡(luò)I/O模型,而Apache則使用的是傳統(tǒng)的select模型。
處理大量的連接的讀寫,Apache所采用的select網(wǎng)絡(luò)I/O模型非常低效。在高并發(fā)服務(wù)器中,輪詢I/O是最耗時間的操作 目前Linux下能夠承受高并發(fā)
訪問的Squid、Memcached都采用的是epoll網(wǎng)絡(luò)I/O模型。
worker_processes
NGINX工作進程數(shù)(默認(rèn)值是1)。在大多數(shù)情況下,一個CPU內(nèi)核運行一個工作進程最好,建議將這個指令設(shè)置成自動就可以。有時可能想增大這個值,比如當(dāng)工作進程需要做大量的磁盤I/O。
worker_connections 65535;
每個工作進程允許最大的同時連接數(shù) (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.
client_header_buffer_size 16k
large_client_header_buffers 4 32k
客戶請求頭緩沖大小
nginx默認(rèn)會用client_header_buffer_size這個buffer來讀取header值,如果header過大,它會使用large_client_header_buffers來讀取
如果設(shè)置過小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 這個指令指定緩存是否啟用,如果啟用,將記錄文件以下信息: ·打開的文件描述符,大小信息和修改時間. ·存在的目錄信息. ·在搜索文件過程中的錯誤信息 -- 沒有這個文件,無法正確讀取,參考o(jì)pen_file_cache_errors 指令選項:
·max - 指定緩存的最大數(shù)目,如果緩存溢出,最長使用過的文件(LRU)將被移除
例: open_file_cache max=1000 inactive=20s; open_file_cache_valid 30s; open_file_cache_min_uses 2; open_file_cache_errors on;
open_file_cache_errors
語法:open_file_cache_errors on | off 默認(rèn)值:open_file_cache_errors off 使用字段:http, server, location 這個指令指定是否在搜索一個文件是記錄cache錯誤.
open_file_cache_min_uses
語法:open_file_cache_min_uses number 默認(rèn)值:open_file_cache_min_uses 1 使用字段:http, server, location 這個指令指定了在open_file_cache指令無效的參數(shù)中一定的時間范圍內(nèi)可以使用的最小文件數(shù),如 果使用更大的值,文件描述符在cache中總是打開狀態(tài).
open_file_cache_valid
語法:open_file_cache_valid time 默認(rèn)值: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;
gzip_types text/plain application/x-javascript text/css
application/xml;
gzip_vary on;
緩存靜態(tài)文件:
location ~* ^.+\.(swf|gif|png|jpg|js|css)$ {
root /usr/local/ku6/ktv/show.ku6.com/;
expires 1m;
}
響應(yīng)緩沖區(qū):
比如我們Nginx+Tomcat 代理訪問JS無法完全加載,這幾個參數(shù)影響:
proxy_buffer_size 128k;
proxy_buffers 32 128k;
proxy_busy_buffers_size 128k;
Nginx在代理了相應(yīng)服務(wù)后或根據(jù)我們配置的UpStream和location來獲取相應(yīng)的文件,首先文件會被解析到nginx的內(nèi)存或者臨時文件目錄中,然后由nginx再來響應(yīng)。那么當(dāng)proxy_buffers和proxy_buffer_size以及proxy_busy_buffers_size 都太小時,會將內(nèi)容根據(jù)nginx的配置生成到臨時文件中,但是臨時文件的大小也有默認(rèn)值。所以當(dāng)這四個值都過小時就會導(dǎo)致部分文件只加載一部分。所以要根據(jù)我們的服務(wù)器情況適當(dāng)?shù)恼{(diào)整proxy_buffers和proxy_buffer_size以及proxy_busy_buffers_size、proxy_temp_file_write_size。具體幾個參數(shù)的詳細(xì)如下:
proxy_buffers 32 128k; 設(shè)置了32個緩存區(qū),每個的大小是128k
proxy_buffer_size 128k; 每個緩存區(qū)的大小是128k,當(dāng)兩個值不一致時沒有找到具體哪個有效,建議和上面的設(shè)置一致。
proxy_busy_buffers_size 128k;設(shè)置使用中的緩存區(qū)的大小,控制傳輸至客戶端的緩存的最大
proxy_temp_file_write_size 緩存文件的大小
記錄每個請求會消耗CPU和I/O周期,一種降低這種影響的方式是緩沖訪問日志。使用緩沖,而不是每條日志記錄都單獨執(zhí)行寫操作,NGINX會緩沖一連串的日志記錄,使用單個操作把它們一起寫到文件中。
要啟用訪問日志的緩存,就涉及到在access_log指令中buffer=size這個參數(shù)。當(dāng)緩沖區(qū)達(dá)到size值時,NGINX會把緩沖區(qū)的內(nèi)容寫到日志中。讓NGINX在指定的一段時間后寫緩存,就包含flush=time參數(shù)。當(dāng)兩個參數(shù)都設(shè)置了,當(dāng)下個日志條目超出緩沖區(qū)值或者緩沖區(qū)中日志條目存留時間超過設(shè)定的時間值,NGINX都會將條目寫入日志文件。當(dāng)工作進程重新打開它的日志文件或退出時,也會記錄下來。要完全禁用訪問日志記錄的功能,將access_log 指令設(shè)置成off參數(shù)。
你可以設(shè)置多個限制,防止用戶消耗太多的資源,避免影響系統(tǒng)性能和用戶體驗及安全。 以下是相關(guān)的指令:
limit_conn and limit_conn_zone:NGINX接受客戶連接的數(shù)量限制,例如單個IP地址的連接。設(shè)置這些指令可以防止單個用戶打開太多的連接,消耗超出自己的資源。
limit_rate:傳輸?shù)娇蛻舳隧憫?yīng)速度的限制(每個打開多個連接的客戶消耗更多的帶寬)。設(shè)置這個限制防止系統(tǒng)過載,確保所有客戶端更均勻的服務(wù)質(zhì)量。
limit_req and limit_req_zone:NGINX處理請求的速度限制,與limit_rate有相同的功能??梢蕴岣甙踩裕绕涫菍Φ卿涰撁?,通過對用戶限制請求速率設(shè)置一個合理的值,避免太慢的程序覆蓋你的應(yīng)用請求(比如DDoS攻擊)。
max_conns:上游配置塊中服務(wù)器指令參數(shù)。在上游服務(wù)器組中單個服務(wù)器可接受最大并發(fā)數(shù)量。使用這個限制防止上游服務(wù)器過載。設(shè)置值為0(默認(rèn)值)表示沒有限制。
queue (NGINX Plus) :創(chuàng)建一個隊列,用來存放在上游服務(wù)器中超出他們最大max_cons限制數(shù)量的請求。這個指令可以設(shè)置隊列請求的最大值,還可以選擇設(shè)置在錯誤返回之前最大等待時間(默認(rèn)值是60秒)。如果忽略這個指令,請求不會放入隊列。
作為網(wǎng)關(guān)或者代理工作的服務(wù)器嘗試執(zhí)行請求時,從上游服務(wù)器接收到無效的響應(yīng)。
常見原因:
1、后端服務(wù)掛了的情況,直接502 (nginx error日志:connect() failed (111: Connection refused) )
2、后端服務(wù)在重啟
實例:將后端服務(wù)關(guān)掉,然后向nginx發(fā)送請求后端接口,nginx日志可以看到502錯誤。
如果nginx+php出現(xiàn)502, 錯誤分析:
php-cgi進程數(shù)不夠用、php執(zhí)行時間長(mysql慢)、或者是php-cgi進程死掉,都會出現(xiàn)502錯誤
一般來說Nginx 502 Bad Gateway和php-fpm.conf的設(shè)置有關(guān),而Nginx 504 Gateway Time-out則是與nginx.conf的設(shè)置有關(guān)
1)、查看當(dāng)前的PHP FastCGI進程數(shù)是否夠用:
netstat -anpo | grep "php-cgi" | wc -l
如果實際使用的“FastCGI進程數(shù)”接近預(yù)設(shè)的“FastCGI進程數(shù)”,那么,說明“FastCGI進程數(shù)”不夠用,需要增大。
2)、部分PHP程序的執(zhí)行時間超過了Nginx的等待時間,可以適當(dāng)增加
nginx.conf配置文件中FastCGI的timeout時間,例如:
http {
......
fastcgi_connect_timeout 300;
fastcgi_send_timeout 300;
fastcgi_read_timeout 300;
......
}
nginx作為網(wǎng)關(guān)或者代理工作的服務(wù)器嘗試執(zhí)行請求時,未能及時從上游服務(wù)器(URI標(biāo)識出的服務(wù)器,例如HTTP、FTP、LDAP)收到響應(yīng)。
常見原因:
該接口太耗時,后端服務(wù)接收到請求,開始執(zhí)行,未能在設(shè)定時間返回數(shù)據(jù)給nginx
后端服務(wù)器整體負(fù)載太高,接受到請求之后,由于線程繁忙,未能安排給請求的接口,導(dǎo)致未能在設(shè)定時間返回數(shù)據(jù)給nginx
解決:增大client_max_body_size
client_max_body_size:指令指定允許客戶端連接的最大請求實體大小,它出現(xiàn)在請求頭部的Content-Length字段. 如果請求大于指定的值,客戶端將收到一個"Request Entity Too Large" (413)錯誤. 記住,瀏覽器并不知道怎樣顯示這個錯誤.
php.ini中增大
post_max_size 和upload_max_filesize
1)如果是nginx反向代理
proxy是nginx作為client轉(zhuǎn)發(fā)時使用的,如果header過大,超出了默認(rèn)的1k,就會引發(fā)上述的upstream sent too big header (說白了就是nginx把外部請求給后端server,后端server返回的header 太大nginx處理不過來就導(dǎo)致了。
server {
listen 80;
server_name *.xywy.com ;
large_client_header_buffers 4 16k;
location / {
#添加這3行
proxy_buffer_size 64k;
proxy_buffers 32 32k;
proxy_busy_buffers_size 128k;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_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 index.htm index.php;
client_header_buffer_size 128k;
large_client_header_buffers 4 128k;
proxy_buffer_size 64k;
proxy_buffers 8 64k;
fastcgi_buffer_size 128k;
fastcgi_buffers 4 128k;
location / {
......
}
}
漏洞介紹:nginx是一款高性能的web服務(wù)器,使用非常廣泛,其不僅經(jīng)常被用作反向代理,也可以非常好的支持PHP的運行。80sec發(fā)現(xiàn)其中存在一個較為嚴(yán)重的安全問題,默認(rèn)情況下可能導(dǎo)致服務(wù)器錯誤的將任何類型的文件以PHP的方式進行解析,這將導(dǎo)致嚴(yán)重的安全問題,使得惡意的攻擊者可能攻陷支持php的nginx服務(wù)器。
漏洞分析:nginx默認(rèn)以cgi的方式支持php的運行,譬如在配置文件當(dāng)中可以以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環(huán)境變量進行選擇,其中傳遞到后端Fastcgi的關(guān)鍵變量SCRIPT_FILENAME由nginx生成的$fastcgi_script_name決定,而通過分析可以看到$fastcgi_script_name是直接由URI環(huán)境變量控制的,這里就是產(chǎn)生問題的點。而為了較好的支持PATH_INFO的提取,在PHP的配置選項里存在cgi.fix_pathinfo選項,其目的是為了從SCRIPT_FILENAME里取出真正的腳本名。
那么假設(shè)存在一個http://www.80sec.com/80sec.jpg,我們以如下的方式去訪問
http://www.80sec.com/80sec.jpg/80sec.php
將會得到一個URI/80sec.jpg/80sec.php
經(jīng)過location指令,該請求將會交給后端的fastcgi處理,nginx為其設(shè)置環(huán)境變量SCRIPT_FILENAME,內(nèi)容為/scripts/80sec.jpg/80sec.php
而在其他的webserver如lighttpd當(dāng)中,我們發(fā)現(xiàn)其中的SCRIPT_FILENAME被正確的設(shè)置為/scripts/80sec.jpg
所以不存在此問題。
后端的fastcgi在接受到該選項時,會根據(jù)fix_pathinfo配置決定是否對SCRIPT_FILENAME進行額外的處理,一般情況下如果不對fix_pathinfo進行設(shè)置將影響使用PATH_INFO進行路由選擇的應(yīng)用,所以該選項一般配置開啟。Php通過該選項之后將查找其中真正的腳本文件名字,查找的方式也是查看文件是否存在,這個時候?qū)⒎蛛x出SCRIPT_FILENAME和PATH_INFO分別為/scripts/80sec.jpg和80sec.php
最后,以/scripts/80sec.jpg作為此次請求需要執(zhí)行的腳本,攻擊者就可以實現(xiàn)讓nginx以php來解析任何類型的文件了。
POC: 訪問一個nginx來支持php的站點,在一個任何資源的文件如robots.txt后面加上/80sec.php,這個時候你可以看到如下的區(qū)別:
訪問http://www.80sec.com/robots.txtHTTP/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
訪問訪問http://www.80sec.com/robots.txt/80sec.phpHTTP/1.1 200 OK
Server: nginx/0.6.32
Date: Thu, 20 May 2010 10:06:49 GMT
Content-Type: text/html
Transfer-Encoding: chunked
Connection: keep-alive
Keep-Alive: timeout=20
X-Powered-By: PHP/5.2.6
其中的Content-Type的變化說明了后端負(fù)責(zé)解析的變化,該站點就可能存在漏洞。
漏洞廠商:http://www.nginx.org
解決方案:
我們已經(jīng)嘗試聯(lián)系官方,但是此前你可以通過以下的方式來減少損失關(guān)閉cgi.fix_pathinfo為0
或者if ( $fastcgi_script_name ~ ..*/.*php ) {
return 403;
}
以上是“Nginx的模塊與工作原理是什么”這篇文章的所有內(nèi)容,感謝各位的閱讀!相信大家都有了一定的了解,希望分享的內(nèi)容對大家有所幫助,如果還想學(xué)習(xí)更多知識,歡迎關(guān)注億速云行業(yè)資訊頻道!
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。