最近在讀 Nginx 相關的書籍,做一下讀書筆記。
Nginx 作為業界知名的高性能伺服器,被廣泛的應用。它的高性能正是由于其優秀的架構設計,其架構主要包括這幾點:子產品化設計、事件驅動架構、請求的多階段異步處理、管理程序與多工作程序設計、記憶體池的設計,以下内容依次進行說明。
高度子產品化的設計是 Nginx 的架構基礎。在 Nginx 中,除了少量的核心代碼,其他一切皆為子產品。
所有子產品間是分層次、分類别的,Nginx 官方共有五大類型的子產品:核心子產品、配置子產品、事件子產品、HTTP 子產品、mail 子產品。它們之間的關系如下:
在這 5 種子產品中,配置子產品和核心子產品是與 Nginx 架構密切相關的。而事件子產品則是 HTTP 子產品和 mail 子產品的基礎。HTTP 子產品和 mail 子產品的 “地位” 類似,它們都是更關注于應用層面。
事件驅動架構,簡單的說就是由一些事件發生源來産生事件,由事件收集器來收集、分發事件,然後由事件處理器來處理這些事件(事件處理器需要先在事件收集器裡注冊自己想處理的事件)。
對于 Nginx 伺服器而言,一般由網卡、磁盤産生事件,Nginx 中的事件子產品将負責事件的收集、分發操作;而所有的子產品都可能是事件消費者,它們首先需要向事件子產品注冊感興趣的事件類型,這樣,在有事件産生時,事件子產品會把事件分發到相應的子產品中進行處理。
對于傳統 web 伺服器(如 Apache)而言,采用的所謂事件驅動往往局限在 TCP 連接配接建立、關閉事件上,一個連接配接建立以後,在其關閉之前的所有操作都不再是事件驅動,這時會退化成按順序執行每個操作的批處理模式,這樣每個請求在連接配接建立後都将始終占用着系統資源,直到關閉才會釋放資源。這種請求占用着伺服器資源等待處理的模式會造成伺服器資源極大的浪費。如下圖所示,傳統 web 伺服器往往把一個程序或線程作為時間消費者,當一個請求産生的事件被該程序處理時,直到這個請求處理結束時,程序資源都将被這一請求所占用。比較典型的例子如 Apache 同步阻塞的多程序模式就是這樣的。
傳統 web 伺服器處理事件的簡單模型(矩形代表程序):
Nginx 采用事件驅動架構處理業務的方式與傳統的 web 伺服器是不同的。它不使用程序或者線程來作為事件消費者,所謂的事件消費者隻能是某個子產品。隻有事件收集、分發器才有資格占用程序資源,它們會在分發某個事件時調用事件消費子產品使用目前占用的程序資源,如下圖所示,該圖中列出了 5 個不同的事件,在事件收集、分發者程序的一次處理過程中,這 5 個事件按照順序被收集後,将開始使用目前程序分發事件,進而調用相應的事件消費者來處理事件。當然,這種分發、調用也是有序的。
Nginx 處理事件的簡單模型:
由上圖可以看出,處理請求事件時,Nginx 的事件消費者隻是被事件分發者程序短期調用而已,這種設計使得網絡性能、使用者感覺的請求時延都得到了提升,每個使用者的請求所産生的事件會及時響應,整個伺服器的網絡吞吐量都會由于事件的及時響應而增大。當然,這也帶來一定的要求,即每個事件消費者都不能有阻塞行為,否則将會由于長時間占用事件分發者程序而導緻其他事件得不到及時響應,Nginx 的非阻塞特性就是由于它的子產品都是滿足這個要求的。
多階段異步處理請求與事件驅動架構是密切相關的,也就是說,請求的多階段異步處理隻能基于事件驅動架構實作。多階段異步處理就是把一個請求的處理過程按照事件的觸發方式劃分為多個階段,每個階段都可以由事件收集、分發器來觸發。
處理擷取靜态檔案的 HTTP 請求時切分的階段及各階段的觸發事件如下所示:
這個例子中,該請求大緻分為 7 個階段,這些階段是可以重複發生的,是以,一個下載下傳靜态資源請求可能會由于請求資料過大,網速不穩定等因素而被分解為成百上千個上圖所列出的階段。
異步處理和多階段是相輔相成的,隻有把請求分為多個階段,才有所謂的異步處理。當一個時間被分發到事件消費者中進行處理時,事件消費者處理完這個事件隻相當于處理完 1 個請求的階段。什麼時候可以處理下一個階段呢?這隻能等待核心的通知,即當下一次事件出現時,epoll 等事件分發器将會擷取到通知,然後去調用事件消費者進行處理。
Nginx 在啟動後,會有一個 master 程序和多個 worker 程序。master 程序主要用來管理 worker 程序,包括接收來自外界的信号,向各 worker 程序發送信号,監控 worker 程序的運作狀态以及啟動 worker 程序。 worker 程序是用來處理來自用戶端的請求事件。多個 worker 程序之間是對等的,它們同等競争來自用戶端的請求,各程序互相獨立,一個請求隻能在一個 worker 程序中處理。worker 程序的個數是可以設定的,一般會設定與機器 CPU 核數一緻,這裡面的原因與事件處理模型有關。Nginx 的程序模型,可由下圖來表示:
在伺服器上檢視 Nginx 程序:
這種設計帶來以下優點:
1) 利用多核系統的并發處理能力
現代作業系統已經支援多核 CPU 架構,這使得多個程序可以分别占用不同的 CPU 核心來工作。Nginx 中所有的 worker 工作程序都是完全平等的。這提高了網絡性能、降低了請求的時延。
2) 負載均衡
多個 worker 工作程序通過程序間通信來實作負載均衡,即一個請求到來時更容易被配置設定到負載較輕的 worker 工作程序中處理。這也在一定程度上提高了網絡性能、降低了請求的時延。
3) 管理程序會負責監控工作程序的狀态,并負責管理其行為
管理程序不會占用多少系統資源,它隻是用來啟動、停止、監控或使用其他行為來控制工作程序。首先,這提高了系統的可靠性,當 worker 程序出現問題時,管理程序可以啟動新的工作程序來避免系統性能的下降。其次,管理程序支援 Nginx 服務運作中的程式更新、配置項修改等操作,這種設計使得動态可擴充性、動态定制性較容易實作。
為了避免出現記憶體碎片,減少向作業系統申請記憶體的次數、降低各個子產品的開發複雜度,Nginx 設計了簡單的記憶體池,它的作用主要是把多次向系統申請記憶體的操作整合成一次,這大大減少了 CPU 資源的消耗,同時減少了記憶體碎片。
是以,通常每一個請求都有一個簡易的獨立記憶體池(如每個 TCP 連接配接都配置設定了一個記憶體池),而在請求結束時則會銷毀整個記憶體池,把曾經配置設定的記憶體一次性歸還給作業系統。這種設計大大提高了子產品開發的簡單些,因為在子產品申請記憶體後不用關心它的釋放問題;而且因為配置設定記憶體次數的減少使得請求執行的時延得到了降低。同時,通過減少記憶體碎片,提高了記憶體的有效使用率和系統可處理的并發連接配接數,進而增強了網絡性能。
————————————————
原文作者:JeffreyC
轉自連結:https://learnku.com/articles/24539