被本身觉得的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.

 

 

总结

最终也仍是本身错了,也没有什么好总结的

要是什么都不知道也不出问题,要是知道的很清楚也不会出问题,就是在“觉得本身知道”的状况下就各类问题。

相关文章
相关标签/搜索