Nginx 是如何處理 HTTP 頭部的?


Nginx 處理 HTTP 頭部的過程

Nginx 在處理 HTTP 請求之前,首先需要 Nginx 的框架先和客戶端建立好連接,然后接收用戶發來的 HTTP 的請求行,比如方法、URL 等,然后接收所有的 Header,根據這些 Header 信息,才能決定由哪些 HTTP 模塊處理請求。下面這張圖,解釋了 Nginx 在處理 HTTP 請求之前,所經歷的一系列流程,強烈建議收藏保存。下面針對每個部分單獨講解一下。

nginx 接收 HTTP 請求流程圖.png

接收請求事件模塊

首先是三次握手,當客戶端發來 ACK 之后,由操作系統內核回一個 SYN+ACK,緊接着客戶端 ACK 之后,連接建立成功。同時可能有很多 worker 進程都在監聽 80 或 443 端口,由操作系統的負載均衡算法,選取一個 worker 進程來處理,這個 worker 進程會通過 epoll_wait 方法,返回一個建立連接的句柄。拿到了監聽的句柄之后,這實際上是一個讀事件(因為是從操作系統中讀取到了一個請求),調用 accept 方法,分配連接內存池。

內存池主要分為連接內存池和請求內存池。

連接內存池大小的配置是 connection_pool_size,到了這一步之后,Nginx 會為已經建立的連接分配一個 512 字節大小的連接內存池。分配完內存池,建立好連接之后,HTTP 模塊會從事件模塊手里接入請求處理的過程,HTTP 模塊在啟動時,會調用 ngx_http_init_connection 方法來設置回調方法,這個時候會把新建立連接的讀事件通過 epoll_ctl 函數添加到 epoll 中,然后加一個超時定時器 client_header_timeout: 60s,這個定時器的作用是,如果超過 60s 還沒有接收到客戶端發來的請求,那么就會斷開連接。這一部分走完之后,Nginx 的事件模塊可能就會切換到其他的句柄去處理了。

當用戶真的把請求發來之后,操作系統會回復一個 ACK,同時事件模塊的 epoll_wait 也拿到了這個請求,這個時候會調用設置的回調方法 ngx_http_wait_request_handler,將接收到的用戶請求讀到用戶態中,而讀取到用戶態中需要操作系統分配內存,那么這段內存分配多大?從哪里分配呢?

這段內存是從連接內存池分配的,初始雖然分配了 512 字節,但是內存池可以擴展,由 client_header_buffer_size: 1k 分配 1k 內存,內存池並不是越大越好,因為用戶即使發送了 1 個字節,也會分配出 1k 的內存出來。當 URL 超過 1k 后,應該怎么辦呢?

接收請求 HTTP 模塊

處理請求和處理連接是不一樣的,處理請求只需要放到 Nginx 內存中就行了,但是處理請求還需要做大量的上下文分析,所以要分配一個請求內存池 request_pool_size: 4k。分配完以后,狀態機開始解析請求行,如果這時候發現 URL 大於 4k,那么就會再分配一個大內存,也就是 large_client_header_buffers: 4 8k,這個配置的意思是說,最多分配 4 個 8k,它並不是一次性分配 32k,而是先分配 8k 然后再去解析請求行,如果依然大於 8k,那么就會再分配 8k 的內存。

Nginx 有很多變量,這些變量都是指針,其中可以用來標識 URI,標識完成之后,就開始處理 header。狀態機解析 header 的時候,如果發現內存不夠,也就是假如 URL 已經用掉了 large_client_header_buffers: 4 8k 中的 2 個 8k,這時候最多也只能分配 8k,請求行和 header 是公用 4 個 8k的。

分配完大內存之后,就開始標識 header,確定哪一個 server 塊去處理請求,然后移除超時定時器,接下來,就開始核心的 11 個階段 HTTP 請求處理請求。

這里需要注意以下幾個地方:

  • 連接內存池:初始大小 512 字節
    • client_header_buffer_size: 1k 從連接內存池中分配
    • large_client_header_buffers: 4 8k 也是從連接內存池中分配
  • 請求內存池:request_pool_size: 4k

公眾號「原少子楊」回復 Nginx 領取知識圖譜

關注公眾號回復 Nginx 領取知識圖譜


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM