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

JAVA架構開發發表於2018-10-09

一、前言

Nginx---Ngine X,是一款免費的、自由的、開源的、高效能HTTP伺服器和反向代理伺服器;也是一個IMAP、POP3、SMTP代理伺服器;Nginx以其高效能、穩定性、豐富的功能、簡單的配置和低資源消耗而聞名。

也就是說Nginx本身就可以託管網站(類似於Tomcat一樣),進行Http服務處理,也可以作為反向代理伺服器 、負載均衡器和HTTP快取。

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

Nginx 解決了伺服器的C10K(就是在一秒之內連線客戶端的數目為10k即1萬)問題。它的設計不像傳統的伺服器那樣使用執行緒處理請求,而是一個更加高階的機制—事件驅動機制,是一種非同步事件驅動結構。

二、Nginx的特點

跨平臺:可以在大多數Unix like 系統編譯執行。而且也有Windows的移植版本。

配置異常簡單:非常的簡單,易上手。

非阻塞、高併發連線:資料複製時,磁碟I/O的第一階段是非阻塞的。官方測試能支援5萬併發連線,實際生產中能跑2~3萬併發連線數(得益於Nginx採用了最新的epoll事件處理模型(訊息佇列)。

Nginx代理和後端Web伺服器間無需長連線;

Nginx接收使用者請求是非同步的,即先將使用者請求全部接收下來,再一次性傳送到後端Web伺服器,極大減輕後端Web伺服器的壓力。

傳送響應報文時,是邊接收來自後端Web伺服器的資料,邊傳送給客戶端。

網路依賴性低,理論上只要能夠ping通就可以實施負載均衡,而且可以有效區分內網、外網流量。

支援內建伺服器檢測。Nginx能夠根據應用伺服器處理頁面返回的狀態碼、超時資訊等檢測伺服器是否出現故障,並及時返回錯誤的請求重新提交到其它節點上。

此外還有記憶體消耗小、成本低廉(比F5硬體負載均衡器廉價太多)、節省頻寬、穩定性高等特點。

如果你依然在程式設計的世界裡迷茫,不知道自己的未來規劃,可以加入JAVA架構學習交流群:835544715 裡面可以與大神一起交流並走出迷茫。進群免費領取學習資料,看看前輩們是如何在程式設計的世界裡傲然前行!群裡不停更新最新的教程和學習方法(進群送JAVA架構視訊資料),有想學習JAVA的,或是轉行,還有工作中想提升自己能力的,正在學習的小夥伴歡迎加入學習

三、Nginx的整體架構

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

1、模組化設計

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

2、代理設計

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

3、事件驅動模型

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

4、主程式模型

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

5、工作程式模型

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

四、Nginx的模組化設計

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

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

1、核心模組

核心模組是 Nginx 伺服器正常執行必不可少的模組,提供錯誤日誌記錄 、配置檔案解析、事件驅動機制、程式管理等核心功能。

2、標準HTTP模組

標準 HTTP 模組提供 HTTP 協議解析相關的功能,比如: 埠配置 、 網頁編碼設定 、 HTTP響應頭設定 等等。

3、可選HTTP模組

可選 HTTP 模組主要用於 擴充套件 標準的 HTTP 功能,讓 Nginx 能處理一些特殊的服務,比如: Flash 多媒體傳輸 、解析 GeoIP 請求、 網路傳輸壓縮 、 安全協議 SSL 支援等。

4、郵件服務模組

郵件服務模組主要用於支援 Nginx 的 郵件服務 ,包括對 POP3 協議、 IMAP 協議和 SMTP協議的支援。

5、第三方模組

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

五、代理設計中的正向代理和反向代理

首先,代理伺服器一般指區域網內部的機器通過代理伺服器傳送請求到網際網路上的伺服器,代理伺服器一般作用在客戶端。例如:GoAgent翻牆軟體。我們的客戶端在進行翻牆操作的時候,我們使用的正是正向代理,通過正向代理的方式,在我們的客戶端執行一個軟體,將我們的HTTP請求轉發到其他不同的伺服器端,實現請求的分發。

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

反向代理伺服器作用在伺服器端,它在伺服器端接收客戶端的請求,然後將請求分發給具體的伺服器進行處理,然後再將伺服器的相應結果反饋給客戶端。Nginx就是一個反向代理伺服器軟體。

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

從上圖可以看出:客戶端必須設定正向代理伺服器,當然前提是要知道正向代理伺服器的IP地址,還有代理程式的埠。 反向代理正好與正向代理相反,對於客戶端而言代理伺服器就像是原始伺服器,並且客戶端不需要進行任何特別的設定。客戶端向反向代理的名稱空間(name-space)中的內容傳送普通請求,接著反向代理將判斷向何處(原始伺服器)轉交請求,並將獲得的內容返回給客戶端。

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

六、Nginx事件驅動模型

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

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

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

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

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

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

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

七、Nginx的請求方式處理

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

1、多程式機制

伺服器每當收到一個客戶端時,就有 伺服器主程式 ( master process )生成一個 子程式( worker process )出來和客戶端建立連線進行互動,直到連線斷開,該子程式就結束了。

使用程式的好處是各個程式之間相互獨立,不需要加鎖,減少了使用鎖對效能造成影響,同時降低程式設計的複雜度,降低開發成本。其次,採用獨立的程式,可以讓程式互相之間不會影響 ,如果一個程式發生異常退出時,其它程式正常工作, master 程式則很快啟動新的 worker 程式,確保服務不會中斷,從而將風險降到最低。

缺點是作業系統生成一個子程式需要進行 記憶體複製等操作,在資源和時間上會產生一定的開銷。當有大量請求時,會導致系統效能下降 。

2、非同步非阻塞機制

每個工作程式 使用 非同步非阻塞方式 ,可以處理 多個客戶端請求 。

當某個 工作程式 接收到客戶端的請求以後,呼叫 IO 進行處理,如果不能立即得到結果,就去 處理其他請求 (即為 非阻塞 );而 客戶端 在此期間也 無需等待響應 ,可以去處理其他事情(即為 非同步 )。

當 IO 返回時,就會通知此 工作程式 ;該程式得到通知,暫時 掛起 當前處理的事務去 響應客戶端請求 。

八、Nginx程式處理模型

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

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

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

淺談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 實現的管道來實現。

1、主程式與工作程式互動

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

2、工作程式與工作程式互動

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

九、小結

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

相關文章