1. 程式人生 > >Nginx學習筆記(十一):Nginx的架構設計

Nginx學習筆記(十一):Nginx的架構設計

前言

       開始學習書的第三部分,深入Nginx了,也越發在Nginx身上看到了之前實習公司所開發系統的影子,感謝過去的這段經歷。另外,越來越發現CSDN的是個好地方,看別人部落格的時候總能讓自己也熱血沸騰,在他們身上看到了自己缺少並且又難能可貴的品質,向他們學習。

Nginx架構設計

        這裡先說下Nginx設計時重視的幾個關鍵點:
  • 效能:包括網路效能,單詞請求的延遲性,網路效率;(名詞就不解釋了~)
  • 可伸縮性:可通過新增元件來提升服務,或者允許元件之間具有互動功能;
  • 簡單性:元件的簡單程度,便於理解和實現;
  • 可修改性:包括可進化性,可擴充套件性,可定製性,可配置性,可重用性;
  • 可見性:可監控關鍵元件的執行情況;
  • 可移植性:跨平臺執行;
  • 可靠性:在服務出現故障時,一個架構容易受到系統層面故障影響的程度;

模組化設計

       高度模組化的設計是Nginx的架構基礎。在Nginx中,除了少量的核心程式碼,其他一切皆為模組。這一點在之前的模組開發的實踐中已經感受到了。        所有模組都遵循ngx_module_t的介面設計,並且所有模組間都是分層次、分類別的。        官方Nginx共有五大型別模組:核心模組、配置模組、事件模組、HTTP模組、mail模組        它們都各具備相同的ngx_module_t介面,但在請求處理流程中的層次不相同。
        Nginx常用模組間的關係如下圖:              
       配置模組與核心模組由Nginx的框架程式碼定義。其中,配置模組是所有模組的基礎,它實現最基本的解析功能(即解析nginx.conf檔案)。然後,Nginx框架會呼叫核心模組,其他三種模組則不會與框架產生直接關係。如上圖示,事件模組、HTTP模組、mail模組在核心模組中分別都有一個自己的“代言人”,並在同類模組中有一個作為核心業務與管理功能的模組。比如,事件模組由其代言人ngx_events_module核心模組定義,但所有事件模組載入又由ngx_event_core_module負責。        在上圖中,配置模組與核心模組都是與Nginx框架密切相關的,是其他模組的基礎。而事件模組又是HTTP模組和mail模組的接觸。HTTP模組與mail模組更關注於應用層面,地位相似。

事件驅動架構

       事件驅動架構,簡單來生活,就是由一些時間發生源產生事件,由一個或多個事件收集器來收集、分發時間,然後許多時間處理器會註冊自己感興趣的事件,同時會“消費”這些事件。         Nginx採用完全的事件驅動架構來處理業務,與傳統的Web伺服器不同。區別用圖示:
  • 傳統Web伺服器處理事件模型
             
  • Nginx處理事件模型
                      兩者最重要差別:傳統Web伺服器是每個事件消費者獨佔一個程序資源,Nginx的事件消費者只是被事件分發程序短期呼叫。        這樣的設計使得網路效能、使用者感知的請求延時得到提升。但同時帶來的一個弊端:每個時間消費者不能有阻塞行為,否則會由於長時間佔用分發者程序而導致其他事件得不到響應。更進一步說,就是不能讓分發者程序轉為休眠或等待狀態。

請求的多階段非同步處理

       請求的多階段非同步處理只能基於事件驅動架構,意思就是把一個請求的處理過程按照事件的觸發方式劃分為多個階段,每個階段都可以由事件收集、分發器來觸發。 非同步處理和多階段:        非同步處理與多階段的劃分是相輔相成的。可以這樣理解,當一個事件被分發到事件消費者中進行處理時,事件消費者處理完當前事件只是相當於處理完一個請求的某個階段,然後再等待核心通知,進而再次呼叫消費者處理... 請求的多階段非同步處理優勢:        這種設計可以使得每個程序都能全力進行,不會或者儘量少地出現程序休眠狀態。
  1. 一旦出現程序休眠,必然減少併發處理事件的數目,進而會降低網路效能同時增加請求處理時間的平均時延;
  2. 程序如果休眠導致網路效能無法滿足業務需求,則系統只能通過增加程序來解決。這時,程序數目過多又將會導致作業系統的額外操作:程序間切換。頻繁地進行程序切換會嚴重消耗CPU資源;
  3. 休眠程序會使得程序佔用記憶體而不得釋放,這將導致系統可用記憶體下降,從而影響系統能夠處理的最大併發連線數;
如何劃分請求的階段?
  • 將阻塞程序的方法按照相關的觸發事件分為兩個階段
       一個本身能夠導致程序休眠的方法或系統呼叫,一般都能夠分解為多個更小的方法或者系統呼叫。大部分情況下,可以這樣劃分:         1,阻塞方法改為非阻塞方法,此為第一階段;         2,處理非阻塞方法返回後的結果,此為第二階段;         舉例:                                      
  • 將阻塞方法呼叫按照時間分解為多個階段的方法呼叫
            如果按照上一種方式試圖劃分時,發現找出的觸發事件不能被事件收集、分發器處理,這時,只能按照執行事件拆分。         舉例:         讀取10M檔案,但檔案未必在磁碟中連續,此時就需要多次驅動硬碟定址。而在定址過程中,就可能會導致程序休眠或等待。此時,我們希望能想上述方法劃分階段。但是,比如在Linux上,讀取磁碟檔案必須要通過核心非同步I/O,但Linux上的Nginx的事件模組在沒開啟非同步I/O時不支援這種做法,所以,這樣我們就只能分寫讀取檔案呼叫:把10M均分成100份,每次讀取10K大小。於是,每階段讀10K,這樣就將事件拆分。
  • 在“無所事事”且必須等待系統的響應,從而導致程序空轉時,使用定時器劃分階段
       程式碼段本身並沒有阻塞方法,但實際上是阻塞程序。比如:進行某個無阻塞的系統呼叫後,必須通過持續的檢查標誌位來確定是否繼續向下執行,當標誌位沒有獲得滿足時就無限迴圈檢查。這種情況下,需要用到定時器控制,如果超時,標誌位仍不滿足,則開始下一階段事件。
  • 如果阻塞方法完全無法繼續劃分,則必須使用獨立的程序執行這個阻塞方法
       當某個方法呼叫時可能導致程序休眠,或者佔用程序時間過長,可是又無法將該方法分解為不阻塞的方法,這種情況通常與事件驅動架構本身相違背。這時,必須通過產生新的程序或者指定某個非事件分發者程序來執行阻塞方法,並在阻塞方法執行完畢時想事件收集、分發者程序傳送事件通知繼續執行。這裡至少拆分兩階段:阻塞方法執行前階段,阻塞方法執行後階段,而阻塞方法的執行要使用單獨的程序去排程,並在方法返回後傳送事件通知。        一般出現這種設計,需要審視事件消費者是否合理。。(這一段比較抽象,書中也沒有舉例,所以這句話不太能理解)

管理程序、多工作程序設計

        Nginx採用一個master管理程序、多個worker工作程序的設計方式。如下圖:                
        這種設計帶來的優勢:
  • 1)利用多核系統的併發處理能力。多個Work程序可以佔用不同的CPU核心來工作,這樣提供網路效能,降低了請求時延。
  • 2)負載均衡。一個請求到來時被分配到負載較輕的Worker程序中去處理。
  • 3)管理程序負責監控工作程序狀態,並負責管理其行為。Master程序基本不佔用多少系統資源,它只負責啟動、停止、監控或使用其他行為來控制Worker程序。

平臺無關程式碼實現

       Nginx重新封裝了日誌、各種基本資料結構(之前的筆記都有記錄)、常用演算法等。        在核心程式碼都使用了與作業系統無關的程式碼實現,在與作業系統相關的系統呼叫上則分別針對各個作業系統都有各自獨立的實現,這造就了Nginx的可移植性。

記憶體池設計

       以前第一次聽說的時候總覺得這是個很高階的東西,我現在突然明白了為什麼要有記憶體池設計了。很簡單,就是把多次向系統申請記憶體的操作整合成一次,從而減少向作業系統申請記憶體的次數,以及避免出現記憶體碎片。        記憶體池不負責回收記憶體池中已經分配出的記憶體。        然後在程式碼結束的最後,再統一釋放,即統一申請,統一釋放。這樣子,方便太多。。

使用統一管道過濾器模式的HTTP過濾模組

       其實就是過濾模組,這個完全可以看之前筆記《HTTP過濾模組》。每一個過濾模組的輸入輸出都是統一的介面,通過一種連結串列式的結構串聯在一起,但每個模組又各自獨立。        整個過程就是一個過濾模組處理完輸入的資料,然後通過統一介面傳給下一個模組。        這種統一管理過濾器的設計方式優點:
  1. 整個HTTP過濾系統的輸入/輸出簡化為一個個過濾模組的簡單組合;
  2. 提供很好的可重用性,可將任意兩個HTTP過濾模組連線在一起;
  3. 容易維護與增強,在可驗證性與可測試性上更加友好,可以靈活變動過濾模組流水線來驗證功能;
  4. 完全支援併發執行;

其他一些使用者模組

       Nginx還有許多特定的使用者模組。這些模組用來改善或者提高Nginx設計上的關鍵點。        比如, ngx_http_stub_status_module模組提供對所有HTTP連線狀態的監控,提高Nginx的系統可見性。

總結

       Nginx的架構設計這一節的筆記,零散的用了兩三天時間。先是自己過了一遍,筆記的過程中又詳讀。有些舉例又通過畫圖重複理解一遍。所以,對Nginx的架構設計這一節基本掌握。總的來說兩個重點吧:多階段和非同步處理、管理程序和多工作程序。

主要參考

《深入理解Nginx》