從 Nginx 默認不壓縮 HTTP/1.0 提及

轉自:  https://imququ.com/post/why-nginx-disable-gzip-in-http10.html?hmsr=toutiao.io&utm_medium=toutiao.io&utm_source=toutiao.iohtml

 

從 Nginx 默認不壓縮 HTTP/1.0 提及

臨近年關,明顯變忙,博客也更新得慢了,之後儘可能保證周更吧。今天這篇文章屬於計劃以外的更新,源自於白天看到的《一個基於 http 協議的優化》。在這篇文章中,做者描述了這樣一個現象:nginx

在 移動的 http 請求量和聯通不相上下的前提下,移動的 http response 帶來的網絡流量是聯通的 2.5 倍。移動大概有 3 成的請求都沒有作壓縮,而聯通幾乎都是通過壓縮的。那些沒有通過壓縮的 http 會話都是走了 1.0 的協議,相反通過壓縮的 http 會話都是走了 http1.1 協議。瀏覽器

也就是說在相同的服務端配置下,移動運營商過來的流量中有 30% 走了 HTTP/1.0,而做者所使用的 HTTP Server,不對 HTTP/1.0 響應啓用 GZip。性能優化

爲何在移動運營商網絡下會有這麼高比例的 HTTP/1.0 請求,本文按下不表,總之這必定是移動的緣由。直接看另一個問題,也就是本文標題所寫:Nginx 爲何默認不壓縮 HTTP/1.0?網絡

那篇文章的做者並無說明他用什麼 HTTP Server,我這裏直接當成 Nginx 好了。後面會發現這個問題跟 HTTP 協議有關,全部 HTTP Server 都會面臨。post

在 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 改一下配置就能夠支持,爲何它默認不開啓?ui

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。因而,前面這種狀況只能要麼不壓縮,要麼不啓用持久鏈接(對於非持久鏈接,TCP 斷開就能夠認爲 HTTP 報文結束),而 Nginx 默認選擇的是前者。

那麼在 HTTP/1.1 中,這個問題解決了嗎?固然!我在以前的文章中講過,HTTP/1.1 新增的 Transfer-Encoding: chunked 所對應的分塊傳輸機制能夠完美解決這類問題。有興趣的同窗能夠查看個人這篇文章:HTTP 協議中的 Transfer-Encoding

理論知識先寫到這裏,最後用實踐來驗證一下:

首先,不啓用 Nginx 的 HTTP/1.0 GZip 功能,使用 HTTP/1.0 請求報文測試:

http/1.0 without gzip

能夠看到,儘管個人請求報文中指明瞭能夠接受 GZip,可是返回的內容依然是未壓縮的;同時服務端響應了 Content-LengthConnection: keep-alive,鏈接並無斷開。也就是說 Nginx 爲了儘量啓用持久鏈接,放棄了 GZip,這是 Nginx 的默認策略。

而後,啓用 Nginx 的 HTTP/1.0 GZip 功能,使用 HTTP/1.0 請求報文測試:

http/1.0 with gzip

能夠看到,此次的請求報文與上次徹底同樣,可是結果大相徑庭:雖然返回的內容被壓縮了,可是鏈接也被斷開了,服務端返回了 Connection: close。緣由就是以前說過的,動態壓縮致使沒法事先得知響應內容長度,在 HTTP/1.0 中只能依靠斷開鏈接來讓客戶端知道響應結束了。

最後,使用 HTTP/1.1 請求報文測試:

http/1.1 with gzip

能夠看到,因爲請求報文是 HTTP/1.1 的,Nginx 能知道這個客戶端能夠支持 HTTP/1.1 的 Transfer-Encoding: chunked,因而經過分塊傳輸解決了全部問題:既啓用了壓縮,也啓用了持久鏈接。

那麼,對於 HTTP/1.0 請求,咱們是讓 Nginx 放棄持久鏈接好,仍是放棄 GZip 好呢?

實際上,因爲 HTML 文檔通常都是使用 PHP、Node.js 等動態語言輸出,即便不壓縮,Nginx 也沒法事先得知它的 Content-Length,在 HTTP/1.0 中橫豎都沒法啓用持久鏈接,這時還不如啓用 GZip 省點流量。

對 於 JS、CSS 等事先能夠知道大小的靜態文本文件,個人建議是,移動端首次訪問把重要的 JS、CSS 都內聯在 HTML 中,而後存在 localStorage 裏,後續不輸出;不重要的 JS、CSS 外鏈並啓用 GZip,犧牲 keep-alive 來達到減小流量的目的。

本文先寫到這裏,歡迎來博客原文進行評論、交流。瀏覽器的 GZip 其實還有不少有趣的小故事,先賣個關子,之後有空再寫。

本文連接:https://imququ.com/post/why-nginx-disable-gzip-in-http10.html參與評論

--EOF--

相關文章
相關標籤/搜索