震撼!全網第一張原始碼分析全景圖揭祕Nginx
不管是C/C++技術棧,還是PHP,Java技術棧,從事後端開發的朋友對nginx一定不會陌生。
想要深入學習nginx,閱讀原始碼一定是非常重要的一環,但nginx原始碼量畢竟還是不算少,一不小心就容易陷入某個細節,迷失在茫茫碼海之中。
如果有一張地圖,讓我們開啟上帝視角,總覽全域性,幫助我們快速學習整體框架結構,又能不至於迷失其中那就再好不過了!
看到這篇文章的你有福了,筆者花了不少時間,把這件事給做了,先來看個全貌(限於平臺圖片尺寸設定,這裡只能看個大概,想獲取高清大圖請看文末):
下面選取一些關鍵部分來一窺神祕的nginx。
主程序啟動
nginx主程序啟動後,進行一系列的初始化,包括但不限於:
- 命令列引數解析
- 時間初始化
- 日誌初始化
- ssl初始化
- 作業系統相關初始化
- 一致性hash表初始化
- 模組編號處理
核心初始化
另外一個最重要的初始化由ngx_init_cycle()
函式完成,該函式圍繞nginx中非常核心的一個全域性資料結構ngx_cycle_t
展開。
該函式完成了幾個核心初始化:
- 配置檔案解析
- 建立並監聽socket
- 初始化nginx各模組
nginx核心模組群
nginx是一個模組化設計的軟體,優秀的架構設計使得nginx可以擴充套件非常多的模組。
要一一描繪出這些模組顯得有些雜亂和工作量巨大,僅選取一些關鍵核心模組進行了展示:
每個模組有一個支援的命令解析列表,在初始化過程中,主程序將會遍歷所有模組的命令列表,進行配置檔案中的命令解析,如經常用的ngx_http_proxy_module
ngx_http_core_module
模組:
main函式的最後,根據是否啟用多程序模型,分別進入多程序版本的ngx_master_process_cycle
和單程序版本的ngx_single_process_cycle()
。
以常見的多程序版本為例,進入該函式後,首先設定程序名稱為:"master process",隨後啟動各工作子程序。
啟動子程序
經過幾層封裝,最終通過fork
啟動多個子程序:
除了工作子程序,還啟動了快取管理程序。
之後主程序進入工作迴圈,週期性更新時間並檢查各全域性標記,根據不同情況給子程序傳送不同訊號。
子程序工作迴圈
子程序啟動後,進入ngx_worker_process_cycle
接著進入工作迴圈函式ngx_process_events_and_timers
,在該函式中主要負責:
- 競爭互斥鎖,拿到鎖的程序才能執行accept接受新的連線,以此在多程序之間解決驚群效應
- 通過epoll非同步IO模型處理網路IO事件,包括新的連線事件和已建立連線發生的讀寫事件
- 處理定時器佇列中到期的定時器事件,定時器通過紅黑樹的方式儲存
HTTP請求預處理
當連線有資料產生時,工作執行緒讀取socket中到來的資料,並根據HTTP協議格式進行解析,最終封裝成ngx_request_t請求物件,提交處理。
HTTP請求處理的11個階段
在nginx中各HTTP模組是以掛載的形式串接而成,以流水線工作模式進行HTTP請求的處理,nginx將一個HTTP請求的處理劃分為11個階段。
typedef enum {
NGX_HTTP_POST_READ_PHASE = 0,
NGX_HTTP_SERVER_REWRITE_PHASE,
NGX_HTTP_FIND_CONFIG_PHASE,
NGX_HTTP_REWRITE_PHASE,
NGX_HTTP_POST_REWRITE_PHASE,
NGX_HTTP_PREACCESS_PHASE,
NGX_HTTP_ACCESS_PHASE,
NGX_HTTP_POST_ACCESS_PHASE,
NGX_HTTP_PRECONTENT_PHASE,
NGX_HTTP_CONTENT_PHASE,
NGX_HTTP_LOG_PHASE
} ngx_http_phases;
每階段(部分階段保留,不允許掛載)允許多個模組掛載,一個模組也可以掛載到多個階段。因此,初次完成掛載的儲存結構是一個二維陣列的形式。
不過在初始化過程中,ngx_http_init_phase_handlers
函式將該二維陣列轉換成了一維陣列。下圖是nginx中各模組掛載情況:
全景圖
最後,再來看一看全貌:
總結
nginx不僅是一款優秀的高效能web伺服器,對於C/C++技術棧的同學來說,還是一個很好的學習物件,其良好的架構設計,優美的程式碼風格和經典的程式設計技法無一不值得細細品來。
不過限於筆者水平和時間有限,雖然號稱全景圖,但依然無法覆蓋到nginx的方方面面,歡迎讀者朋友留言交流,讓此圖日漸完善,謝謝大家。
獲取完整高清大圖,可在公眾號裡回覆“nginx”自動獲取。
往期熱門回顧
一個Java物件的回憶錄:那些被鎖住的日子
一個整數+1引發的災難
一網打盡!每個程式猿都該瞭解的黑客技術大彙總
看過無數Java GC文章,這5個問題你也未必知道!
Python一鍵轉Jar包,Java呼叫Python新姿勢!
一個Java物件的回憶錄:垃圾回收
核心地址空間大冒險3:許可權管理
誰動了你的HTTPS流量?
路由器裡的廣告祕密
核心地址空間大冒險2:中斷與異常
DDoS攻擊:無限戰爭
一條SQL注入引出的驚天大案
核心地址空間大冒險:系統呼叫
一個HTTP資料包的奇幻之旅
一個DNS資料包的驚險之旅
我是一個流氓軟體執行緒
掃碼關注,更多精彩