官术网_书友最值得收藏!

4.3.2 傳輸壓縮

我們接下來討論鏈路優化中緩存、連接之外的另一個主要話題:壓縮。同時也是為了解決上一節遺留的問題:如何不以斷開TCP連接為標志來判斷資源已傳輸完畢。

HTTP很早就支持了GZip壓縮,因為HTTP傳輸的內容主要是文本數據,譬如HTML、CSS、Script等,而對于文本數據啟用壓縮的收益是非常高的,傳輸數據量一般會降至原有的20%左右。對于那些不適合壓縮的資源,Web服務器能根據MIME類型自動判斷是否對響應進行壓縮,這樣,已經采用過壓縮算法存儲的資源,如JPEG、PNG圖片,便不會被二次壓縮,空耗性能。

不過,大概就沒有多少人想過壓縮與之前提到的用于節約TCP的持久連接機制是存在沖突的。在網絡時代的早期,服務器處理能力還很薄弱,為了啟用壓縮,會把靜態資源先預先壓縮為.gz文件的形式存放起來,當客戶端可以接收壓縮版本的資源時(請求的Header中包含Accept-Encoding:gzip)就返回壓縮后的版本(響應的Header中包含Content-Encoding:gzip),否則返回未壓縮的原版,這種方式被稱為“靜態預壓縮”(Static Precompression)。而現代的Web服務器處理能力有了大幅提升,已經沒有人再采用麻煩的預壓縮方式了,都是由服務器對符合條件的請求在輸出時進行“即時壓縮”(On-The-Fly Compression),整個壓縮過程全部在內存的數據流中完成,不必等資源壓縮完成再返回響應,這樣可以顯著提高“首字節時間”(Time To First Byte,TTFB),改善Web性能體驗。而這個過程中唯一不好的地方就是服務器再也沒有辦法給出Content-Length這個響應Header了,因為輸出Header時服務器還不知道壓縮后資源的確切大小。

到這里,大家了解即時壓縮與持久連接的沖突在哪了嗎?持久連接機制不再依靠TCP連接是否關閉來判斷資源請求是否結束,它會重用同一個連接以便向同一個域名請求多個資源,這樣,客戶端就必須要有除了關閉連接之外的其他機制來判斷一個資源什么時候算傳遞完畢,這個機制最初(在HTTP/1.0時)就只有Content-Length,即依靠請求Header中明確給出資源的長度判斷,傳輸到達該長度即宣告一個資源的傳輸已結束。由于啟用即時壓縮后就無法給出Content-Length了,如果是HTTP/1.0的話,持久連接和即時壓縮只能二選一,事實上在HTTP/1.0中對兩者都支持,卻默認都是不啟用的。依靠Content-Length來判斷傳輸結束的缺陷,不僅僅在于即時壓縮這一種場景,譬如對于動態內容(Ajax、PHP、JSP等輸出),服務器也同樣無法事先得知Content-Length。

HTTP/1.1版本中修復了這個缺陷,增加了另一種“分塊傳輸編碼”(Chunked Transfer Encoding)的資源結束判斷機制,徹底解決了Content-Length與持久連接的沖突問題。分塊編碼的原理相當簡單:在響應Header中加入“Transfer-Encoding:chunked”之后,就代表這個響應報文將采用分塊編碼。此時,報文中的Body需要改為用一系列“分塊”來傳輸。每個分塊包含十六進制的長度值和對應長度的數據內容,長度值獨占一行,數據從下一行開始,最后以一個長度值為0的分塊來表示資源結束。舉個具體例子[1]


HTTP/1.1 200 OK
Date: Sat, 11 Apr 2020 04:44:00 GMT
Transfer-Encoding: chunked
Connection: keep-alive

25
This is the data in the first chunk

1C
and this is the second one

3
con

8

sequence

0

根據分塊長度可知,前兩個分塊包含顯式的回車換行符(CRLF,即\r\n字符)。


"This is the data in the first chunk\r\n"      (37 字符 => 十六進制: 0x25)
"and this is the second one\r\n"               (28 字符 => 十六進制: 0x1C)
"con"                                          (3  字符 => 十六進制: 0x03)
"sequence"                                     (8  字符 => 十六進制: 0x08)

所以解碼后的內容為:


This is the data in the first chunk
and this is the second one
consequence

一般來說,Web服務器給出的數據分塊大小應該(但并不強制)是一致的,而不是如例子中那樣隨意。HTTP/1.1通過分塊傳輸解決了即時壓縮與持久連接并存的問題,到了HTTP/2,由于多路復用和單域名單連接的設計,已經無須再刻意去提持久連接機制了,但數據壓縮仍然有節約傳輸帶寬的重要價值。

[1] 例子來自于維基百科,為便于觀察,只分塊,未壓縮。

主站蜘蛛池模板: 雷州市| 海南省| 都昌县| 山东| 武邑县| 诸暨市| 乡宁县| 莲花县| 洞头县| 盘锦市| 鹰潭市| 榆树市| 香港| 木兰县| 桐城市| 二连浩特市| 汶上县| 石台县| 沙雅县| 许昌县| 呼伦贝尔市| 宜章县| 松阳县| 易门县| 上林县| 深州市| 南华县| 泰和县| 宁强县| 鄂托克前旗| 千阳县| 平乡县| 张家界市| 石城县| 芦溪县| 丽水市| 晋中市| 天门市| 神木县| 兰溪市| 静乐县|