處理並行連線的傳統的基於進程或執行緒的模型涉及使用單獨的進程或執行緒處理每個連線,並阻止網路或輸入/輸出操作。 根據應用,在記憶體和CPU消耗方面可能非常低效。 產生一個單獨的進程或執行緒需要準備一個新的執行時環境,包括分配堆和堆疊記憶體,以及建立新的執行上下文。 額外的CPU時間也用於建立這些專案,這可能會導致由於執行緒在過多的上下文切換上的轉機而導致效能下降。 所有這些並行症都表現在較老的Web伺服器架構(如Apache)中。 這是提供豐富的一般應用功能和優化的伺服器資源使用之間的一個折衷。
從一開始nginx就是一個專門的工具,可以實現更高效能,更密集和經濟地使用伺服器資源,同時實現網站的動態發展,所以它採用了不同的模式。 它實際上受到各種作業系統中高階事件機制的不斷發展的啟發。發展結果變成是一個模組化的,事件驅動的,非同步的,單執行緒的非阻塞架構的nginx程式碼基礎。
nginx大量使用複用和事件通知,並專門用於分離進程的特定任務。 連線在有限數量的單執行緒進程稱為工作(worker
)的高效執行迴圈中處理。 在每個工作(worker
)中,nginx可以處理每秒數千個並行連線和請求。
nginx工作(worker
)碼包括核心和功能模組。 nginx的核心是負責維護嚴格的執行迴圈,並在請求處理的每個階段執行模組程式碼的適當部分。 模組構成了大部分的演示和應用層功能。 模組讀取和寫入網路和儲存,轉換內容,執行出站過濾,應用伺服器端包含操作,並在代理啟動時將請求傳遞給上游伺服器。
nginx的模組化架構通常允許開發人員擴充套件一組Web伺服器功能,而無需修改nginx核心。 nginx模組略有不同,即核心模組,事件模組,階段處理程式,協定,可變處理程式,過濾器,上游和負載平衡器。nginx不支援動態載入的模組; 即在構建階段將模組與核心一起編譯。
在處理與接受,處理和管理網路連線和內容檢索相關的各種操作時,nginx在基於Linux,Solaris和BSD的作業系統中使用事件通知機制和一些磁碟I/O效能增強,如:kqueue,epoll, 和事件埠。 目標是為作業系統提供盡可能多的提示,以便及時獲取入站和出站流量,磁碟操作,讀取或寫入通訊端,超時等非同步反饋。 對於每個基於Unix的nginx執行的作業系統,大量優化了複用和高階I/O操作的不同方法的使用。
nginx架構的高階概述如下圖所示 -
如前所述,nginx不會為每個連線生成一個進程或執行緒。 相反,工作(worker
)進程接受來自共用「listen」通訊端的新請求,並在每個工作(worker
)內執行高效的執行迴圈,以處理每個工作(worker
)中的數千個連線。 沒有專門的仲裁或分配與nginx工作(worker
)的聯絡; 這個工作(worker
)是由作業系統核心機制完成的。 啟動後,將建立一組初始偵聽通訊端。 然後,工作(worker
)在處理HTTP請求和響應時不斷接受,讀取和寫入通訊端。
執行迴圈是nginx工作(worker
)程式碼中最複雜的部分。 它包括全面的內部呼叫,並且在很大程度上依賴非同步任務處理的想法。 非同步操作通過模組化,事件通知,廣泛使用回撥函式和微調定時器來實現。 總體而言,關鍵原則是盡可能不阻塞。 nginx仍然可以阻塞的唯一情況是工作(worker
)進程沒有足夠的磁碟儲存。
由於nginx不會連線一個進程或執行緒,所以在絕大多數情況下,記憶體使用非常保守,非常有效。 nginx也節省CPU週期,因為進程或執行緒沒有持續的建立 - 銷毀模式。 nginx的作用是檢查網路和儲存的狀態,初始化新連線,將其新增到執行迴圈中,並非同步處理直到完成,此時連線被重新分配並從執行迴圈中刪除。 結合仔細使用系統呼叫(syscall
)和精確實現支援介面(如pool
和slab
記憶體分配器),nginx通常可以在極端工作負載下實現中到低的CPU使用。
在一些磁碟使用和CPU負載模式,應調整nginx工作(worker
)的數量。 在這裡說一點基礎規則:系統管理員應該為其工作負載嘗試幾個組態。 一般建議可能如下:如果負載模式是CPU密集型的,例如,處理大量TCP/IP,執行SSL或壓縮,則nginx工作(worker
)的數量應與CPU核心數量相匹配; 如果負載大多是磁碟I/O系結,例如,從儲存或重代理服務不同的內容集合 - 工作(worker
)的數量可能是核心數量的一到兩倍。有些工程師會根據個人儲存單元的數量選擇工作(worker
)的數量,但這種方法的效率取決於磁碟儲存的型別和組態。
nginx的開發人員將在即將推出的版本中解決的一個主要問題是如何避免磁碟I/O上的大多數阻塞。 目前,如果沒有足夠的儲存效能來提供特定工作(worker
)生成的磁碟操作,該工作(worker
)可能仍然阻止從磁碟讀取/寫入。 存在許多機制和組態檔案指令來減輕此類磁碟I/O阻塞情況。要注意的是,諸如:sendfile和AIO之類的選項的組合通常會為磁碟效能帶來很大的餘量。 應該根據資料集,可用於nginx的記憶體量和底層儲存架構來規劃安裝一個nginx伺服器。
現有工作(worker
)模式的另一個問題是與嵌入式指令碼的有限支援有關。 一個使用標準的nginx分發,只支援嵌入Perl指令碼。一個簡單的解釋:關鍵問題是嵌入式指令碼阻止任何操作或意外退出的可能性。 這兩種型別的行為將立即導致工作(worker
)掛起的情況,同時影響到數千個連線。 更多的工作(worker
)計劃是使nginx的嵌入式指令碼更簡單,更可靠,適用於更廣泛的應用。
nginx在記憶體中執行多個進程; 有一個主進程和幾個工作(worker
)進程。 還有一些特殊用途的過程,特別是快取載入器和快取管理器。 所有進程都是單執行緒版本為1.x
的nginx。 所有進程主要使用共用記憶體機制進行進程間通訊。主進程作為root
使用者執行。 快取載入器,快取管理器和工作(worker
)則以無許可權使用者執行。
主程式負責以下任務:
worker
)進程數工作(worker
)進程接受,處理和處理來自用戶端的連線,提供反向代理和過濾功能,並執行幾乎所有其他的nginx能力。 關於監視nginx範例的行為,系統管理員應該關注工作(worker
)進程,因為它們是反映Web伺服器實際日常操作的過程。
快取載入器進程負責檢查磁碟快取專案,並使用快取後設資料填充nginx的記憶體資料庫。 本質上,快取載入器準備nginx範例來處理已經儲存在磁碟上的特定分配的目錄結構中的檔案。 它遍歷目錄,檢查快取內容後設資料,更新共用記憶體中的相關條目,然後在所有內容清潔並準備使用時退出。
快取管理器主要負責快取到期和無效。 在正常的nginx操作期間它保持在記憶體中,並且在失敗的情況下由主進程重新啟動。
在nginx中的快取以檔案系統上的分層資料儲存的形式實現。 快取金鑰是可組態的,並且可以使用不同的請求特定引數來控制進入快取的內容。 快取金鑰和快取後設資料儲存在共用儲存器段中,快取記憶體載入器,快取管理器和工作(worker
)可以存取它們。 目前,除了作業系統的虛擬檔案系統機制暗示的優化之外,沒有任何記憶體中的檔案快取。 每個快取的響應都放在檔案系統上的不同檔案中。 層次結構(級別和命名細節)通過nginx組態指令進行控制。 當響應寫入快取目錄結構時,檔案的路徑和名稱將從代理URL的MD5雜湊匯出。
將內容放置在快取中的過程如下:當nginx從上游伺服器讀取響應時,內容首先寫入快取目錄結構之外的臨時檔案。 當nginx完成處理請求時,它重新命名臨時檔案並將其移動到快取目錄。 如果用於代理的臨時檔案目錄位於另一個檔案系統上,則該檔案將被複製,因此建議將臨時檔案目錄和快取目錄儲存在同一檔案系統上。 當需要顯式清除快取目錄結構時,從檔案中刪除檔案也是非常安全的。 nginx有第三方擴充套件,可以遠端控制快取的內容,還有更多的工作計劃將此功能整合到主分發中。