被本身覺得的GZIP秀到了

 

問題的開始

 我司某產品線有這麼一個神奇接口 (https://host/path/customQuery)html

該接口在預發或線上緩存正常的狀況下TTFB爲150ms左右(能夠認爲服務處理時間差很少就是TTFB),不過相比150ms的TTFB,顯然數據資源下載時間過長的問題會更引人注意須要100ms左右(固然這也是網絡條優秀的狀況下,網絡通常的話這個下載時間會更誇張)
customQuery請求一次請求的數據響應大概爲2.7MB, 壓縮後也有超過300KB
下載時間過長看起來就是由於這個響應實體過大了(100Mb的帶寬滿速,300KB差很少也須要30ms),經過測試能夠發現一樣的網絡條件同一個應用的其餘接口,若是響應壓縮後小於1KB,其ContentDownLoad時間能夠忽略不計(一般都會小於2ms)
由於代理默認開啓了gzip,其實數據已經被壓縮了近10倍,可是壓縮後的數據仍是過大。
分析了customQuery響應實體的數據結構。
發現數據每一個list中fields節點大量重複出現。算法

如上圖其中field的描述是徹底一致的(按一頁50條計算,這些數據重複了50遍)
這些數據field描述數據單個都大小大概是50KB(重複50次能夠看到2.7MB的數據幾乎都是這些重複的數據)瀏覽器

 

開始秀了

既然已經明確了這些重複描述數據,服務端的同窗很天然想到把這些field描述提取出來從新組裝數據能夠大幅度減少數據傳輸的大小。
不過本身剛好曾經「看過」DEFLATE壓縮(http的gzip正好使用的是DEFLATE)其中使用到的LZ77是會匹配前文相同短語後面的相同短語都會被替換成「標記」。
那我「秀」的時候又到了,立即表示採用這種數據重組的方式並不會帶來明顯的實際提高,由於數據實際的信息量沒有實際變化,只是手動去除了冗餘,而以前冗餘的數據其實已經被gzip處理過了,因此僅僅單純去除重複描述數據片斷並不能帶來預期的收益。
由於我秀的時候如此自信,對方立刻就本身不自信了,表示要回去先驗證效果後在作打算。緩存

 

看起來是失敗了

果真後面的結果「竟然」是我被打臉了網絡

 

 

 customQuery接口返回的實體大小直接變成了25kb,解壓後189kb(以前是327kb,解壓後2.7Mb)數據結構

那這差距太大了,實體大小減少到了以前的10%不到,固然下載速度ContentDownLoad也有了大幅度的下降。(基本上就是一個RTT的時間)
不過這徹底跟我以前的認知不同啊,必定是哪裏出現了問題。(畢竟是覺得本身懂了系列)app

 

試圖搶救下

爲了挽回顏面,我把這2組原始數據下載下來,本地壓縮進行分析(還不想認可本身錯了,試圖找到產生這種結果的其餘解釋)測試

以下圖老的數據爲customQuery_v1(2.7MB),新的爲customQuery_v2(190KB)編碼

分別使用zip,gzip,rar對2組數據進行壓縮 (gzip即爲http默認使用的壓縮算法,MAC上直接使用gzip命令能夠對文件進行壓縮)
能夠發現RAR的壓縮結果就與我最開始的想法差很少(即便原始數據差了超過10倍,而壓縮的結果是幾乎一致的,v1爲19kb ;v2爲17kb)
不過gzip對2組數據的壓縮結果與在瀏覽器上看到的是同樣的。(v1爲329kb ;v2爲25kb)
既然本地壓縮也獲得了一樣的結果,看來真的是本身Too young too naive (大意了,沒有閃,秀的時候應該先在本地驗證一下的)spa

 

默默面對錯誤分析緣由

可是爲何會有這樣的結果,按個人理解壓縮結果應該與rar一致纔對。要搞清楚還要從壓縮的方式入手。
必定是我覺得的壓縮行爲與實際存在差別,gzip的基礎是DEFLATE,DEFLATE是LZ77哈夫曼編碼的一個組合體( https://tools.ietf.org/html/rfc1951
Huffman Coding 只是單純的字符編碼,編碼後的大小與編碼前的大小直接正相關,確定不是產生結果的緣由。
那剩下就只有是LZ77,只能是LZ77一開始沒有把那些重複的fields壓縮掉,而爲何LZ77沒有把原始數據裏大量重複的描述「標記」起來。
LZ77總體是是使用已經出現過的相應匹配數據信息替換當前數據從而實現壓縮功能,爲了匹配數據須要用到了「滑動窗口」的概念
細細一品,LZ77並非全文匹配,數據爲了能夠邊發送邊壓縮會進行分塊壓縮。經過查閱RFC文檔,大概能夠明確塊的大小被限制在64k內,最大滑動窗口就是64k/2=32k,而且還要求「標記」的最大長度爲256字節(固然標記長度這個問題不大,大不了很少用幾個標記)。這裏的問題在於使用滑動窗口就要求重複的數據必需要「相鄰」 而塊大小最大爲64K,若是重複的2段數據不能出如今一個窗口內是不能被標記的。可是窗口最可能是塊大小的一半32Kb(實際也不會用這麼大的窗口),而咱們以前就計算過咱們重複的單個field描述就有50Kb,要出現有2個重複的內容,即便2個描述相鄰那也至少上100Kb(他們甚至都沒法在同一個塊裏),實際上窗口最大32Kb,因此LZ77根本不能標記出這些重複的field。

如下引至https://tools.ietf.org/html/rfc1951#section-2

Compressed representation overview

A compressed data set consists of a series of blocks, corresponding
to successive blocks of input data. The block sizes are arbitrary,
except that non-compressible blocks are limited to 65,535 bytes.

Each block is compressed using a combination of the LZ77 algorithm
and Huffman coding. The Huffman trees for each block are independent
of those for previous or subsequent blocks; the LZ77 algorithm may
use a reference to a duplicated string occurring in a previous block,
up to 32K input bytes before.

Each block consists of two parts: a pair of Huffman code trees that
describe the representation of the compressed data part, and a
compressed data part. (The Huffman trees themselves are compressed
using Huffman encoding.) The compressed data consists of a series of
elements of two types: literal bytes (of strings that have not been
detected as duplicated within the previous 32K input bytes), and
pointers to duplicated strings, where a pointer is represented as a
pair <length, backward distance>. The representation used in the
"deflate" format limits distances to 32K bytes and lengths to 258
bytes, but does not limit the size of a block, except for
uncompressible blocks, which are limited as noted above.

Each type of value (literals, distances, and lengths) in the
compressed data is represented using a Huffman code, using one code
tree for literals and lengths and a separate code tree for distances.
The code trees for each block appear in a compact form just before
the compressed data for that block.

 

 

總結

最終也仍是本身錯了,也沒有什麼好總結的

要是什麼都不知道也不出問題,要是知道的很清楚也不會出問題,就是在「覺得本身知道」的狀況下就各類問題。

相關文章
相關標籤/搜索