nginx介紹(二) 架構篇

2. nginx架構總覽

傳統的基於進程或者基於線程的模型處理併發的方式都是爲每個連接單獨創建一個處理進程或線程,會在網絡傳輸或者I/O操作上阻塞。而這對應用來說,在內存和 CPU的使用上效率都是非常低的。而且生成一個單獨的進程或者線程還需要爲該進程或者線程準備新的運行環境包括分配堆棧內存,還必須爲它創新一個新的上下文執行環境。創建這些都消耗額外的CPU時間,這最終也會因爲線程上下文來回切換導致性能非常差。以上這些問題都存在於老的web服務器架構中,比如 Apache。因此,需要在提供一些豐富的通用功能和對服務器資源合理使用這兩者之間進行權衡取捨。

最初,nginx打算作爲一個專業工具通過獲取更高的性能,更好的比重以及對服務器資源更合理的使用來滿足一個網站動態增長的要求,所以它使用了不同的模型。其實這種模型的靈感來自於各種操作系統在高級的基於事件處理機制方面的不斷髮展。而且這種靈感最終使模塊化、事件驅動、異步模式、單個線程處理以及非阻塞式架構成爲nginx代碼的基礎。

nginx 對多路複用技術和事件通知機制使用的非常多,而且爲每個進程分配特定的任務。多個連接是在一個被限制在一定數量或者單一的被稱作worker(s)的進程中通過高效的迴環(run-loop)機制來處理的。對於nginx的每個worker進程來說,每秒鐘可以處理幾千個請求和連接。

代碼結構

worker 線程的代碼涵蓋了nginx的核心功能模塊。nginx的核心則是負責維護迴環(run-loop)機制以及處理請求時在合適的階段來執行某些模塊的部分代碼;各個模塊則主要實現了表現層和應用層的功能,主要用來對網絡存儲的讀寫、傳輸內容、做對外功能的過濾以及請求服務端,這包括各種請求和代理功能開啓時將請求傳遞給上行的web服務器。

nginx 的總體模塊化架構允許開發者在不改動核心代碼的情況下來擴展nginx的web服務功能。nginx的模塊和通用劃分方式稍有不同,包括核心模塊、事件處理模塊、階段處理模塊、協議模塊、變量處理模塊、過濾器模塊、upstream模塊和負載均衡模塊。當時,nginx並不支持動態加載模塊,也就是說,模塊必須在構建nginx是和核心代碼一起編譯。但是支持可加載模塊和ABI已經納入之後的主版本計劃中了。更多關於不同模塊規則的細節將在“14.4小節”中講到。

爲了處理各種各樣關於接受、處理以及管理網絡內容和查詢網絡內容的請求動作,nginx採用了事件通知機制以及一些針對Linux、Solaris和基於 BSD操作系統的性能提升措施,比如kqueue,epoll和event ports(譯者注:操作系統IO模型)。這樣做的目的是儘可能多的給操作系統提示,爲了使出入站流量、磁盤操作、從套接字讀取內容或者向套接字寫入內容、超時設定等操作可以更快的獲得異步響應。也正使用不同的多路複用的方法和高級I/O操作在很大程度上優化了每一個運行在基於unix操作系統上的 nignx性能。

nginx的高層次架構總覽將在圖14.1中呈現。

圖14.1:圖解nginx架構

worker進程模型

就像之前提到的一樣,nginx不會爲每一個連接生成一個進程或者線程。相反,是由worker進程通過一個共享的監聽socket來接受新的請求,並且在每個worker進程內部通過執行一個高效迴環(run-loop)來使每個worker進程可以處理數千連接。對於nginx中的worker進程來說並沒有特殊的機制來種菜或者分配這些連接;這由操作系統內核機制來完成。在nginx啓動的時候,一定數量的socket被創建,接着work進程通過不斷的接受、讀寫這些socket來處理HTTP請求和響應。

其中,迴環(run-loop)是worker代碼中最複雜的部分。這個迴環包括全面的內部調用並且極度依賴異步任務處理思想,因此,模塊化、事件通知機制、廣泛的回調函數的使用和調整計數器都通過異步操作來實現。總的來說,就是儘可能的使用非阻塞模式。而niginx也只有在對worker進程來說磁盤存儲性能不足時纔會進入阻塞模式。

一方面因爲nginx不會爲每一個連接都分配一個處理進程或者線程,所以非常節省內存並且在絕大多數情況下效率非常高。nginx同樣在CPU的使用上也非常節省,這是因爲它不需要不斷的來對線程或者進程執行創建-銷燬模式。而nginx需要做的就是檢查網絡狀態、保存和初始化新的連接並將它們加到迴環 (run-loop)中、異步處理直到處理完成,在這個狀態連接時被釋放的而且被從迴環中移出。另外,由於niginx對系統調用的使用非常謹慎以及對支持藉口比如線程池以及內存的slab分配模式的準確實現使nginx在極限負載的情況下出色的做到了CPU的低使用率。

另一方面由於nginx爲產生多個worker進程來處理客戶端連接,所以在多核情況下擴展性很好。通常,多核cpu時爲每個核心分配只一個的worker 進程,這樣既可以充分利用多核架構又可以防止線程上下文切換和鎖競爭。而且不存在資源匱乏問題,因爲資源控制機制是被單獨隔離在每個worker進程中的。這種模型還可支持通過增加物理存儲設備來獲得更好的擴展性,促進磁盤利用率以及避免磁盤I/O阻塞。這使得在多個worker進程來共同處理負載時可以對服務器資源更有效的利用。

隨着使用的硬盤和cpu的加載方式的不同,nginx的worker進程數也應該跟着調整。這裏講談一些調整的基本規則,而系統管理員則要根據自己網站的負載情況嘗試好幾種配置。一般建議如下:

如果負載模式是CPU密集型的-如負責處理許多TCP/IP協議,SSL,或者壓縮-那麼nginx的的worker線程數必須和CPU核心數相同;

如果工作負荷很大程度上依賴於磁盤I/O-例如提供提供保存下來的內容或者數量非常大的代理工作-那麼worker線程數可以使CPU核心數的1.5到2倍;

然而一些工程師卻選擇根據單獨存儲單元的個數來決定worker工作線程的數量,即使這種方法只是在一定類型和結構的存儲設備上纔有效。

對於nginx的開發人員來說,在接下來的版本中需要解決的一個主要問題就是如果避免磁盤I/O的大面積阻塞。目前,如果沒有足夠的存儲性能來滿足某個特定的worker產生的磁盤操作,那麼這個worker可能在磁盤讀寫方面將一直被阻塞;針對這種情況,有許多機制和配置文件指令來緩和這個問題。最有效的方法就是將文件發送和異步IO結合來大幅提升磁盤性能。而一個niginx的構建安裝應建立在對nginx來說可用的內存大小、存儲架構基礎這些數據之上。

另一個問題是關於現行worker模型中對嵌入式腳本進行有限支持的問題。一方面,對於標準配置的nginx來說,只支持Perl腳本;對於這個問題的簡單解釋是:關鍵是嵌入式腳本會非正常阻塞任何操作和退出,而這兩種關鍵的操作的阻塞都會迅速導致worker線程被掛起,從而一次影響數千連接。 nignix開發者們已經計劃做更多工作使嵌入式腳本能更方便,更可靠以及更合適的被廣大應用程序使用。

nginx處理規則

nginx 會啓動多個進程在內存中,包括一個主進程(marster進程)和多個工作進程(worker進程),還有一些專用進程,特別是緩存加載進程和緩存管理進程。所有的這些進程在nginx1.x版本中都是單線程模式。所有的進程都主要通過共享內存來進行線程間通信。其中marster進程以root用戶身份運行,緩存加載進程和管理進程以及工作進程(worker進程)則以普通權限用戶身份運行。

主線程(master)的主要職責如下:

1.讀取並校驗配置文件

2.創建、綁定以及關閉socket

3.初始化、結束以及保持工作線程(worker)線程的數量與維持在所配置的數量上

4.不啓動服務的情況下重新配置

5.控制不間斷的二進制升級(開始新的二進制必要時做回滾)

6.重新打開日誌文件

7.編譯內嵌的Perl腳本

工作進程(worker)則主要用來接收、處理客戶端連接,提供反向代理和過濾功能以及做任何nginx可以做的事情。關於監控一個nginx實例的行爲,系統管理員還需要關注worker進程的情況,因爲worker進程真實的反應了一個web服務器的日常操作。

緩存加載進程主要負責檢查磁盤上的緩存以及緩存在nginx內存中的數據庫元數據。實際上,緩存加載器將nginx實例所需要的磁盤文件準備成特定的目錄結構來供nginx實例來使用。緩存加載器遍歷目錄,檢查緩存內容元數據,更新相關的共享內存,然後在任何東西被清理以及準備使用時都將起作用。

緩存管理器主要負責緩存的過期和校驗。它在nginx正常操作時將一直保存在內存中,在失敗的情況下會被主進程(master)重啓。

nginx緩存簡述

nginx 緩存是以文件系統中分層存儲的方式來實現的。緩存的key是可配置的,可以根據特定請求參數的不同取值來決定什麼東西可以放入緩存中。緩存的鍵(key) 和緩存元數據存儲在一個可以被緩存加載器、緩存管理器以及worker進程都能訪問的共享內存片段中。目前,沒有任何文件緩存在內存中,因爲緩存在內存中不如依賴使用操作系統對虛擬文件系統的優化機制。每個不同的響應的緩存都被放在不同的文件中,其中分層(等級和命名細節)由nginx配置命令來完成。當一個響應被寫入緩存目錄結構時,它的目錄和文件名都是通過對代理鏈接做md5哈希得到。

將內容放入緩存的流程如下:

當 nginx從上行服務器得到響應內容以後,首先將內容寫入到緩存目錄結構之外的一個臨時目錄裏;當nginx對請求處理完成以後它再重命名這個臨時文件並將重命名過的文件移到緩存目錄中。如果臨時目錄被代理到其他文件系統,那麼這個臨時文件在放入緩存文件後會被再複製一份到當前系統中,這樣,nginx就在同一個文件系統中既擁有這個請求的緩存又擁有臨時文件。而且如果已經明確不再需要緩存,那麼從緩存目錄結構中刪除文件也是非常安全的。同時,nginx 還有第三方擴展可以支持遠程管理緩存內容,後續工作中也計劃將該功能整合到nginx主版本中。


(未完,待續。。。)

1. 本文由程序員學架構翻譯,mathew同學校審

2. 本文譯自The Architecture of Open Source Applications

3. 轉載請務必註明本文出自程序員學架構(微信號:archleaner )



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章