天天看點

Nginx 架構初探

一、Nginx 子產品化設計

高度子產品化的設計是 Nginx 的架構基礎。Nginx 伺服器被分解為多個子產品,每個子產品就是一個功能子產品,隻負責自身的功能,子產品之間嚴格遵循“高内聚,低耦合”的原則。

Nginx 架構初探

 ●  核心子產品

核心子產品是 Nginx 伺服器正常運作必不可少的子產品,提供錯誤日志記錄、配置檔案解析、事件驅動機制、程序管理等核心功能。

 ●  标準 HTTP 子產品

标準 HTTP 子產品提供 HTTP 協定解析相關的功能,如:端口配置、網頁編碼設定、HTTP 響應頭設定等。

 ●  可選 HTTP 子產品

可選 HTTP 子產品主要用于擴充标準的 HTTP 功能,讓 Nginx 能處理一些特殊的服務,如:Flash 多媒體傳輸、解析 GeoIP 請求、SSL 支援等。

 ●  郵件服務子產品

郵件服務子產品主要用于支援 Nginx 的郵件服務,包括對 POP3 協定、IMAP 協定和 SMTP 協定的支援。

 ●  第三方子產品

第三方子產品是為了擴充 Nginx 伺服器應用,完成開發者自定義功能,如:Json 支援、Lua 支援等。

二、Nginx 請求處理方式

Nginx 是一個高性能的 Web 伺服器,能夠同時處理大量的并發請求。它結合多程序機制和異步機制,異步機制使用的是異步非阻塞方式,接下來就給大家介紹一下 Nginx 的多線程機制和異步非阻塞機制。

 ●  多程序

伺服器每當收到一個用戶端時。就有伺服器主程序(master process)生成一個子程序(worker process)出來和用戶端建立連接配接進行互動,直到連接配接斷開,該子程序就結束了。使用程序的好處是各個程序之間互相獨立,不需要加鎖,減少了使用鎖對性能造成影響,同時降低程式設計的複雜度,降低開發成本。其次,采用獨立的程序,可以讓程序互相之間不會影響,如果一個程序發生異常退出時,其它程序正常工作,master 程序則很快啟動新的 worker 程序,確定服務部中斷,将風險降到最低。缺點是作業系統生成一個子程序需要進行記憶體複制等操作,在資源和時間上會産生一定的開銷;當有大量請求時,會導緻系統性能下降。

 ●  異步非阻塞

每個工作程序使用異步非阻塞方式,可以處理多個用戶端請求。當某個工作程序接收到用戶端的請求以後,調用 IO 進行處理,如果不能立即得到結果,就去處理其他的請求(即為非阻塞);而用戶端在此期間也無需等待響應,可以去處理其他事情(即為異步);當 IO 傳回時,就會通知此工作程序;該程序得到通知,暫時挂起目前處理的事務去響應用戶端請求。

三、Nginx 事件驅動模型

在 Nginx 的異步非阻塞機制中,工作程序在調用 IO 後,就去處理其他的請求,當 IO 調用傳回後,會通知該工作程序。對于這樣的系統調用,主要使用 Nginx 伺服器的事件驅動模型來實作。

Nginx 架構初探

如上圖所示,Nginx 的事件驅動模型由事件收集器、事件發送器和事件處理器三部分基本單元組成。其中,事件收集器負責收集 worker 程序的各種 IO 請求,事件發送器負責将 IO 事件發送到事件處理器,而事件處理器負責各種事件的響應工作。

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

四、Nginx 設計架構

Nginx 伺服器使用 master/worker 多程序模式。多線程啟動和執行的流程如下:主程式 Master process 啟動後,通過一個 for 循環來接收和處理外部信号;主程序通過 fork() 函數産生子程序,每個子程序執行一個 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 架構初探

在 Nginx 伺服器的運作過程中,主程序和工作程序需要程序互動。互動依賴于 Socket 實作的管道來實作。

 ●  Master-Worker 互動

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

 ●  worker-worker 互動

這種互動是和 Master-Worker 互動是基本一緻的,但是會通過主程序。工作程序之間是互相隔離的,是以當工作程序 W1 需要向工作程序 W2 發指令時,首先找到 W2 的程序 ID,然後将正确的指令寫入指向 W2 的通道。W2 收到信号采取相應的措施。

五、總結

通過這篇文章,我們對 Nginx 伺服器的整體架構有了一個整體的認識。包括其子產品化的設計、多程序和異步非阻塞的請求處理方式、事件驅動模型等。通過這些理論知識,對于我們以後學習 Nginx 的源碼有很大的幫助;也推薦大家多看看 Nginx 的源碼,才能更好地領悟 Nginx 的設計思想。

原文釋出時間為:2018-09-15

本文作者:鄭杭進

本文來自雲栖社群合作夥伴“

Java架構沉思錄

”,了解相關資訊可以關注“

”。