nginx底層實現有幾個主要的模塊:
- 進程模塊
- 事件模塊
- 網絡模塊
進程模塊
默認采用守護模式啟動,守護模式讓master進程啟動后在后台運行,不在窗口上卡住。
Nginx 啟動后會有一個 Master 進程和多個Worker 進程,Master 進程主要用來管理 Worker 進程,對網絡事件進程進行收集和分發,調度哪個模塊可以占用 CPU 資源,從而處理請求。
一般配置Worker進程的個數與機器cpu個數一致,從而達到cpu資源的最大化利用,也避免由於進程資源分配帶來的額外開銷。
Master進程工作原理
master進程主要用來管理worker進程,如:
- 接收來自外界的信號,向各worker進程發送信號;
- 監控worker進程的運行狀態,當worker進程退出后(異常情況下),會自動重新啟動新的worker進程。
Worker進程工作原理
基本的網絡事件,則是放在worker進程中來處理了。
worker進程之間是平等的,每個進程,處理請求的機會也是一樣的。
當一個worker進程在accept這個連接之后,就開始讀取請求,解析請求,處理請求,產生數據后,再返回給客戶端,最后才斷開連接,這樣一個完整的請求就是這樣的了。我們可以看到,一個請求,完全由worker進程來處理,而且只在一個worker進程中處理。
(nginx提供了一個accept_mutex(互斥鎖),有了這把鎖之后,同一時刻,就只會有一個進程在accpet連接,worker進程內部不需要鎖)
好處:
- 對於每個worker進程來說,獨立的進程,不需要加鎖,所以省掉了鎖帶來的開銷,同時在編程以及問題查找時,也會方便很多。
- 互相之間不會影響,一個進程退出后,其它進程還在工作,服務不會中斷,master進程則很快啟動新的worker進程。
事件模塊
apache的常用工作方式:每個請求會獨占一個工作線程,當並發數上到幾千時,就同時有幾千的線程在處理請求了。這對操作系統來說,是個不小的挑戰,線程帶來的內存占用非常大,線程的上下文切換帶來的cpu開銷很大,自然性能就上不去了,而這些開銷完全是沒有意義的。
nginx采用多worker的方式來處理請求,每個worker里面只有一個主線程,那能夠處理的並發數很有限啊,多少個worker就能處理多少個並發,何來高並發呢?非也,這就是nginx的高明之處,nginx采用了異步非阻塞的方式來處理請求,也就是說,nginx是可以同時處理成千上萬個請求的。
為什么nginx可以采用異步非阻塞的方式來處理呢,或者異步非阻塞到底是怎么回事呢?
我們先回到原點,看看一個請求的完整過程。
請求過來,要建立連接,然后再接收數據,接收數據后,再發送數據。
nginx里面,最忌諱阻塞的系統調用了。不要阻塞,那就非阻塞嘍。非阻塞就是,事件沒有准備好,馬上返回EAGAIN,告訴你,事件還沒准備好呢,你慌什么,過會再來吧。好吧,你過一會,再來檢查一下事件,直到事件准備好了為止,在這期間,你就可以先去做其它事情,然后再來看看事件好了沒。
雖然不阻塞了,但你得不時地過來檢查一下事件的狀態,你可以做更多的事情了,但帶來的開銷也是不小的。所以,才會有了異步非阻塞的事件處理機制。
我們之前說過,推薦設置worker的個數為cpu的核數,在這里就很容易理解了,更多的worker數,只會導致進程來競爭cpu資源了,從而帶來不必要的上下文切換。
現在,知道了nginx為什么會選擇這樣的進程模型與事件模型了。對於一個基本的web服務器來說,事件通常有三種類型,網絡事件、信號、定時器。從上面的講解中知道,網絡事件通過異步非阻塞可以很好的解決掉。如何處理信號與定時器?
- 信號的處理。(待補充)
- 定時器。nginx里面的定時器事件是放在一顆維護定時器的紅黑樹里面,每次在進入epoll_wait前,先從該紅黑樹里面拿到所有定時器事件的最小時間,在計算出epoll_wait的超時時間后進入epoll_wait。(待補充)
網絡模塊
先不提。
參考資料:
- Nginx 之實現原理 http://www.jianshu.com/p/b77482d4b670
- Nginx 多進程模型是如何實現高並發的? https://www.zhihu.com/question/22062795
- nginx平台初探 http://tengine.taobao.org/book/chapter_02.html