為什么nginx轉發后端默認使用1.0而不是1.1
在 Nginx 的官網文檔中,有這樣一個指令:
Syntax: gzip_http_version 1.0 | 1.1;
Default: gzip_http_version 1.1;
Context: http, server, location
Sets the minimum HTTP version of a request required to compress a response.
很明顯,這個指令是用來設置 Nginx 啟用 GZip 所需的 HTTP 最低版本,默認是 HTTP/1.1。
也就是說 Nginx 默認不壓縮 HTTP/1.0 是因為這個指令,將它的值改為?1.0
?就能解決問題。
對于文本文件,GZip 的效果非常明顯,開啟后傳輸所需流量大約會降至 1/4 ~ 1/3。這么好的事情,Nginx 改一下配置就可以支持,為什么它默認不開啟?
Nginx 對于滿足條件(請求頭中有 Accept-Encoding: gzip,響應內容的 Content-Type 存在于 gzip_types 列表)的請求會采用即時壓縮(On-The-Fly Compression),整個壓縮過程在內存中流式完成。也就是說,Nginx 不會等文件 GZip 完成再返回響應,而是邊壓縮邊響應,這樣可以顯著提高 TTFB(Time To First Byte,首字節時間,WEB 性能優化重要指標)。這樣唯一的問題是,Nginx 開始返回響應時,它無法知道將要傳輸的文件最終有多大,也就是無法給出?Content-Length
?這個響應頭部。
我們還知道,HTTP/1.1 默認支持 TCP 持久連接(Persistent Connection),HTTP/1.0 也可以通過顯式指定?Connection: keep-alive
?來啟用持久連接。HTTP 運行在 TCP 連接之上,自然也有著跟 TCP 一樣的三次握手、慢啟動等特性,要想提高 HTTP 性能,啟用持久連接就顯得尤為重要。
明白以上兩點,馬上就能水落石出了:對于 TCP 持久連接上的 HTTP 報文,客戶端需要一種機制來準確判斷結束位置。而在 HTTP/1.0 中,這種機制只有?Content-Length
。于是,對于本文前面提出的情況,HTTP Server 只能要么不壓縮,要么不啟用持久連接(對于非持久連接,TCP 斷開就可以認為 HTTP 報文結束),而 Nginx 默認選擇的是前者。
?
不啟用 Nginx 的 HTTP/1.0 GZip 功能,使用 HTTP/1.0 請求報文測試:
請求報文中指明了可以接受 GZip,但是返回的內容依然是未壓縮的;
同時服務端響應了?Content-Length
?和?Connection: keep-alive
,連接并沒有斷開。
也就是說對于 HTTP/1.0 請求,Nginx 為了盡可能啟用持久連接,放棄了 GZip,這是 Nginx 的默認策略。
?
啟用 Nginx 的 HTTP/1.0 GZip 功能,使用 HTTP/1.0 請求報文測試:
?
返回的內容被壓縮了,連接也被斷開了,服務端返回了?Connection: close
。原因就是之前說過的,動態壓縮導致無法事先得知響應內容長度,在 HTTP/1.0 中只能依靠斷開連接來讓客戶端知道響應結束了。
?
使用 HTTP/1.1 請求報文測試:
請求報文是 HTTP/1.1 的,Nginx 能知道這個客戶端支持 HTTP/1.1 的?Transfer-Encoding: chunked
,于是通過分塊傳輸解決了所有問題:既啟用了壓縮,也啟用了持久連接。
那么,對于 HTTP/1.0 請求,我們是讓 Nginx 放棄持久連接好,還是放棄 GZip 好呢?
實際上,由于 HTML 文檔或 JSON 接口一般都是用 PHP、Node.js 等服務端語言動態輸出,即使不壓縮,Nginx 也無法事先得知它的?Content-Length
,在 HTTP/1.0 中無論如何都無法啟用持久連接,這時還不如啟用 GZip 省點流量。
?