引言:衆所周知,Nginx 服務器是一個高性能的 Web 和反向代理服務器。Nginx 在激烈的 Web 服務器競爭中依舊保持良好的發展勢頭,一度成爲 Web 服務器市場的後期之秀,這一切跟 Nginx 的架構設計是分不開的。spring
高度模塊化的設計是 Nginx 的架構基礎。Nginx 服務器被分解爲多個模塊,每一個模塊就是一個功能模塊,只負責自身的功能,模塊之間嚴格遵循「高內聚,低耦合」的原則。編程
核心模塊是 Nginx 服務器正常運行必不可少的模塊,提供錯誤日誌記錄、配置文件解析、事件驅動機制、進程管理等核心功能。性能優化
標準 HTTP 模塊提供 HTTP 協議解析相關的功能,如:端口配置、網頁編碼設置、HTTP 響應頭設置等。服務器
可選 HTTP 模塊主要用於擴展標準的 HTTP 功能,讓 Nginx 能處理一些特殊的服務,如:Flash 多媒體傳輸、解析 GeoIP 請求、SSL 支持等。微信
郵件服務模塊主要用於支持 Nginx 的郵件服務,包括對 POP3 協議、IMAP 協議和 SMTP 協議的支持。多線程
第三方模塊是爲了擴展 Nginx 服務器應用,完成開發者自定義功能,如:Json 支持、Lua 支持等。架構
Nginx 是一個高性能的 Web 服務器,可以同時處理大量的併發請求。它結合多進程機制和異步機制,異步機制使用的是異步非阻塞方式,接下來就給你們介紹一下 Nginx 的多線程機制和異步非阻塞機制。併發
服務器每當收到一個客戶端時。就有服務器主進程(master process)生成一個子進程(worker process)出來和客戶端創建鏈接進行交互,直到鏈接斷開,該子進程就結束了。使用進程的好處是各個進程之間相互獨立,不須要加鎖,減小了使用鎖對性能形成影響,同時下降編程的複雜度,下降開發成本。其次,採用獨立的進程,可讓進程互相之間不會影響,若是一個進程發生異常退出時,其它進程正常工做,master 進程則很快啓動新的 worker 進程,確保服務部中斷,將風險降到最低。缺點是操做系統生成一個子進程須要進行內存複製等操做,在資源和時間上會產生必定的開銷;當有大量請求時,會致使系統性能降低。異步
每一個工做進程使用異步非阻塞方式,能夠處理多個客戶端請求。當某個工做進程接收到客戶端的請求之後,調用 IO 進行處理,若是不能當即獲得結果,就去處理其餘的請求(即爲非阻塞);而客戶端在此期間也無需等待響應,能夠去處理其餘事情(即爲異步);當 IO 返回時,就會通知此工做進程;該進程獲得通知,暫時掛起當前處理的事務去響應客戶端請求。socket
在 Nginx 的異步非阻塞機制中,工做進程在調用 IO 後,就去處理其餘的請求,當 IO 調用返回後,會通知該工做進程。對於這樣的系統調用,主要使用 Nginx 服務器的事件驅動模型來實現。
如上圖所示,Nginx 的事件驅動模型由事件收集器、事件發送器和事件處理器三部分基本單元組成。其中,事件收集器負責收集 worker 進程的各類 IO 請求,事件發送器負責將 IO 事件發送到事件處理器,而事件處理器負責各類事件的響應工做。
事件發送器將每一個請求放入一個待處理事件的列表,使用非阻塞 I/O 方式調用「事件處理器」來處理該請求。其處理方式稱爲「多路 IO 複用方法」,常見的包括如下三種:select 模型、poll 模型、epoll 模型。
Nginx 服務器使用 master/worker 多進程模式。多線程啓動和執行的流程以下:主程序 Master process 啓動後,經過一個 for 循環來接收和處理外部信號;主進程經過 fork() 函數產生子進程,每一個子進程執行一個 for 循環來實現 Nginx 服務器對事件的接收和處理。
通常推薦 worker 進程數與 cpu 內核數一致,這樣一來不存在大量的子進程生成和管理任務,避免了進程之間競爭 CPU 資源和進程切換的開銷。並且 Nginx 爲了更好的利用多核特性,提供了 cpu 親緣性的綁定選項,咱們能夠將某一個進程綁定在某一個核上,這樣就不會由於進程的切換帶來 cache 的失效。
對於每一個請求,有且只有一個工做進程對其處理。首先,每一個 worker 進程都是從 master 進程 fork 過來,在 master 進程裏面,先創建好須要 listen 的 socket(listenfd)以後,而後再 fork 出多個 worker 進程。全部 worker 進程的 listenfd 會在新鏈接到來時變得可讀,爲保證只有一個進程處理該鏈接,全部 worker 進程在註冊 listenfd 讀事件前搶 accept_mutex,搶到互斥鎖的那個進程註冊 listenfd 讀事件,在讀事件裏調用 accept 接受該鏈接。當一個 worker 進程在 accept 這個鏈接以後,就開始讀取請求,解析請求,處理請求,產生數據後,再返回給客戶端,最後才斷開鏈接,這樣一個完整的請求就是這樣的了。咱們能夠看到,一個請求,徹底由 worker 進程來處理,並且只在一個 worker 進程中處理。
在 Nginx 服務器的運行過程當中,主進程和工做進程須要進程交互。交互依賴於 Socket 實現的管道來實現。
這條管道與普通的管道不一樣,它是由主進程指向工做進程的單向管道,包含主進程向工做進程發出的指令,工做進程 ID 等;同時主進程與外界經過信號通訊;每一個子進程具有接收信號,並處理相應的事件的能力。
這種交互是和 Master-Worker 交互是基本一致的,可是會經過主進程。工做進程之間是相互隔離的,因此當工做進程 W1 須要向工做進程 W2 發指令時,首先找到 W2 的進程 ID,而後將正確的指令寫入指向 W2 的通道。W2 收到信號採起相應的措施。
在此我向你們推薦一個架構學習交流羣。交流學習羣號:478030634 裏面會分享一些資深架構師錄製的視頻錄像:有Spring,MyBatis,Netty源碼分析,高併發、高性能、分佈式、微服務架構的原理,JVM性能優化、分佈式架構等這些成爲架構師必備的知識體系。還能領取免費的學習資源,目前受益良多
經過這篇文章,咱們對 Nginx 服務器的總體架構有了一個總體的認識。包括其模塊化的設計、
多進程和異步非阻塞的請求處理方式、事件驅動模型等。經過這些理論知識,對於咱們之後學習 Nginx 的源碼有很大的幫助;也推薦你們多看看 Nginx 的源碼,才能更好地領悟 Nginx 的設計思想。
注:關注做者微信公衆號,瞭解更多分佈式架構、微服務、netty、MySQL、spring、性能優化、等知識點。
公衆號:《Java爛豬皮》