【反向代理服務器】Nginx的基礎架構

在進入Nginx的結構分析之前,首先介紹一下一個良好的Web服務器需要考慮哪些因素。這樣更加有利於我們深入理解Nginx優秀的架構設計。

1 Web服務器設計中的關鍵約束

1.1 性能

1 網絡性能

是指在不同負載下,Web服務在網絡通信上的吞吐量。網絡性能受制於帶寬(指在特定的網絡連接上可以達到的最大吞吐量)。

2 單次請求的延遲性

針對一個用戶而言,指服務器初次接收到一個用戶請求直至返回響應之間持續的時間。

3 網絡效率

使用的網絡效率,常用的提高網絡效率的方法包括使用長連接、使用壓縮算法。

1.2 可伸縮性

是指架構可以通過添加組件來提升服務,或者允許組件之間具有交互功能。一般可以通過簡化組件、降低組件之間的耦合度、將服務分散到許多組件等方法來改善可伸縮性。

1.3 簡單性

是指組件的簡單程度。一般我們採用分離關注點原則來設計組件,對於整體結構來說通常使用通用性原則。

1.4 可修改性

簡單來講,可修改性就是在當前架構下對於系統功能做出修改的難易程度。

1 可進化性

表示在修改一個組件時對其他組件產生負面影響的程度。

2 可擴展性

表示將一個新的功能添加到系統中且不影響其他功能的能力。

3 可定製性

是指可以臨時性的重新規定一個組件或其他結構元素的特性,從而提供一種常規服務的能力。

4 可配置性

在Web服務部署後,通過對服務提供的配置文件進行修改來提供不同的功能。

5 可重用性

指的是一個應用中的功能組件在不被修改的情況下,可以在其他應用中重用程度。

1.5 可見性

在Web服務器中,可見性通常是指一些關鍵組件的運行情況可以被監控的程度,如服務中正在交互的網絡連接數、緩存的使用情況等。

1.6 可移植性

指服務可以跨平臺運行。

1.7 可靠性

可靠性可以看做是在服務出現部分故障時,一個架構容易受到系統層面故障影響的程度。

2 Nginx的架構設計

Nginx的設計格外重視以上7個關鍵點,Nginx採用了優秀的模塊化設計思想。

2.1 模塊化設計

Nginx底層有個基本的接口ngx_module_t,所有的模塊都遵循着這個接口的規範。ngx_module_t接口有一個type成員,它指明瞭Nginx允許在設計
模塊時定義模塊類型。Nginx最底層的模塊時配置類型模塊(即NGX_CONF_MODULE),它指導着所有模塊以配置項爲核心來提供功能。Nginx包括五大類型的模塊:核心模塊、配置模塊、事件模塊、HTTP模塊、mail模塊

2.2 事件驅動架構

所謂事件驅動架構是指由一些事件發生源來產生事件,由一個或多個事件收集器來收集、分發事件(收集、分發事件由Nginx的事件模塊完成),然後許多事件處理器會註冊自己感興趣的事件,同時會“消費”這些事件。

服務器 區別
傳統的Apache 使用進程或線程作爲事件消費者
Nginx 事件消費者只能是某個模塊,只有事件收集、分發器才能佔用進程資源

Nginx處理事件的簡單模型如下圖所示:圖中列出的5個不同的事件,在事件收集、分發者進程的一次處理過程中,這5個事件按照順序被收集後,將開始使用當前進程分發事件,從而調用相應的事件消費者模塊來處理事件。
在這裏插入圖片描述

2.3 請求的多階段異步處理

正因Nginx的事件驅動架構,所以才讓請求的多階段異步處理成爲現實。

請求的多階段異步處理就是把一個請求的處理過程按照事件的觸發方式劃分爲多個階段,每個階段都可以由事件收集、分發器來觸發。

2.4 管理進程、多工作進程設計

Nginx採用一個master管理進程、多個worker工作進程的設計方式,如下圖所示。
在這裏插入圖片描述
該設計的優點在於:多核系統的併發處理能力;多個worker工作進程間通過進程通信來實現負載均衡,也就是說一個請求到來時更容易被分配到負載較輕的worker工作進程中處理、管理進程會負責監控工作進程的狀態並負責管理其行爲。

2.5 內存池的設計

爲了避免出現內存碎片、減少向操作系統申請內存的次數,Nginx設計了簡單的內存池:把多次向系統申請內存的操作整合成一次,這大大減少了CPU資源的消耗,同時減少了內存碎片。

3 Nginx啓動時框架的處理流程

首先啓動流程見如下的流程圖:
在這裏插入圖片描述
接下來對關鍵步驟進行說明:
1、上圖第1步,Nginx是以配置文件作爲核心提供服務的,所以第1步最主要的就是確定配置文件nginx.conf的路徑。
2、上圖第2步,Nginx在不重啓服務升級時(也就是所謂的平滑升級),它會不重啓master進程而啓動新版本的Nginx程序。此時,舊版本的master進程會通過execve系統調用,先fork出子進程再調用exec來運行新程序,進而啓動新版本的master進程。
3、上圖第3步,調用所有核心模塊的create_conf方法,也就是說需要所有的核心模塊開始構造用於存儲配置項的結構體
4、上圖第4步,遍歷nginx.conf中的所有配置項並調用配置模塊提供的解析配置項方法。
5、上圖第5步,調用所有NGINX_CORE_MODULE核心模塊的init_conf方法,讓所有核心模塊在解析完配置項後可以做綜合性處理
6、上圖第9步,如果nginx.conf中配置爲單進程工作模式,這時將會調用ngx_single_process_cycle方法進入單進程工作模式。
7、上圖第10步,在單進程工作模式下,調用所有模塊的init_process方法,單進程工作模式的啓動工作至此全部完成,將進入正常的工作模式。
8、上圖第11~16步,進入master、worker工作模式。在啓動worker子進程、cache manage子進程、cache loader子進程後就開始進入工作狀態。

4 worker進程是如何工作的

首先列出Nginx中的信號及對應的全局標誌位變量的含義:

信號 對應進程中的全局標誌位變量 意義
QUIT ngx_quit 優雅的關閉進程
TERM或INT ngx_terminate 強制關閉進程
USR1 ngx_reopen 重新打開所有文件
WINCH ngx_debug_quit 暫無實際意義

另外還有ngx_exiting標誌位,僅由方法ngx_worker_process_cycle在退出時作爲標誌位使用。

worker進程工作流程如下:

  1. 首先檢查ngx_exiting標誌位,如果爲1則進程退出,否則開始檢查並分發、處理事件;
  2. 然後判斷ngx_terminate標誌位是否爲1,若是則強制結束進程,否則判斷ngx_quit位是否爲1,若是則優雅的關閉進程,同時關閉所有監聽句柄並設置ngx_exiting標誌,否則進入下一步;
  3. 其次,判斷ngx_reopen標誌位是否爲1,是的話重新打開所有文件,回到第一步繼續判斷ngx_exiting。

5 master進程是如何工作的

首先需要明確master進程是不需要處理網絡事件的,它無需負責業務的執行,只會通過管理worker子進程來實現重啓服務、平滑升級等功能。同樣的列出master進程中各種標誌位的含義:

信號 對應進程中的全局標誌位變量 意義
QUIT ngx_quit 優雅的關閉整個服務
TERM或INT ngx_terminate 強制關閉整個服務
USR1 ngx_reopen 重新打開服務中的所有文件
WINCH ngx_noaccept 所有子進程不再接受處理新的連接,相對於對所有子進程發送QUIT信號量
USR2 ngx_change_binary 平滑升級到新版本的Nginx程序
HUP ngx_reconfigure 重讀配置文件並使服務對新配置項生效
CHLD ngx_reap 有子進程意外結束,這時需要監控所有的子進程,也就是ngx_reap_children方法所負責的工作

那麼master進程如何啓動一個子進程呢?

很簡單,通過fork系統調用即可實現。然後會從ngx_processes數組中選擇一個還未使用的ngx_process_t元素存儲這個子進程的相關信息。

既然ngx_processes數組保存了所有子進程的信息,那麼這些進程的狀態是如何改變的呢?

依靠信號!例如當若干個子進程意外退出時,master父進程會接收到Linux內核發來的CHLD信號,由於Nginx服務器的ngx_signal_handler方法做出處理:將sig_reap標誌位置爲1,調用ngx_process_get_status方法修改ngx_processes數組中所有子進程的狀態(通過waitpid系統調用得到意外結束的子進程的ID,然後遍歷ngx_processes數組找到該子進程ID對應的ngx_process_t結構體,將其exited標誌位置1)。

master進程的工作流程如下:

  1. 首先判斷ngx_reap標誌位。如果爲1則表示監控所有的子進程:即通過ngx_reap_children方法遍歷ngx_processes數組,檢查每個子進程的狀態;如果爲0則進入下一步;
  2. 然後判斷live標誌位。如果live標誌位爲0(所有子進程已經退出)、ngx_terminate標誌位爲1或者ngx_quit標誌位爲1時,則開始退出master進程,同時會刪除存儲進程號的pid文件、關閉進程中打開的監聽接口、銷燬內存池;否則進入下一步;
  3. 其次判斷ngx_terminate標誌位。如果爲1則向所有子進程發送信號TERM,強制子進程退出,然後跳到第1步掛起進程等待再次激活信號;否則執行下一步;
  4. 判斷ngx_quit標誌位。如果爲1,則向所有子進程發送QUIT信號優雅的退出服務,同時關閉所有監聽的接口,同樣的跳到第1步掛起進程等待信號激活。否則執行下一步。
  5. 判斷ngx_reconfigure標誌位。如果爲1則表示需要重新讀取配置文件。注意Nginx不會讓原先的worker等子進程重新讀取,它的策略是重新初始化結構體ngx_cycle_t,讓它來讀取新的配置文件並拉起新的worker子進程,銷燬舊有進程。如果爲0則進入下一步;
  6. 檢查ngx_restart標誌位,如果爲1則拉起新的worker子進程,同時將ngx_restart置0,然後根據緩存模塊情況決策是啓動cache manager還是cache loader進程,並將live標誌位置1。否則進入下一步。
  7. 檢查ngx_reopen標誌位。如果爲1則重新打開所有文件同時將該位置0,並向所有子進程發送USR1信號,通知它們重新打開所有文件。否則進入下一步;
  8. 接着檢查ngx_change_binary標誌位。如果爲1則表示需要平滑升級Nginx,這時將調用ngx_exec_new_binary方法用新的子進程啓動新版本的Nginx程序,同時將ngx_change_binary標誌位置0;
  9. 最後需要檢查的是ngx_noaccept標誌位。如果爲1則向所有的子進程發送QUIT信號,要求它們優雅的關閉服務同時將ngx_noaccept位置0,ngx_noaccepting置1(表示停止接收新的連接)、如果爲0則繼續第1步進行下一個循環。

6 小結

本機側重介紹了Nginx的基礎架構,介紹了Nginx框架如何啓動、初始化、加載各Nginx模塊的代碼,以及worker進程和master進程如何在工作中循環運行。事實上這些機制都是基於Nginx的核心數據結構——ngx_cycle_t。

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