nginx介紹(二) 架構篇

flynike發表於2021-09-09

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主版本中。

(未完,待續。。。)


來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/2618/viewspace-2824550/,如需轉載,請註明出處,否則將追究法律責任。

相關文章