orangeyyy
12/30/2017 - 7:53 AM

Gzip

概述

Gzip通常指GNU计划实现的文件压缩程序,所以是GNUzip的缩写。在http协议中通过gzip来进行文件压缩,以减少资源在传输过程中所占用的带宽,提升网站性能,一般通过gzip压缩的文件只有原始大小的1/4,不过对于JPG/PNG 这类本身已经高度压缩的二进制文件,不建议使用内容压缩,效果微乎其微还浪费CPU。目前主流的浏览器,chrome、safari、firefox和IE都已经支持了gzip,常见的服务器如apache、Nginx、IIS也都支持了gzip,所以通过gzip进行文件压缩已经成为现在比较通用的资源传输方式。

浏览器中的使用

通过上图我们可以清晰的看到在浏览器和服务端之间是如何使用gzip的:

  • 浏览器请求URL,并在request header中设置属性accept-encoding:gzip,来告诉服务端浏览器支持gzip;
  • 服务端在收到浏览器的请求后根据request header来判断浏览器是否支持gzip,如果支持,则项浏览器传送通过gzip压缩的内容,并在response header中设置content-encoding:gzip,告诉浏览器内容已通过gzip压缩。反之,若浏览器不支持gzip,则直接返回未经压缩的内容。
  • 浏览器在接收到服务器的响应后根据response header判断内容是否经过压缩,若经过压缩,则进行解压并使用解压后的内容;

需要注意的是压缩只针对传输正文,在http/1中,头部始终通过ASCII编码进行传输,没有经过任何压缩,不过在http/2中得到了解决,可以参考HTTP/2 头部压缩技术介绍

官方文档中对accept-encoding和content-encoding做了如下解释:

  • accept-encoding:这个request header 属性用于表明哪种文件编码(通常为一种压缩算法)客户端可以理解。通过内容协商机制,服务端选择一种协议来响应请求,并通过content-encoding来告诉客户端使用了哪种协议;
  • content-encoding:用于告诉客户端响应的内容使用了哪种编码方式,同时它告诉浏览器如何通过解码获取Content-type中指定的媒体资源;

accept-encoding可以设置如下值:

  • gzip:是通过Lempel-Ziv coding(LZ77)压缩并使用32位循环冗余校验(CRC)的文件格式;

  • compress:是通过Lempel-Ziv-Welch(LZW)算法压缩的文件格式;

  • deflate:通过deflate压缩算法压缩,并使用zlib结构的文件格式,主要归咎于HTTP/1.1对deflate的错误命名导致这个地方含糊不清,应该理解为包含使用Lempel-Ziv压缩算法(LZ77)和哈夫曼编码的DEFLATE数据流的ZLIB数据格式;

  • br:使用Brotli算法压缩的文件格式;

  • indentity:设置一致性方法(例如:no compression、nor modification),默认值为acceptable,在这种情况下,就算客户端和服务端都支持同一种压缩方式,服务端也会因为一下原因而不压缩并直接返回:

    • 数据已经进行过压缩,再次进行压缩并不会有太大作用,例如图片格式;
    • 服务端已经过载了,已经没有资源来支持压缩计算;
    • :匹配所有的编码格式,这个是accept-encoding的默认值,但并不代表所有的算法都是支持的,仅代表无优先级区分;
  • ;q=(weight):表明优先级,weight为权重;

常用库通过gzip压缩的效果:

|包名|原始大小|gzip压缩大小|压缩比| |-|-|-| |react15|23K|8K|34.8%| |react-dom15|130K|39K|30%| |react16|5K|2.6K|52%| |react-dom16|93K|29.4K|31.6%| |react-router|22K|7.2K|32.7%| |vue|87K|31K|35.6%| |preact|8K|3.7K|46.5%| |babel-polyfill|67K|18.5K|27.6%|

原理详解

通过上面的介绍,如果要了解原理,首先得了解下面几个内容

Lempel-Ziv(LZ77)

LZ77与LZ78是Abraham Lempel与Jacob Ziv在1977年以及1978年发表的论文中的两个无损数据压缩算法。这两个算法是大多数LZ算法变体如LZW、LZSS以及其它一些压缩算法的基础。与最小冗余编码器或者进程长度编码器不同,这两个都是基于字典的编码器。 在了解LZ77原理之前,需要先了解几个概念:

  • lookahead buffer:待编码区;
  • search buffer:已编码的区域,搜索缓冲区;
  • 滑动窗口: 指定窗口大小,包含“搜索缓冲区” + “待编码区”

现在我们来看一下压缩的具体过程:

  1. 设置编码位置为输入流的开始;
  2. 在滑动窗口的待编码区查找搜索区中的最大匹配字符串;
  3. 如果找到字符串,输出(偏移值,匹配长度),窗口向右滑动“匹配长度”;
  4. 如果没有找到,输出(0,0,待编码区的第一个字符),窗口向右滑动一个单位;
  5. 如果待编码区不为空,回到步骤2;

gzip 中使用固定窗口大小,一般为32KB;

循环冗余校验码(CRC)

循环冗余校验是一种根据网络数据包或电脑文件等数据产生的简短固定位数校验码的一种散列函数,主要用来检验或校验数据传输或者保存后可能出现的错误。

奇偶校验

先从最简单的奇偶校验说起吧,所谓奇偶校验就是在发送的每一个字节后面加上一位,使得每个字节中1的个数为基数或是偶数。奇偶校验的优点是非常简单,可以通过硬件实现,减少软件负担,但是缺点也是很明显的,它的错误检查率只有50%,而且每个字节都需要增加一位,对传输效率有影响。

累加和校验

累加和的校验方式有很多种,最常见的一种是在一次通讯包的最后加入一个字节的校验数据,这个字节内容为前面数据包中全部数据的忽略进位的按字节累加和。累加和方式也很简单,而且得到广泛应用,但是它的验错概率也比较一般。

CRC

终于到了重点CRC了,CRC的基本思想是将传输数据当做一个位数很长的数,将这个数除以另外一个数,得到的余数作为校验数据,附加到源数据后面。

在除法计算过程中遇到加减的情况,直接使用异或运算来取代,这样就不需要考虑借位问题,除法要更加简单一些,在除法计算中使用是除数有一个专有名词叫“生成多项式”,生成多项式对于错误的检查率至关重要,专家们经过多年研究,总结下面常用的生成多项式如下:

CRC8=X8+X5+X4+X0 CRC-CCITT=X16+X12+X5+X0 CRC16=X16+X15+X2+X0 CRC12=X12+X11+X3+X2+X0 CRC32=X32+X26+X23+X22+X16+X12+X11+X10+X8+X7+X5+X4+X2+X1+X0

需要注意的是:

  • 文献中提到生成多项式经常会说到多项式的位宽,这个位宽不是多项式对于的二进制位数,而是位数减1,例如CRC8中用到位宽为8的多项式,但是其实对于的二进制是9位:100110001;
  • 使用多项式进行二进制表达比较繁琐,因此文献中一般用16进制写法来表示,因为多项式的最高位肯定为1,最高位的值由位宽可知,所以在简记方式中统一将最好的1去掉,如CRC32的生成多项式简记为:04C11DB7;
  • 按照CRC的计算要求,计算前要在原始数据后面填上W个0,W为生成多项式的位宽,由此可以证明奇偶校验是CRC校验的一种特例,它的位宽为1;

gzip 中使用的是CRC-32;

参考