NGINX以高性能的負(fù)載均衡器,緩存,和web服務(wù)器聞名,驅(qū)動了全球超過 40% 最繁忙的網(wǎng)站,因此
有一定的參考價值,有需要的朋友可以參考一下,希望對大家有所幫助
一. 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分為單工作進程和多工作進程兩種模式。在單工作進程模式下,除主進程外,還有一個工作進程,工作進程是單線程的;在多工作進程模式下,每個工作進程包含多個線程。Nginx默認(rèn)為單工作進程模式。
Nginx在啟動后,會有一個master進程和多個worker進程。
1、master進程:管理進程
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ā)送信號一樣了。
2、worker進程:處理請求
而基本的網(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的進程模型,可以由下圖來表示:
三. Nginx為啥性能高-多進程IO模型
參考http://mp.weixin.qq.com/s?__biz=MjM5NTg2NTU0Ng==&mid=407889757&idx=3&sn=cfa8a70a5fd2a674a91076f67808273c&scene=23&srcid=0401aeJQEraSG6uvLj69Hfve#rd
1、nginx采用多進程模型好處
首先,對于每個worker進程來說,獨立的進程,不需要加鎖,所以省掉了鎖帶來的開銷,同時在編程以及問題查找時,也會方便很多。
其次,采用獨立的進程,可以讓互相之間不會影響,一個進程退出后,其它進程還在工作,服務(wù)不會中斷,master進程則很快啟動新的worker進程。當(dāng)然,worker進程的異常退出,肯定是程序有bug了,異常退出,會導(dǎo)致當(dāng)前worker上的所有請求失敗,不過不會影響到所有請求,所以降低了風(fēng)險。
2、nginx多進程事件模型:異步非阻塞
雖然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開銷很大,自然性能就上不去了,而這些開銷完全是沒有意義的。