nginx基礎概念 100 之request

2021-06-19 15:30:16 字數 3529 閱讀 2094

這節我們講request,在nginx中我們指的是http請求,具體到nginx中的資料結構是ngx_http_request_t。ngx_http_request_t是對乙個http請求的封裝。 我們知道,乙個http請求,包含請求行、請求頭、請求體、響應行、響應頭、響應體。

http請求是典型的請求-響應型別的的網路協議,而http是檔案協議,所以我們在分析請求行與請求頭,以及輸出響應行與響應頭,往往是一行一行的進行處理。如果我們自己來寫乙個http伺服器,通常在乙個連線建立好後,客戶端會傳送請求過來。然後我們讀取一行資料,分析出請求行中包含的method、uri、http_version資訊。然後再一行一行處理請求頭,並根據請求method與請求頭的資訊來決定是否有請求體以及請求體的長度,然後再去讀取請求體。得到請求後,我們處理請求產生需要輸出的資料,然後再生成響應行,響應頭以及響應體。在將響應傳送給客戶端之後,乙個完整的請求就處理完了。當然這是最簡單的webserver的處理方式,其實nginx也是這樣做的,只是有一些小小的區別,比如,當請求頭讀取完成後,就開始進行請求的處理了。nginx通過ngx_http_request_t來儲存解析請求與輸出響應相關的資料。

那接下來,簡要講講nginx是如何處理乙個完整的請求的。對於nginx來說,乙個請求是從ngx_http_init_request開始的,在這個函式中,會設定讀事件為ngx_http_process_request_line,也就是說,接下來的網路事件,會由ngx_http_process_request_line來執行。從ngx_http_process_request_line的函式名,我們可以看到,這就是來處理請求行的,正好與之前講的,處理請求的第一件事就是處理請求行是一致的。通過ngx_http_read_request_header來讀取請求資料。然後呼叫ngx_http_parse_request_line函式來解析請求行。nginx為提高效率,採用狀態機來解析請求行,而且在進行method的比較時,沒有直接使用字串比較,而是將四個字元轉換成乙個整型,然後一次比較以減少cpu的指令數,這個前面有說過。很多人可能很清楚乙個請求行包含請求的方法,uri,版本,卻不知道其實在請求行中,也是可以包含有host的。比如乙個請求get  http/1.0這樣乙個請求行也是合法的,而且host是www.taobao.com,這個時候,nginx會忽略請求頭中的host域,而以請求行中的這個為準來查詢虛擬主機。另外,對於對於http0.9版來說,是不支援請求頭的,所以這裡也是要特別的處理。所以,在後面解析請求頭時,協議版本都是1.0或1.1。整個請求行解析到的引數,會儲存到ngx_http_request_t結構當中。

在解析完請求行後,nginx會設定讀事件的handler為ngx_http_process_request_headers,然後後續的請求就在ngx_http_process_request_headers中進行讀取與解析。ngx_http_process_request_headers函式用來讀取請求頭,跟請求行一樣,還是呼叫ngx_http_read_request_header來讀取請求頭,呼叫ngx_http_parse_header_line來解析一行請求頭,解析到的請求頭會儲存到ngx_http_request_t的域headers_in中,headers_in是乙個鍊錶結構,儲存所有的請求頭。而http中有些請求是需要特別處理的,這些請求頭與請求處理函式存放在乙個對映表裡面,即ngx_http_headers_in,在初始化時,會生成乙個hash表,當每解析到乙個請求頭後,就會先在這個hash表中查詢,如果有找到,則呼叫相應的處理函式來處理這個請求頭。比如:host頭的處理函式是ngx_http_process_host。

當nginx解析到兩個回車換行符時,就表示請求頭的結束,此時就會呼叫ngx_http_process_request來處理請求了。ngx_http_process_request會設定當前的連線的讀寫事件處理函式為ngx_http_request_handler,然後再呼叫ngx_http_handler來真正開始處理乙個完整的http請求。這裡可能比較奇怪,讀寫事件處理函式都是ngx_http_request_handler,其實在這個函式中,會根據當前事件是讀事件還是寫事件,分別呼叫ngx_http_request_t中的read_event_handler或者是write_event_handler。由於此時,我們的請求頭已經讀取完成了,之前有說過,nginx的做法是先不讀取請求body,所以這裡面我們設定read_event_handler為ngx_http_block_reading,即不讀取資料了。剛才說到,真正開始處理資料,是在ngx_http_handler這個函式裡面,這個函式會設定write_event_handler為ngx_http_core_run_phases,並執行ngx_http_core_run_phases函式。ngx_http_core_run_phases這個函式將執行多階段請求處理,nginx將乙個http請求的處理分為多個階段,那麼這個函式就是執行這些階段來產生資料。因為ngx_http_core_run_phases最後會產生資料,所以我們就很容易理解,為什麼設定寫事件的處理函式為ngx_http_core_run_phases了。在這裡,我簡要說明了一下函式的呼叫邏輯,我們需要明白最終是呼叫ngx_http_core_run_phases來處理請求,產生的響應頭會放在ngx_http_request_t的headers_out中,這一部分內容,我會放在請求處理流程裡面去講。nginx的各種階段會對請求進行處理,最後會呼叫filter來過濾資料,對資料進行加工,如truncked傳輸、gzip壓縮等。這裡的filter包括header filter與body filter,即對響應頭或響應體進行處理。filter是乙個鍊錶結構,分別有header filter與body filter,先執行header filter中的所有filter,然後再執行body filter中的所有filter。在header filter中的最後乙個filter,即ngx_http_header_filter,這個filter將會遍歷所有的響應頭,最後需要輸出的響應頭在乙個連續的記憶體,然後呼叫ngx_http_write_filter進行輸出。ngx_http_write_filter是body filter中的最後乙個,所以nginx首先的body資訊,在經過一系列的body filter之後,最後也會呼叫ngx_http_write_filter來進行輸出(有圖來說明)。

這裡要注意的是,nginx會將整個請求頭都放在乙個buffer裡面,這個buffer的大小通過配置項client_header_buffer_size來設定,如果使用者的請求頭太大,這個buffer裝不下,那nginx就會重新分配乙個新的更大的buffer來裝請求頭,這個大buffer可以通過large_client_header_buffers來設定,這個large_buffer這一組buffer,比如配置4 8k,就是表示有四個8k大小的buffer可以用。注意,為了儲存請求行或請求頭的完整性,乙個完整的請求行或請求頭,需要放在乙個連續的記憶體裡面,所以,乙個完整的請求行或請求頭,只會儲存在乙個buffer裡面。這樣,如果請求行大於乙個buffer的大小,就會返回414錯誤,如果乙個請求頭大小大於乙個buffer大小,就會返回400錯誤。在了解了這些引數的值,以及nginx實際的做法之後,在應用場景,我們就需要根據實際的需求來調整這些引數,來優化我們的程式了。

處理流程圖:

nginx基礎概念 100 之keepalive

當然,在nginx中,對於http1.0與http1.1也是支援長連線的。什麼是長連線呢?我們知道,http請求是基於tcp協議之上的,那麼,當客戶端在發起請求前,需要先與服務端建立tcp連線,而每一次的tcp連線是需要三次握手來確定的,如果客戶端與服務端之間網路差一點,這三次互動消費的時間會比較多...

Nginx 基礎概念

1 nginx是什麼 1.nginx engine x 是乙個高效能的http和反向 伺服器,特點是占用記憶體少,併發能力強。2.ngix專門為效能優化而開發,效能是其最重要的考量,有報告表明能支援高達50000個併發連線數。2.安裝 安裝教程 3.nginx常用命令 進入nginx所在目錄 usr...

Nginx之基礎概念及特性介紹

2.nginx的安裝部署 nginx是一款輕量級的http伺服器,發音為 engine x 是一款高效能的http和反向 伺服器,同時也是乙個imap pop3 smtp 伺服器,乙個通用的tcp udp 伺服器。其特點是占用記憶體資源少,併發能力強,根據官方提供的資料,單台伺服器併發能力最高能達到...