在專案中遇到乙個問題,需要詳細了解下http協議及其nginx中對http協議的支援程度。今天一天收集了一些資料,也梳理出最終方案。記錄到部落格上,方便後續查閱。重點關注以下幾個方面:1、http互動中如何判定內容的長度及其http協議中關於content-length的解讀。2、chunk和gzip在nginx中的實現及原理。3、upstream如何和chunked結合。
在http協議中,有content-length的詳細解讀。content-length用於描述http訊息實體的傳輸長度the transfer-length of the message-body。在http協議中,訊息實體長度和訊息實體的傳輸長度是有區別,比如說gzip壓縮下,訊息實體長度是壓縮前的長度,訊息實體的傳輸長度是gzip壓縮後的長度。
在具體的http互動中,客戶端是如何獲取訊息長度的呢,主要基於以下幾個規則:
其實後面幾條幾乎可以忽視,簡單總結後如下:
1、content-length如果存在並且有效的話,則必須和訊息內容的傳輸長度完全一致。(經過測試,如果過短則會截斷,過長則會導致超時。)
2、如果存在transfer-encoding(重點是chunked),則在header中不能有content-length,有也會被忽視。
3、如果採用短連線,則直接可以通過伺服器關閉連線來確定訊息的傳輸長度。(這個很容易懂)
結合http協議其他的特點,比如說http1.1之前的不支援keep alive。那麼可以得出以下結論:
1、在http 1.0及之前版本中,content-length欄位可有可無。
2、在http1.1及之後版本。如果是keep alive,則content-length和chunk必然是二選一。若是非keep alive,則和http1.0一樣。content-length可有可無。
第一、nginx的chunk模組
nginx的chunk模組是乙個典型的filter模組,它本身是內建必選的nginx模組。在0.7.66版本之後,有乙個配置項chunked_transfer_encoding可以開啟或者關閉chunk模式,預設是開啟的。
首先,先簡單了解下在http協議中chunked相關的知識點。chunked一種transfer coding方式,在http1.0之前(包含http1.0)的版本是不支援的。在http協議中定義如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
chunked-body = *chunk
last-chunk
trailer
crlf
chunk = chunk-size [ chunk-extension ] crlf
chunk-data crlf
chunk-size = 1*hex
last-chunk = 1*(
"0"
) [ chunk-extension ] crlf
chunk-extension= *(
";"
chunk-ext-name [
"="
chunk-ext-val ] )
chunk-ext-name = token
chunk-ext-val = token | quoted-string
chunk-data = chunk-size(octet)
trailer = *(entity-header crlf)
其中可以看到,每乙個chunk都是可以大小自描述的。
在nginx的chunked模組中,header filter函式的流程如下:
1
2
3
4
5
6
7
8
9
10
11
12
//如果沒有content或者是head請求,則直接跳過。
if
(r->headers_out.content_length_n == -1)
else
else
}
}
對應的body filter的流程,則更加簡單,直接對當前輸出的buf chain進行乙個chunk封裝。
第二、nginx中gzip模組和r->headers_out.content_length_n
r->headers_out.content_length_n :這個在nginx內部用於表述請求返回內容的長度。但注意這不是完全相等的,只有在 r->headers_out.content_length_n >=0的時候,才有意義。比如說,通常後端的upstream(比如說php),如果沒有在指令碼中強制header輸出content-length,則預設在nginx中 r->headers_out.content_length_n = -1。
gzip模組也是乙個典型的filter模組。這裡簡單介紹下,後續可以詳細描述。在header filter中會直接清空 r->headers_out.content_length_n和header中輸出的content_length。為什麼要清空呢?主要是因為gzip要對內容模組進行壓縮處理,而在header filter的時候,gzip模組不可能計算出壓縮後的內容長度(原因是在nginx中,header 輸出和body的輸出是完全兩個不同的階段),所以最好的辦法就是在清空header中的content-length。這樣結合之前的介紹的chunked模組,可以看出:在nginx中,如果採用gzip,如果是keep alive,則必然是chunked模式。
在前面介紹的nginx chunked模式實現中提到,chunked模組的body filter是對當前處理的buf chain進行封裝成乙個chunk。那麼在nginx中是如何實現多個chunk的呢?這裡要從 ngx_event_pipe_write_to_downstream這個函式說起,該函式是upstream處理中非常關鍵的乙個函式,用於把upstream返回的資料直接進行輸出。
分析該函式的偽**如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
for
()
if
(p->upstream_eof || p->upstream_error || p->upstream_done)
/*計算當前buf的size*/
if
(bsize >= (
size_t
) p->busy_size)
for
( ;; )
flush:
//flush輸出
rc = p->output_filter(p->output_ctx, out);
}
可以看到,有乙個關鍵點,就是busy_size,它決定了乙個chunk的大小。這個對應乙個配置,追查後如下:在不同的upstream中有不同的定義,比如說proxy模式下,有proxy_busy_buffers_size,該值預設是proxy buffer size * 2 。(proxy buffer size根據作業系統,可以是4k或者8k)。fastcgi模式下,buffer_size預設是ngx_pagesize。配置項是fastcgi_busy_buffers_size。預設情況下是fastcgi的buffer size的2倍。(buffer size個是可配置的,buffer 個個數也是可以配置的,個數不能小於2)。
HTTP雜談 一 HTTP協議理解
一 http協議是什麼?你瀏覽的 每乙個網頁 都是 基於 http 協議 呈現的,http 協議是網際網路應用中,客戶端 瀏覽器 與伺服器 之間進行 資料通訊的一種協議 只要大家都 按照協議規定方式 發起請求和返回響應結果,任何人都可以基於http協議 實現自己的web客戶端 瀏覽器 爬蟲 和web...
HTTP協議 與HTTP相關的協議
在tcp ip協議族中與http密不可分的有3個協議,分別是ip tcp和dns。下面簡單介紹下這3種協議。ip internet protocol 網際協議位於網路層,幾乎所有使用網路的系統都會用到 ip 協議。tcp ip 協議族中的 ip 指的就 是網際協議,ip 和 ip位址 是不同的,ip...
HTTP協議與TCP協議
http是乙個屬於應用層的物件導向的協議,由於其簡捷 快速的方式,適用於分布式超 資訊系統。http協議工作於客戶端 服務端架構為上。超文字傳輸協議 http,hypertext transfer protocol 是網際網路上應用最為廣泛的一種網路協議。它可以使瀏覽器更加高效,使網路傳輸減少,它不...