Nginx服務器啟動后,產(chǎn)生一個主進程(master process),主進程執(zhí)行一系列工作后產(chǎn)生一個或者多個工作進程(worker processes)。主進程主要進行Nginx配置文件解析、數(shù)據(jù)結構初始化、模塊配置和注冊、信號處理、網(wǎng)絡監(jiān)聽生成、工作進程生成和管理等工作;工作進程主要進行進程初始化、模塊調用和請求處理等工作,是Nginx服務器提供服務的主體。
在客戶端請求動態(tài)站點的過程中,Nginx服務器還涉及和后端服務器的通信。Nginx服務器將接收到的Web請求通過代理轉發(fā)到后端服務器,由后端服務器進行數(shù)據(jù)處理和頁面組織,然后將結果返回。
另外,Nginx服務器為了提高對請求的響應效率,進一步降低網(wǎng)絡壓力,采用了緩存機制,將歷史應答數(shù)據(jù)緩存到本地。在每次Nginx服務器啟動后的一段時間內,會啟動專門的進程對本地緩存的內容重建索引,保證對緩存文件的快速訪問。
根據(jù)上面的分析,我們可以將Nginx服務器的結構大致分為主進程、工作進程、后端服務器和緩存等部分
在該示意圖中,有幾個方面的內容我們需要重點闡述,包括Nginx服務器的進程、進程交互和Run-Loop事件處理循環(huán)機制等。
Nginx服務器的進程
到目前為止,我們一共提到Nginx服務器的三大類進程:一類是主進程,另一類是由主進程生成的工作進程,還有剛才提到的用于為緩存文件建立索引的進程。
主進程(Master Process)
Nginx服務器啟動時運行的主要進程。它的主要功能是與外界通信和對內部其他進程進行管理,具體來說有以下幾點:
- 讀取Nginx配置文件并驗證其有效性和正確性。
- 建立、綁定和關閉Socket。
- 按照配置生成、管理和結束工作進程。
- 接收外界指令,比如重啟、升級及退出服務器等指令。
- 不中斷服務,實現(xiàn)平滑重啟,應用新配置。
- 不中斷服務,實現(xiàn)平滑升級,升級失敗進行回滾處理。
- 開啟日志文件,獲取文件描述符。
- 編譯和處理Perl腳本。
工作進程(Worker Process)
由主進程生成,生成數(shù)量可以通過Nginx配置文件指定,正常情況下生存于主進程的整個生命周期。該進程的主要工作有以下幾項:
- 接收客戶端請求。
- 將請求依次送入各個功能模塊進行過濾處理。
- IO調用,獲取響應數(shù)據(jù)。
- 與后端服務器通信,接收后端服務器處理結果。
- 數(shù)據(jù)緩存,訪問緩存索引、查詢和調用緩存數(shù)據(jù)。
- 發(fā)送請求結果,響應客戶端請求。
- 接收主程序指令,比如重啟、升級和退出等指令。
工作進程完成的工作還有很多,我們在這里列出了主要的幾項。從這些工作中可以看到,該進程是Nginx服務器提供Web服務、處理客戶端請求的主要進程,完成了Nginx服務器的主體工作。因此,在實際使用中,作為服務器管理者,我們應該重點監(jiān)視工作進程的運行狀態(tài),保證Nginx服務器對外提供穩(wěn)定的Web服務。
緩存索引重建及管理進程(Cache Loader & Cache Manager)
上圖中的Cache模塊,主要由緩存索引重建(Cache Loader)和緩存索引管理(Cache Manager)兩類進程完成工作。緩存索引重建進程是在Nginx服務啟動一段時間之后(默認是1分鐘)由主進程生成,在緩存元數(shù)據(jù)重建完成后就自動退出;緩存索引管理進程一般存在于主進程的整個生命周期,負責對緩存索引進行管理。
緩存索引重建進程完成的主要工作是,根據(jù)本地磁盤上的緩存文件在內存中建立索引元數(shù)據(jù)庫。該進程啟動后,對本地磁盤上存放緩存文件的目錄結構進行掃描,檢查內存中已有的緩存元數(shù)據(jù)是否正確,并更新索引元數(shù)據(jù)庫。
緩存索引管理進程主要負責在索引元數(shù)據(jù)更新完成后,對元數(shù)據(jù)是否過期做出判斷。
這兩個進程維護的內存索引元數(shù)據(jù)庫,為工作進程對緩存數(shù)據(jù)的快速查詢提供了便利。
進程交互
Nginx服務器在使用Master-Worker模型時,會涉及主進程與工作進程(Master-Worker)之間的交互和工作進程(Worker-Worker)之間的交互。這兩類交互都依賴于管道(channel)機制,交互的準備工作都是在工作進程生成時完成的。
Master-Worker交互
工作進程是由主進程生成的(使用了fork函數(shù),具體的源碼實現(xiàn)我們在后邊的相關章節(jié)中完整解析)。Nginx服務器啟動以后,主進程根據(jù)配置文件決定生成的工作進程的數(shù)量,然后建立一張全局的工作進程表用于存放當前未退出的所有工作進程。
在主進程生成工作進程后,將新生成的工作進程加入到工作進程表中,并建立一個單向管道并將其傳遞給該工作進程。該管道與普通的管道不同,它是由主進程指向工作進程的單向管道,包含了主進程向工作進程發(fā)出的指令、工作進程ID、工作進程在工作進程表中的索引和必要的文件描述符等信息。
主進程與外界通過信號機制進行通信,當接收到需要處理的信號時,它通過管道向相關的工作進程發(fā)送正確的指令。每個工作進程都有能力捕獲管道中可讀事件,當管道中有可讀事件時,工作進程從管道讀取并解析指令,然后采取相應的措施。這樣就完成了Master-Worker的交互。
Worker-Worker交互
Worker-Worker交互在實現(xiàn)原理上和Master-Worker交互基本是一樣的。只要工作進程之間能夠得到彼此的信息,建立管道,即可通信。由于工作進程之間是相互隔離的,因此一個進程要想知道另一個進程的信息,只能通過主進程來設置了。
為了達到工作進程之間交互的目的,主進程在生成工作進程后,在工作進程表中進行遍歷,將該新進程的ID以及針對該進程建立的管道句柄傳遞給工作進程表中的其他進程,為工作進程之間的交互做準備。每個工作進程捕獲管道中可讀事件,根據(jù)指令采取響應的措施。
當工作進程W1需要向W2發(fā)送指令時,首先在主進程給它的其他工作進程信息中找到W2的進程ID,然后將正確的指令寫入指向W2的通道。工作進程W2捕獲到管道中的事件后,解析指令并采取相應措施。這樣就完成了Worker-Worker交互。
Run Loops事件處理循環(huán)模型
Run Loops,指的是進程內部用來不停地調配工作,對事件進行循環(huán)處理的一種模型。它屬于進程或者線程的基礎架構部分。該模型對事件的處理不是自動的,需要在設計代碼過程中,在適當?shù)臅r候啟動Run-Loop機制對輸入的事件作出響應。
該模型是一個集合,集合中的每一個元素稱為一個Run-Loop。每個Run-Loop可運行在不同的模式下,其中可以包含它所監(jiān)聽的輸入事件源、定時器以及在事件發(fā)生時需要通知的Run-Loop監(jiān)聽器(Run-Loop Observers)。為了監(jiān)聽特定的事件,可以在Run Loops中添加相應的Run-Loop監(jiān)聽器。當被監(jiān)聽的事件發(fā)生時,Run-Loop會產(chǎn)生一個消息,被Run-Loop監(jiān)聽器捕獲,從而執(zhí)行預定的動作。
Nginx服務器在工作進程中實現(xiàn)了Run-Loop事件處理循環(huán)模型的使用,用來處理客戶端發(fā)來的請求事件。該部分的實現(xiàn)可以說是Nginx服務器程序實現(xiàn)中最為復雜的部分,包含了對輸入事件繁雜的響應和處理過程,并且這些處理過程都是基于異步任務處理的。
通過學習Nginx服務器的整體架構,我們對Nginx服務器各個模塊的作用和聯(lián)系有了比較清晰的認識。可以看到,Nginx服務器提供了異步的、非阻塞的Web服務,系統(tǒng)中的模塊各司其職,彼此之間通常使用網(wǎng)絡、管道和信號等機制進行通信,從而保持了松耦合的關系。工作進程中事件處理機制的使用,在很大程度上降低了在網(wǎng)絡負載繁重的情況下Nginx服務器對內存、磁盤的壓力,同時保證了對客戶端請求的及時響應。
這里需要提及的一點是,當磁盤沒有足夠的性能處理大量IO調用時,工作進程仍然可能因為磁盤讀寫調用而阻塞,進而導致客戶端請求超時失敗等問題。目前可以通過多種方法來降低對磁盤IO的調用,比如引入異步輸入/輸出(Asynchronous Input/Output,AIO)機制等,但這些處理辦法沒有從根本上解決問題。為了盡量避免產(chǎn)生這種問題,大家在實際部署Nginx服務器時,應當對其運行環(huán)境有一個基本的了解,針對不同的網(wǎng)絡負載環(huán)境選擇相匹配的硬件環(huán)境,并對Nginx服務器進行合理的配置。