前言
Nginx
是一個 免費的,開源的,高效能 的 HTTP
伺服器和 反向代理,以及 IMAP
/ POP3
代理伺服器。 Nginx
以其高效能,穩定性,豐富的功能,簡單的配置和低資源消耗而聞名。Nginx
是一個 Web
伺服器,也可以用作 反向代理,負載均衡器 和 HTTP
快取。
很多高知名度的網站都使用 Nginx
,如:Netflix
,GitHub
,SoundCloud
,MaxCDN
等。
正文
1. Nginx的整體架構
1.1. 主程式
Nginx
啟動時,會生成兩種型別的 程式*,一個是 主程式(master
),一個(windows
版本的目前只有一個)或 多個工作程式(worker
)。主程式 並不處理網路請求,主要負責 排程工作程式,也就是圖示的 3
項:載入配置、啟動工作程式 及 非停升級。所以,Nginx
啟動以後,檢視作業系統的程式列表,我們就能看到 至少有兩個 Nginx
程式。
1.2. 工作程式
伺服器實際 處理網路請求 及 響應 的是 工作程式(worker
),在類 unix
系統上,Nginx
可以配置 多個 worker
,而每個 worker
程式 都可以同時處理 數以千計 的 網路請求。
1.3. 模組化設計
Nginx
的 worker
程式,包括 核心 和 功能性模組,核心模組 負責維持一個 執行迴圈(run-loop
),執行網路請求處理的 不同階段 的模組功能,比如:網路讀寫、儲存讀寫、內容傳輸、外出過濾,以及 將請求發往上游伺服器 等。而其程式碼的 模組化設計,也使得我們可以根據需要對 功能模組 進行適當的 選擇 和 修改,編譯成具有 特定功能 的伺服器。
1.4. 事件驅動模型
基於 非同步及非阻塞 的 事件驅動模型,可以說是 Nginx
得以獲得 高併發、高效能 的關鍵因素,同時也得益於對 Linux
、Solaris
及類 BSD
等作業系統核心中 事件通知 及 I/O
效能增強功能 的採用,如 kqueue
、epoll
及 event ports
。
1.5. 代理(proxy)設計
代理設計,可以說是 Nginx
深入骨髓的設計,無論是對於 HTTP
,還是對於 FastCGI
、Memcache
、Redis
等的網路請求或響應,本質上都採用了 代理機制。所以,Nginx
天生就是高效能的 代理伺服器。
2. Nginx的模組化設計
高度模組化 的設計是 Nginx
的架構基礎。Nginx
伺服器被分解為 多個模組,每個模組就是一個 功能模組,只負責自身的功能,模組之間嚴格遵循 “高內聚,低耦合” 的原則。
2.1. 核心模組
核心模組 是 Nginx
伺服器正常執行 必不可少 的模組,提供 錯誤日誌記錄、配置檔案解析、事件驅動機制、程式管理 等核心功能。
2.2. 標準HTTP模組
標準 HTTP
模組提供 HTTP
協議解析相關的功能,比如:埠配置、網頁編碼設定、HTTP
響應頭設定 等等。
2.3. 可選HTTP模組
可選 HTTP
模組主要用於 擴充套件 標準的 HTTP
功能,讓 Nginx
能處理一些特殊的服務,比如:Flash
多媒體傳輸、解析 GeoIP
請求、網路傳輸壓縮、安全協議 SSL
支援等。
2.4. 郵件服務模組
郵件服務模組 主要用於支援 Nginx
的 郵件服務,包括對 POP3
協議、IMAP
協議和 SMTP
協議的支援。
2.5. 第三方模組
第三方模組 是為了擴充套件 Nginx
伺服器應用,完成開發者自定義功能,比如:Json
支援、Lua
支援等。
3. Nginx的請求方式處理
Nginx
是一個 高效能 的 Web
伺服器,能夠同時處理 大量的併發請求。它結合 多程式機制 和 非同步機制,非同步機制使用的是 非同步非阻塞方式,接下來就給大家介紹一下 Nginx
的 多執行緒機制 和 非同步非阻塞機制。
3.1. 多程式機制
伺服器每當收到一個客戶端時,就有 伺服器主程式(master process
)生成一個 子程式(worker process
)出來和客戶端建立連線進行互動,直到連線斷開,該子程式就結束了。
使用 程式 的好處是 各個程式之間相互獨立,不需要加鎖,減少了使用鎖對效能造成影響,同時降低程式設計的複雜度,降低開發成本。其次,採用獨立的程式,可以讓 程式互相之間不會影響,如果一個程式發生異常退出時,其它程式正常工作,master
程式則很快啟動新的 worker
程式,確保服務不會中斷,從而將風險降到最低。
缺點是作業系統生成一個 子程式 需要進行 記憶體複製 等操作,在 資源 和 時間 上會產生一定的開銷。當有 大量請求 時,會導致 系統效能下降。
3.2. 非同步非阻塞機制
每個 工作程式 使用 非同步非阻塞方式,可以處理 多個客戶端請求。
當某個 工作程式 接收到客戶端的請求以後,呼叫 IO
進行處理,如果不能立即得到結果,就去 處理其他請求(即為 非阻塞);而 客戶端 在此期間也 無需等待響應,可以去處理其他事情(即為 非同步)。
當 IO
返回時,就會通知此 工作程式;該程式得到通知,暫時 掛起 當前處理的事務去 響應客戶端請求。
4. Nginx事件驅動模型
在 Nginx
的 非同步非阻塞機制 中,工作程式 在呼叫 IO
後,就去處理其他的請求,當 IO
呼叫返回後,會 通知 該 工作程式。對於這樣的系統呼叫,主要使用 Nginx
伺服器的 事件驅動模型 來實現。
如上圖所示,Nginx
的 事件驅動模型 由 事件收集器、事件傳送器 和 事件處理器 三部分基本單元組成。
-
事件收集器:負責收集
worker
程式的各種IO
請求; -
事件傳送器:負責將
IO
事件傳送到 事件處理器; -
事件處理器:負責各種事件的 響應工作。
事件傳送器 將每個請求放入一個 待處理事件列表,使用非阻塞 I/O
方式呼叫 事件處理器 來處理該請求。其處理方式稱為 “多路 IO 複用方法”,常見的包括以下三種:select
模型、poll
模型、epoll
模型。
5. Nginx程式處理模型
Nginx
伺服器使用 master/worker
多程式模式。多執行緒啟動和執行的流程如下:
-
主程式
Master process
啟動後,通過一個for
迴圈來 接收 和 處理外部訊號; -
主程式 通過
fork()
函式產生worker
子程式,每個 子程式 執行一個for
迴圈來實現Nginx
伺服器 對事件的接收 和 處理。
一般推薦 worker
程式數 與 CPU
核心數 一致,這樣一來不存在 大量的子程式 生成和管理任務,避免了程式之間 競爭 CPU
資源 和 程式切換 的開銷。而且 Nginx
為了更好的利用 多核特性,提供了 CPU
親緣性 的繫結選項,我們可以將某 一個程式繫結在某一個核 上,這樣就不會因為 程式的切換 帶來 Cache
的失效。
對於每個請求,有且只有一個 工作程式 對其處理。首先,每個 worker
程式都是從 master
程式 fork
過來。在 master
程式裡面,先建立好需要 listen
的 socket(listenfd)
之後,然後再 fork
出多個 worker
程式。
所有 worker
程式的 listenfd
會在 新連線 到來時變得 可讀,為保證只有一個程式處理該連線,所有 worker
程式在註冊 listenfd
讀事件 前 搶佔 accept_mutex
,搶到 互斥鎖 的那個程式 註冊 listenfd
讀事件,在 讀事件 裡呼叫 accept
接受該連線。
當一個 worker
程式在 accept
這個連線之後,就開始 讀取請求,解析請求,處理請求,產生資料後,再 返回給客戶端,最後才 斷開連線,這樣一個完整的請求就是這樣的了。我們可以看到,一個請求,完全由 worker
程式來處理,而且只在一個 worker
程式中處理。
在 Nginx
伺服器的執行過程中,主程式 和 工作程式 需要程式互動。互動依賴於 Socket
實現的 管道 來實現。
5.1. 主程式與工作程式互動
這條管道與普通的管道不同,它是由 主程式 指向 工作程式 的 單向管道,包含主程式向工作程式發出的 指令,工作程式 ID
等;同時 主程式 與外界通過 訊號通訊;每個 子程式 具備 接收訊號,並處理相應的事件的能力。
5.2. 工作程式與工作程式互動
這種互動是和 主程式-工作程式 互動是基本一致的,但是會通過 主程式 間接完成。工作程式 之間是 相互隔離 的,所以當工作程式 W1
需要向工作程式 W2
發指令時,首先找到 W2
的 程式 ID
,然後將正確的指令寫入指向 W2
的 通道。W2
收到訊號採取相應的措施。
小結
通過這篇文章,我們對 Nginx
伺服器的 整體架構 有了一個整體的認識。包括其 模組化的設計、多程式 和 非同步非阻塞 的請求處理方式、事件驅動模型 等。通過這些理論知識,才能更好地領悟 Nginx
的設計思想。對於我們學習 Nginx
來說有很大的幫助。
歡迎關注技術公眾號: 零壹技術棧
本帳號將持續分享後端技術乾貨,包括虛擬機器基礎,多執行緒程式設計,高效能框架,非同步、快取和訊息中介軟體,分散式和微服務,架構學習和進階等學習資料和文章。