淺談Nginx伺服器的內部核心架構設計

零壹技術棧發表於2018-07-15

前言

Nginx 是一個 免費的開源的高效能HTTP 伺服器和 反向代理,以及 IMAP / POP3 代理伺服器。 Nginx 以其高效能,穩定性,豐富的功能,簡單的配置和低資源消耗而聞名。Nginx是一個 Web 伺服器,也可以用作 反向代理負載均衡器HTTP 快取

很多高知名度的網站都使用 Nginx,如:NetflixGitHubSoundCloudMaxCDN 等。

淺談Nginx伺服器的內部核心架構設計

正文

1. Nginx的整體架構

淺談Nginx伺服器的內部核心架構設計

1.1. 主程式

Nginx 啟動時,會生成兩種型別的 程式*,一個是 主程式master),一個windows 版本的目前只有一個)或 多個工作程式worker)。主程式 並不處理網路請求,主要負責 排程工作程式,也就是圖示的 3 項:載入配置啟動工作程式非停升級。所以,Nginx 啟動以後,檢視作業系統的程式列表,我們就能看到 至少有兩個 Nginx 程式。

1.2. 工作程式

伺服器實際 處理網路請求響應 的是 工作程式worker),在類 unix 系統上,Nginx 可以配置 多個 worker,而每個 worker 程式 都可以同時處理 數以千計網路請求

1.3. 模組化設計

Nginxworker 程式,包括 核心功能性模組核心模組 負責維持一個 執行迴圈run-loop),執行網路請求處理的 不同階段 的模組功能,比如:網路讀寫儲存讀寫內容傳輸外出過濾,以及 將請求發往上游伺服器 等。而其程式碼的 模組化設計,也使得我們可以根據需要對 功能模組 進行適當的 選擇修改,編譯成具有 特定功能 的伺服器。

1.4. 事件驅動模型

基於 非同步及非阻塞事件驅動模型,可以說是 Nginx 得以獲得 高併發高效能 的關鍵因素,同時也得益於對 LinuxSolaris 及類 BSD 等作業系統核心中 事件通知I/O 效能增強功能 的採用,如 kqueueepollevent ports

1.5. 代理(proxy)設計

代理設計,可以說是 Nginx 深入骨髓的設計,無論是對於 HTTP,還是對於 FastCGIMemcacheRedis 等的網路請求或響應,本質上都採用了 代理機制。所以,Nginx 天生就是高效能的 代理伺服器

2. Nginx的模組化設計

高度模組化 的設計是 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伺服器的內部核心架構設計

如上圖所示,Nginx事件驅動模型事件收集器事件傳送器事件處理器 三部分基本單元組成。

  • 事件收集器:負責收集 worker 程式的各種 IO 請求;

  • 事件傳送器:負責將 IO 事件傳送到 事件處理器

  • 事件處理器:負責各種事件的 響應工作

事件傳送器 將每個請求放入一個 待處理事件列表,使用非阻塞 I/O 方式呼叫 事件處理器 來處理該請求。其處理方式稱為 “多路 IO 複用方法”,常見的包括以下三種:select 模型、poll 模型、epoll 模型。

5. Nginx程式處理模型

Nginx 伺服器使用 master/worker 多程式模式。多執行緒啟動和執行的流程如下:

  1. 主程式 Master process 啟動後,通過一個 for 迴圈來 接收處理外部訊號

  2. 主程式 通過 fork() 函式產生 worker 子程式,每個 子程式 執行一個 for 迴圈來實現 Nginx 伺服器 對事件的接收處理

一般推薦 worker 程式數CPU 核心數 一致,這樣一來不存在 大量的子程式 生成和管理任務,避免了程式之間 競爭 CPU 資源程式切換 的開銷。而且 Nginx 為了更好的利用 多核特性,提供了 CPU 親緣性 的繫結選項,我們可以將某 一個程式繫結在某一個核 上,這樣就不會因為 程式的切換 帶來 Cache 的失效。

對於每個請求,有且只有一個 工作程式 對其處理。首先,每個 worker 程式都是從 master 程式 fork 過來。在 master 程式裡面,先建立好需要 listensocket(listenfd) 之後,然後再 fork 出多個 worker 程式。

所有 worker 程式的 listenfd 會在 新連線 到來時變得 可讀,為保證只有一個程式處理該連線,所有 worker 程式在註冊 listenfd 讀事件搶佔 accept_mutex,搶到 互斥鎖 的那個程式 註冊 listenfd 讀事件,在 讀事件 裡呼叫 accept 接受該連線。

當一個 worker 程式在 accept 這個連線之後,就開始 讀取請求解析請求處理請求,產生資料後,再 返回給客戶端,最後才 斷開連線,這樣一個完整的請求就是這樣的了。我們可以看到,一個請求,完全由 worker 程式來處理,而且只在一個 worker 程式中處理。

淺談Nginx伺服器的內部核心架構設計

Nginx 伺服器的執行過程中,主程式工作程式 需要程式互動。互動依賴於 Socket 實現的 管道 來實現。

5.1. 主程式與工作程式互動

這條管道與普通的管道不同,它是由 主程式 指向 工作程式單向管道,包含主程式向工作程式發出的 指令工作程式 ID 等;同時 主程式 與外界通過 訊號通訊;每個 子程式 具備 接收訊號,並處理相應的事件的能力。

5.2. 工作程式與工作程式互動

這種互動是和 主程式-工作程式 互動是基本一致的,但是會通過 主程式 間接完成。工作程式 之間是 相互隔離 的,所以當工作程式 W1 需要向工作程式 W2 發指令時,首先找到 W2程式 ID,然後將正確的指令寫入指向 W2通道W2 收到訊號採取相應的措施。

小結

通過這篇文章,我們對 Nginx 伺服器的 整體架構 有了一個整體的認識。包括其 模組化的設計多程式非同步非阻塞 的請求處理方式、事件驅動模型 等。通過這些理論知識,才能更好地領悟 Nginx 的設計思想。對於我們學習 Nginx 來說有很大的幫助。


歡迎關注技術公眾號: 零壹技術棧

零壹技術棧

本帳號將持續分享後端技術乾貨,包括虛擬機器基礎,多執行緒程式設計,高效能框架,非同步、快取和訊息中介軟體,分散式和微服務,架構學習和進階等學習資料和文章。

相關文章