只知道 Nginx 牛逼,卻不知道它怎麼支持百萬併發?

20

大進階架構專題每日送達

只知道 Nginx 牛逼,卻不知道它怎麼支持百萬併發?

來源 | https://urlify.cn/UN3eUb

Nginx相關文章:

  1. 基於Nginx實現訪問控制、連接限制

  2. Ngxix | 超詳細!Nginx 日誌配置實踐

  3. Nginx為什麼快到根本停不下來?

  4. Nginx在高併發下的性能優化點!有這篇就夠了!

今天,再給大家講講Nginx的整體架構,以及進程模型。Nginx是一個免費的,開源的,高性能的HTTP服務器和反向代理。以其高性能,穩定性,豐富的功能,簡單的配置和低資源消耗而聞名。Nginx是一個Web服務器,也可以用作負載均衡器和HTTP緩存。

很多高知名度的網站都使用Nginx,比如:Netflix,GitHub,SoundCloud,MaxCDN等。

1.Nginx的整體架構

只知道 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及eventports。

1.5.代理(proxy)設計

代理設計,可以說是Nginx深入骨髓的設計,無論是對於HTTP,還是對於FastCGI、Memcache、Redis等的網絡請求或響應,本質上都採用了代理機制。所以,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 多進程模式,多線程啟動和執行的流程如下:

主程序Masterprocess啟動後,通過一個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 牛逼,卻不知道它怎麼支持百萬併發?

在Nginx服務器的運行過程中,主進程和工作進程需要進程交互。交互依賴於Socket實現的管道來實現。

5.1.主進程與工作進程交互

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

5.2.工作進程與工作進程交互

這種交互和主進程-工作進程交互基本一致,但是會通過主進程間接完成,工作進程之間是相互隔離的。

所以當工作進程W1需要向工作進程W2發指令時,首先找到W2的進程ID,然後將正確的指令寫入指向W2的通道,W2收到信號採取相應的措施。

只知道 Nginx 牛逼,卻不知道它怎麼支持百萬併發?

之前,給大家發過三份Java面試寶典,這次新增了一份,目前總共是四份面試寶典,相信在跳槽前一個月按照面試寶典準備準備,基本沒大問題。

  • 《java面試寶典5.0》(初中級)

  • 《350道Java面試題:整理自100+公司》(中高級)

  • 《資深java面試寶典-視頻版》(資深)

  • 《Java[BAT]面試必備》(資深)

分別適用於初中級,中高級資深

級工程師的面試複習。

內容包含java基礎、javaweb、mysql性能優化、JVM、鎖、百萬併發、消息隊列,高性能緩存、反射、Spring全家桶原理、微服務、Zookeeper、數據結構、限流熔斷降級等等。

只知道 Nginx 牛逼,卻不知道它怎麼支持百萬併發?

看到這裡,證明有所收穫


分享到:


相關文章: