1. 程式人生 > >Nginx:HTTP框架是如何介入請求

Nginx:HTTP框架是如何介入請求

考資料 <深入理解Nginx>(陶輝)

            Nginx事件模組部落格地址:http://www.cnblogs.com/runnyu/p/4914698.html

 

Nginx是一個事件驅動構架的Web伺服器,在上次的部落格中我們可以看到Nginx是如何驅動事件的處理的。

本次將介紹HTTP框架是如何介入跟處理HTTP網路事件的。因為書上的思路已經足夠清晰而且內容比較獨立,因此本次基本上就把本章的重點記錄一下。

 

HTTP框架存在的目的

1.Nginx事件框架主要是針對傳輸層的TCP的,作為Web伺服器HTTP模組需要處理的則是HTTP,HTTP框架必須要針對基於TCP的事件框架解決好HTTP的網路傳輸、解析、組裝等問題。

2.雖然事件驅動構架在效能上是不錯的,但是HTTP模組的業務通常較複雜,HTTP框架的存在則可以讓我們遮蔽事件驅動架構,儘量只關注業務,提供開發效率。

 

 

新連線建立時的行為

在上次部落格的最後可以看到,在ngx_event_accept方法建立連線的最後一步,將會呼叫ngx_listening_t監聽結構體的handler方法。這時候HTTP框架就開始介入請求了。

HTTP框架在初始化時就會將每個監聽ngx_listening_t結構體的handler方法設為ngx_http_init_connection方法,該方法執行流程如下圖:

其中定時器中的超時時間是nginx.conf配置檔案中指定的client_header_timeout,後面的超時時間設定也是這個值。

 

 

第一次可讀事件的處理

當TCP連線上第一次出現可讀事件時,將會呼叫ngx_http_init_request方法初始化這個HTTP請求

該方法主要做了3件事情:

1.對請求構造ngx_http_request_t結構體並初始化部分引數;

2.修改讀事件的回撥方法為ngx_http_process_request_line

3.呼叫上面的回撥方法解析HTTP請求行

6.讀事件被觸發,這是需要在使用者態的程序空間分配記憶體,用來把核心緩衝區上的TCP流複製到使用者態的記憶體中。

   這一步將在ngx_connection_t的記憶體池中分配一塊記憶體,記憶體塊的大小與nginx.conf檔案中的client_header_buffer_size配置項引數一致。

   ngx_connection_t結構體的buffer指標以及ngx_http_request_t結構體的header_in指標共同指向這塊記憶體緩衝區。

 

 

 

接收HTTP請求行

在初始化請求之後,將呼叫ngx_http_process_request_line方法接收HTTP請求行。

因為請求行的長度是不定的,這意味著在讀事件被觸發時,核心套接字緩衝區的大小未必足夠接收到全部的HTTP請求行。

因此呼叫一次ngx_http_process_request_line方法不一定能夠接收完完整的HTTP請求行,該方法會被多次排程。下圖展示了該方法的流程

該方法會呼叫recv方法把Linux核心套接字緩衝區中的TCP流複製到header_in緩衝區中。

header_in的型別是ngx_buf_t,它的pos成員和last成員指向的地址之間的記憶體就是收到的未解析的字元流。

 

4.在本次沒有接收到TCP流的時候,告訴事件驅動程式繼續檢測這個讀事件,然後該方法就結束。在該讀事件準備好的時候,該方法將被再次排程。

5.在接收到TCP流後,用狀態機(ngx_http_parse_request_line方法)解析已經接收到的TCP字元流,確認其是否構成完整的HTTP請求行。

7.如果ngx_http_parse_request_line方法返回NGX_OK,表示已經成功地接收到完整的請求行。這一步將把請求行的的資訊設定到ngx_http_request_t結構體的相應成員中

   (request_line、uri、method_name、http_protocol、args等)。

11.接收完HTTP請求行後,把讀事件的回撥方法更改為ngx_http_request_headers準備接收HTTP頭部。

 

 

接收HTTP頭部

跟HTTP請求行一樣,HTTP頭部也屬於可變長度的字串,它與HTTP請求行和包體間都是通過換行符來區分的。

下圖展示了HTTP框架使用ngx_http_process_request_headers方法接收、解析HTTP頭部的流程

6.呼叫ngx_http_parse_header_line方法解析緩衝區的字元流。這個方法有3個返回值:

   返回NGX_OK時,表示解析出一行HTTP頭部;返回NGX_HTTP_PARSE_HEADER_DONE時,表示已經解析出了完整的HTTP頭部;

   返回NGX_AGAIN時,表示還需要接收到更多的字元流才能繼續解析;除此之外的錯誤情況,將傳送400錯誤給客戶端。

7.解析出的HTTP頭部資訊設定到ngx_http_request_t結構體headers_in成員的headers連結串列中。

9.當ngx_http_parse_header_line方法返回NGX_HTTP_PARSE_HEADER_DONE時,將會根據HTTP頭部中的host欄位情況,

   呼叫ngx_http_find_virtual_server方法找到對應的虛擬主機配置塊。ngx_http_request_t結構體裡的srv_conf、loc_conf成員被重新設定,以指向正確的虛擬主機。

 

在接收到完整的HTTP頭部後,已經有足夠的必要資訊開始在業務上處理HTTP請求了。下一節將說明HTTP框架是如何召集負責具體功能的各HTTP模組合作處理請求的。