Nginx的工作原理
1.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ù)代理和負載均衡等功能喧笔。
下圖展示了Nginx模塊常規(guī)的HTTP請求和響應(yīng)的過程帽驯。
Nginx本身做的工作實際很少,當(dāng)它接到一個HTTP請求時溃斋,它僅僅是通過查找配置文件將此次請求映射到一個location block 界拦,因此location中所配置的各個指令則會啟動不同的模塊去完成工作,因此模塊可以看做Nginx真正的勞動工作者梗劫。通常一個location中的指令會涉及一個handler模塊和多個filter模塊(當(dāng)然享甸,多個location可以復(fù)用同一個模塊)。handler模塊負責(zé)處理請求梳侨,完成響應(yīng)內(nèi)容的生成蛉威,而filter模塊對響應(yīng)內(nèi)容進行處理。
Nginx的模塊直接被編譯進Nginx走哺,因此屬于靜態(tài)編譯方式蚯嫌。啟動Nginx后,Nginx的模塊被自動加載丙躏,不像Apache择示,首先將模塊編譯為一個so文件,然后在配置文件中指定是否進行加載晒旅。在解析配置文件時栅盲,Nginx的每個模塊都有可能去處理某個請求,但是同一個處理請求只能由一個模塊來完成废恋。
2.Nginx的進程模型
在工作方式上谈秫,Nginx分為單工作進程和多工作進程兩種模式扒寄。在單工作進程模式下,除主進程外拟烫,還有一個工作進程该编,工作進程是單線程的;在多工作進程模式下硕淑,每個工作進程包含多個線程课竣。Nginx默認為單工作進程模式。
Nginx在啟動后喜颁,會有一個master進程和多個worker進程稠氮。
master進程
主要用來管理worker進程,包含:接收來自外界的信號半开,向各worker進程發(fā)送信號,監(jiān)控worker進程的運行狀態(tài)赃份,當(dāng)worker進程退出后(異常情況下)寂拆,會自動重新啟動新的worker進程。
master進程充當(dāng)整個進程組與用戶的交互接口抓韩,同時對進程進行監(jiān)護纠永。它不需要處理網(wǎng)絡(luò)事件,不負責(zé)業(yè)務(wù)的執(zhí)行谒拴,只會通過管理worker進程來實現(xiàn)重啟服務(wù)尝江、平滑升級、更換日志文件英上、配置文件實時生效等功能炭序。
我們要控制nginx,只需要通過kill向master進程發(fā)送信號就行了苍日。比如kill -HUP pid惭聂,則是告訴nginx,從容地重啟nginx相恃,我們一般用這個信號來重啟nginx辜纲,或重新加載配置,因為是從容地重啟拦耐,因此服務(wù)是不中斷的耕腾。master進程在接收到HUP信號后是怎么做的呢?首先master進程在接到信號后杀糯,會先重新加載配置文件扫俺,然后再啟動新的worker進程,并向所有老的worker進程發(fā)送信號火脉,告訴他們可以光榮退休了牵舵。新的worker在啟動后柒啤,就開始接收新的請求,而老的worker在收到來自master的信號后畸颅,就不再接收新的請求担巩,并且在當(dāng)前進程中的所有未處理完的請求處理完成后,再退出没炒。當(dāng)然涛癌,直接給master進程發(fā)送信號,這是比較老的操作方式送火,nginx在0.8版本之后拳话,引入了一系列命令行參數(shù),來方便我們管理种吸。比如弃衍,./nginx -s reload,就是來重啟nginx坚俗,./nginx -s stop镜盯,就是來停止nginx的運行。如何做到的呢猖败?我們還是拿reload來說速缆,我們看到,執(zhí)行命令時恩闻,我們是啟動一個新的nginx進程艺糜,而新的nginx進程在解析到reload參數(shù)后,就知道我們的目的是控制nginx來重新加載配置文件了幢尚,它會向master進程發(fā)送信號破停,然后接下來的動作,就和我們直接向master進程發(fā)送信號一樣了侠草。
worker進程:
而基本的網(wǎng)絡(luò)事件辱挥,則是放在worker進程中來處理了。多個worker進程之間是對等的边涕,他們同等競爭來自客戶端的請求晤碘,各進程互相之間是獨立的。一個請求功蜓,只可能在一個worker進程中處理园爷,一個worker進程,不可能處理其它進程的請求式撼。worker進程的個數(shù)是可以設(shè)置的童社,一般我們會設(shè)置與機器cpu核數(shù)一致,這里面的原因與nginx的進程模型以及事件處理模型是分不開的著隆。
worker進程之間是平等的扰楼,每個進程呀癣,處理請求的機會也是一樣的。當(dāng)我們提供80端口的http服務(wù)時弦赖,一個連接請求過來项栏,每個進程都有可能處理這個連接,怎么做到的呢蹬竖?首先沼沈,每個worker進程都是從master進程fork過來,在master進程里面币厕,先建立好需要listen的socket(listenfd)之后列另,然后再fork出多個worker進程。所有worker進程的listenfd會在新連接到來時變得可讀旦装,為保證只有一個進程處理該連接页衙,所有worker進程在注冊listenfd讀事件前搶accept_mutex,搶到互斥鎖的那個進程注冊listenfd讀事件同辣,在讀事件里調(diào)用accept接受該連接拷姿。當(dāng)一個worker進程在accept這個連接之后,就開始讀取請求旱函,解析請求,處理請求描滔,產(chǎn)生數(shù)據(jù)后棒妨,再返回給客戶端,最后才斷開連接含长,這樣一個完整的請求就是這樣的了券腔。我們可以看到,一個請求拘泞,完全由worker進程來處理纷纫,而且只在一個worker進程中處理。worker進程之間是平等的陪腌,每個進程辱魁,處理請求的機會也是一樣的。當(dāng)我們提供80端口的http服務(wù)時诗鸭,一個連接請求過來染簇,每個進程都有可能處理這個連接,怎么做到的呢强岸?首先锻弓,每個worker進程都是從master進程fork過來,在master進程里面蝌箍,先建立好需要listen的socket(listenfd)之后青灼,然后再fork出多個worker進程暴心。所有worker進程的listenfd會在新連接到來時變得可讀,為保證只有一個進程處理該連接杂拨,所有worker進程在注冊listenfd讀事件前搶accept_mutex专普,搶到互斥鎖的那個進程注冊listenfd讀事件,在讀事件里調(diào)用accept接受該連接扳躬。當(dāng)一個worker進程在accept這個連接之后脆诉,就開始讀取請求,解析請求贷币,處理請求击胜,產(chǎn)生數(shù)據(jù)后,再返回給客戶端役纹,最后才斷開連接偶摔,這樣一個完整的請求就是這樣的了。我們可以看到促脉,一個請求辰斋,完全由worker進程來處理,而且只在一個worker進程中處理瘸味。
nginx的進程模型宫仗,可以由下圖來表示:
3.什么是FastCGI
FastCGI是一個可伸縮地、高速地在HTTP server和動態(tài)腳本語言間通信的接口旁仿。多數(shù)流行的HTTP server都支持FastCGI藕夫,包括Apache、Nginx和lighttpd等枯冈。同時毅贮,F(xiàn)astCGI也被許多腳本語言支持,其中就有PHP尘奏。
FastCGI是從CGI發(fā)展改進而來的滩褥。
CGI工作原理和缺點:
每次HTTP服務(wù)器遇到動態(tài)程序時都需要重新啟動腳本解析器來執(zhí)行解析,然后將結(jié)果返回給HTTP服務(wù)器炫加。
這在處理高并發(fā)訪問時幾乎是不可用的瑰煎。另外傳統(tǒng)的CGI接口方式安全性也很差,現(xiàn)在已經(jīng)很少使用了琢感。
FastCGI工作原理和優(yōu)點:
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)的性能其垄。
另外fastCGI程序與CGI程序與服務(wù)器的交互方式也不同
CGI程序通過環(huán)境變量、命令行卤橄、標準輸入輸出進行交互绿满,因此CGI程序進程必須與服務(wù)器進程在同一臺物理計算機上,而fastCGI程序與服務(wù)器進程通過網(wǎng)絡(luò)連接交互窟扑,因此fastCGI程序可以分布在不同的計算機上喇颁,這不但可以提高性能,同時也提高了系統(tǒng)的擴展能力嚎货。
4.什么是PHP-fpm
PHP-FPM是管理FastCGI的一個管理器橘霎,它作為PHP的插件存在,在安裝PHP要想使用PHP-FPM時在老php的老版本(php5.3.3之前)就需要把PHP-FPM以補丁的形式安裝到PHP中殖属,而且PHP要與PHP-FPM版本一致姐叁,這是必須的)
PHP-FPM是FastCGI的實現(xiàn),任何實現(xiàn)了FastCGI協(xié)議的Web Server都能夠與之通信洗显。FPM之于標準的FastCGI外潜,也提供了一些增強功能,具體可以參考官方文檔:PHP: FPM Installation挠唆。
FPM是一個PHP進程管理器处窥,包含master進程和worker進程兩種進程:master進程只有一個,負責(zé)監(jiān)聽端口玄组,接收來自Web Server的請求碧库,而worker進程則一般有多個(具體數(shù)量根據(jù)實際需要配置),每個進程內(nèi)部都嵌入了一個PHP解釋器巧勤,是PHP代碼真正執(zhí)行的地方,下圖是我本機上fpm的進程情況弄匕,1一個master進程颅悉,3個worker進程:
從FPM接收到請求迁匠,到處理完畢剩瓶,其具體的流程如下:
1.FPM的master進程接收到請求
2.master進程根據(jù)配置指派特定的worker進程進行請求處理,如果沒有可用進程城丧,返回錯誤延曙,這也是我們配合Nginx遇到502錯誤比較多的原因。
3.worker進程處理請求亡哄,如果超時枝缔,返回504錯誤
4.請求處理結(jié)束,返回結(jié)果
5.FastCGI子進程接著等待并處理來自FastCGI進程管理器(運行在 WebServer中)的下一個連接
4.那么Nginx,PHP-fpm和FastCGI是怎么的運行流程呢愿卸?
Nginx不支持對外部程序的直接調(diào)用或者解析灵临,所有的外部程序(包括PHP)必須通過FastCGI接口來調(diào)用。FastCGI接口在Linux下是socket(這個socket可以是文件socket趴荸,也可以是ip socket)儒溉。
1)、FastCGI進程管理器php-fpm自身初始化发钝,啟動主進程php-fpm和啟動start_servers個CGI 子進程顿涣。
主進程php-fpm主要是管理fastcgi子進程,監(jiān)聽9000(這個根據(jù)配置文件的監(jiān)聽端口改變而變)端口酝豪。
fastcgi子進程等待來自Web Server的連接涛碑。
2)、當(dāng)客戶端請求到達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)境變量和標準輸入發(fā)送到FastCGI子進程痛悯。
4)余黎、FastCGI子進程完成處理后將標準輸出和錯誤信息從同一連接返回Web Server。當(dāng)FastCGI子進程關(guān)閉連接時载萌,請求便告處理完成惧财。
5)、FastCGI子進程接著等待并處理來自FastCGI進程管理器(運行在 WebServer中)的下一個連接扭仁。
以上流程是根據(jù)下方配置文件來說明:
PHP-FPM的默認配置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地址和端口乖坠。
參考:
http://www.reibang.com/p/d0b858ed5030
http://www.reibang.com/p/a51a2d70e096
如有不正確之處搀突,麻煩指出改正。謝謝~