輕鬆應對百萬併發的Nginx是怎麼處理這些網路事件的?

安全劍客發表於2020-01-25
在深入瞭解 Nginx 各種原理及在極端場景下的一些錯誤場景處理時,需要首先理解什麼是網路事件。

Nginx 是一個事件驅動的框架,所謂事件主要指的是網路事件,Nginx 每個網路連線會對應兩個網路事件,一個讀事件一個寫事件。在深入瞭解 Nginx 各種原理及在極端場景下的一些錯誤場景處理時,需要首先理解什麼是網路事件。

網路傳輸

輕鬆應對百萬併發的Nginx是怎麼處理這些網路事件的?輕鬆應對百萬併發的Nginx是怎麼處理這些網路事件的?
接下來看上面這張圖,比如主機 A 就是一臺家裡的膝上型電腦,那麼主機 B 就是一臺伺服器,上面跑著 Nginx 服務。從主機 A 傳送一個 HTTP 的 GET 請求到主機 B,這樣的一個過程中主要經歷了哪些事件?透過上圖資料流部分可以看出:

應用層裡傳送了一個 GET 請求 -> 到了傳輸層,這一步主要在做一件事,就是瀏覽器開啟了一個埠,在 windows 的工作管理員中可以看到這一點,他會把這個埠記下來以及把 Nginx 開啟的埠比如 80 或者 443 也記到傳輸層 -> 然後在網路層會記下我們主機所在的 IP 和目標主機,也就是 Nginx 所在伺服器公網 IP -> 到鏈路層以後 -> 經過乙太網 -> 到達家裡的路由器(網路層),家中的路由器會記錄下所在運營商的一些下一段的 IP -> 透過廣域網 -> 跳轉到主機 B 所在的機器中 -> 報文會經過鏈路層 -> 網路層 -> 到傳輸層,在傳輸層作業系統就知道是給那個開啟了 80 或者 443 的程式,這個程式自然就是 Nginx -> 那麼 Nginx 在他的 HTTP 狀態處理機裡面(應用層)就會處理這個請求。

在上述過程中網路報文扮演了一個怎樣的角色呢?

TCP流與報文

輕鬆應對百萬併發的Nginx是怎麼處理這些網路事件的?輕鬆應對百萬併發的Nginx是怎麼處理這些網路事件的?
資料鏈路層會在資料的前面 Header 部分和 Footer 部分新增上源 MAC 地址和源目的地址 -> 到了網路層則是 Nginx 的公網地址(目的 IP 地址)和瀏覽器的公網地址(源 IP 地址)-> 到了 TCP 層(傳輸層),指定了 Nginx 開啟的埠(目的埠)和瀏覽器開啟的埠(源埠)-> 然後應用層就是 HTTP 協議了。

這就是一個報文,也就是說我們傳送的 HTTP 協議會被切割成很多小的報文,在網路層會切割叫 MTU,乙太網的每個 MTU 是 1500 位元組;在 TCP 層(傳輸層)呢會考慮中間每個環節中最大的一個 MTU 值,這個時候往往每個報文只有幾百位元組,這個報文大小我們稱為叫 MSS ,所以每收到一個 MSS 小於這麼大小的一個報文時其實就是一個網路事件。

這個時候,我們來看下 TCP 協議中許多事件是怎樣和我們日常呼叫的一些介面(比如Accept、Read、Write、Close)是怎樣關聯在一起的?

TCP 協議與非阻塞介面

輕鬆應對百萬併發的Nginx是怎麼處理這些網路事件的?輕鬆應對百萬併發的Nginx是怎麼處理這些網路事件的?
請求建立 TCP 連線事件實際上是傳送了一個 TCP 報文,透過上面第二部分講解的那樣的一個流程到達了 Nginx,對應的是讀事件。因為對於 Nginx 來說,我讀取到了一個報文,所以就是 Accept 建立連結事件。

如果是 TCP 連線可讀事件,就是傳送了一個訊息,對於 Nginx 也是一個讀事件,就是 Read 讀訊息。

如果是對端(也就是瀏覽器)主動地關掉了,相當於 windows 作業系統會去傳送一個要求關閉連結的一個事件,對於 Nginx 來說還是一個讀事件,因為他只是去讀取一個報文。

那什麼是寫事件呢?當我們的瀏覽器需要向瀏覽器傳送響應的時候,需要把訊息寫到作業系統中,要求作業系統傳送到網路中,這就是一個寫事件。

像這樣的一些網路讀寫事件,通常在 Nginx 中或者任何一個非同步事件的處理框架中,他會有個東西叫事件收集、分發器。會定義每類事件處理的消費者,也就是說事件是一個生產者,是透過網路中自動的生產到我們的 Nginx 中的,我們要對每種事件建立一個消費者。比如連線建立事件消費者,就是對 Accept 呼叫,HTTP 模組就會去建立一個新的連線。還有很多讀訊息或者寫訊息,在 HTTP 狀態機中不同的時間段會呼叫不同的方法也就是每個消費者處理。

以上就是一個事件分發、消費器,包括 AIO 像非同步讀寫磁碟事件,還有定時器事件,比如是否超時(worker_shutdown_timeout)。

Nginx 網路事件例項

上面介紹了網路報文的傳送以及對應的 Nginx 中的網路事件,比如 Accept 建立一條新連線其實是收到一條讀事件,接下來我們透過抓包來分析建立三次握手時時怎麼樣讓 Nginx 收到讀事件,使用的抓包工具是 Wireshark。
輕鬆應對百萬併發的Nginx是怎麼處理這些網路事件的?輕鬆應對百萬併發的Nginx是怎麼處理這些網路事件的?
首先我們安裝 Wireshark 軟體,並對 Nginx 所在 IP 和埠進行抓包,然後訪問頁面,在 TCP 層主要說兩件事情:

•瀏覽器首先會開啟這個頁面,本地開啟了一個 1875 埠,而 Nginx 啟動的是 8080 埠。

•TCP 層主要做的是程式與程式之間通訊這件事。
輕鬆應對百萬併發的Nginx是怎麼處理這些網路事件的?輕鬆應對百萬併發的Nginx是怎麼處理這些網路事件的?
IP 層主要解決機器與機器之間怎樣互相找到的問題。
輕鬆應對百萬併發的Nginx是怎麼處理這些網路事件的?輕鬆應對百萬併發的Nginx是怎麼處理這些網路事件的?
三次握手也就是 windows 先向 Nginx 傳送了一次 [SYN],那麼相反的 Nginx 所在的伺服器也會向 windows 傳送一個 [SYN],這個時候 Nginx 是沒有感知到的,因為這個連線還是處於半開啟的狀態。直到這臺 windows 伺服器再次傳送 [ACK] 到 Nginx 所在的伺服器之上時,Nginx 所在的作業系統才會去通知 Nginx 我們收到了一個讀事件,這個讀事件對應是建立一個新連線,所以此時 Nginx 應該呼叫 Accept 方法去建立一個新的連線。

以上我們透過 Wireshark 抓包演示了正常的三次握手是怎麼樣引發一個讀事件來使得 Nginx 去處理這樣一個讀事件來建立新的連線的。

總結

這篇文章主要講解了網路事件,並透過抓包來分析 Nginx 網路事件,這對我們理解 Nginx 非同步處理框架是非常有幫助的,包括 OpenResty 也是強依賴於網路事件以及事件分發的。

原文地址:

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/31559985/viewspace-2674298/,如需轉載,請註明出處,否則將追究法律責任。

相關文章