关于Gzip

1,378 阅读5分钟

引言:之前看了网上很多资料,对于在webpack开启了gzip压缩后,服务端该如何设置以及其中的基本原理,很多文章都阐述得不大清楚,所以才有了这篇文章。

gzip是一种压缩文件格式并且也是一个在类 Unix 上的一种文件解压缩的软件。在基于 HTTP 协议的网络传输中,gzip是一种在万维网中加速传输HTML和其他内容的技术 *(gzip通过减小文件体积,去节省带宽和加快传输速度。)*它是在 RFC 2016 中规定的三种标准HTTP压缩格式之一。

gzip算法

gzip算法的核心是Deflate,DEFLATE是LZ77与哈夫曼编码的一个组合体。

LZ77的核心思路是如果一个串中有两个重复的串,那么只需要知道第一个串的内容和后面串相对于第一个串起始位置的距离 + 串的长度例如,ABCDEFGABCDEFH (经过该算法变成)→ ABCDEFG(7,6)H

这里不展开说明,对LZ77有兴趣的可以看看这篇文章

哈夫曼编码

哈夫曼编码通过构造 Huffman Tree 的方式给字符重新编码, 用较短的编码代替较常用的字母,用较长的编码代替较少用的字母,从而减少了文本的总长度。

步骤大致是:统计出现频率,并从小到大排序 → 以频率作为叶节点,频率最小的两个点组出底层的叶子节点,父节点为两者之和,以此自下而上构成一颗二叉树根据这棵二叉树来对文本进行编码,左分支当成 0,遇到右分支当成 1,从根节点访问各个字母。** 例如有4 个结点 a, b, c, d,权值分别为 7, 5, 2, 4会变成a -> 0,b-> 10 c-> 110, d->111,推演过程如下图:

哈夫曼算法

关于压缩

使用gzip 去压缩哪些文件

虽然gzip可以压缩所有的文件,但是这不代表我们要对所有文件进行gzip压缩。css,js之类的文件会有很好的压缩效果。

对于特定类型的文件来说,比如jpeg图片文件,已经是进行过压缩的了。有时候再次进行额外的压缩无助于负载体积的减小,反而有可能会使其增大。

由谁去压缩文件

  • 服务端响应请求时候压缩(实时压缩):当我们点击网页发送一个请求时候,服务端会找到对应的文件,然后对文件进行压缩,然后返回压缩后的内容。

    如果上游配有 nginx 转发处理层,最好交给 nginx 来处理这些,因为它们有专门为此构建的内容,可以更好的利用缓存并减小开销。

服务端压缩使用Nginx默认集成的ngx_http_gzip_module模块,该模块使用chunked编码动态压缩,nginx配置如下:

http {
  gzip  on; // 打开或者关闭gzip压缩的功能
  gzip_min_length  1k; // 被压缩响应的最小长度
  gzip_buffers     4 16k; // 压缩响应的缓冲区的数量(number)和大小(size)
  gzip_http_version 1.1; 
  gzip_comp_level 6;  //  压缩级别
  gzip_types       text/javascript application/javascript text/css;  // 针对指定的 MIME 类型启用 gzip 响应
  gzip_disable "MSIE [1-6]\.";
  gzip_vary on;
}

Nginx每次请求服务端都要压缩很久才回返回信息回来,不仅服务器开销会增大很多,请求方也会等的不耐烦。如果我们在 Webpack打包时就直接生成高压缩等级的文件,作为静态资源放在服务器上,是不是能提高效率呢?所以有了以下的应用构建时候压缩的方式。

  • 应用构建时候压缩:使用Nginx的ngx_http_gzip_static_module模块。

ngx_http_gzip_static_module模块使用的是静态编码,数据以*.gz作为后缀名存储在服务器上,如果客户端的浏览器支持压缩,将直接返回压缩后的数据。nginx配置如下:

location ~ .*\.(js|css)$ {
  gzip_static on;  
  gzip_proxied expired no-cache no-store private auth;
}

gzip_static可选值off | on | alwayson为开启并检查客户端浏览器是否支持gzip压缩功能,off为关闭,always一直发送gzip压缩文件,而不检查浏览器是否支持gzip压缩。

ngx_http_gzip_static_module模块是一个可选模块,需要使用--with-http_gzip_static_module指令进行编译 (可以使用nginx -V查看是否已安装)

应用构建时压缩的实现

流程梳理

  1. webpack开启gzip压缩,构建打包,生成压缩后的文件*.gz上传到服务器。
  2. 浏览器发送请求给服务器, 请求中有Accept-Encoding: gzip, deflate, br。 (告诉服务器,浏览器支持gzip压缩)
  3. 服务端找到对应压缩后文件返回(nginx开启静态压缩 gzip_static on
  4. 浏览器接收到数据后,根据Content-Encoding:gzip来对内容进行解码,然后显示出网页。

关于解压的浏览器兼容:基本不用考虑兼容性的问题,几乎所有浏览器都支持它。

webpack配置

const plugins = [
  ...
  new CompressionWebpackPlugin({
    asset: '[path].gz[query]', // 生成的资源名称
    algorithm: 'gzip', // 压缩算法
    test: /\.(js|css)$/, // 压缩资源匹配的正则
    threshold: 10240, // 只处理比这个值大的资源,示例为大于1K
    // 示例:一个1024b大小的文件,压缩后大小为768b,minRatio : 0.75
    minRatio: 0.8 // 只有压缩率比这个值小的资源才会被处理
  })
]

nginx配置

location ~ .*\.(js|css)$ {
  gzip_static on;  
  gzip_proxied expired no-cache no-store private auth;
}

验证成功开启gzip

通过查看response headersrequest headers,如下图: 验证gzip

题外话:关于Content-Encoding

Content-Encoding 是一个实体消息首部,用于对特定媒体类型的数据进行压缩。这个消息首部用来告知客户端应该怎样解码才能获取在 Content-Type 中标示的媒体类型内容,参数值为gzip | compress | deflate | identity | br

  • gzip:表示采用 Lempel-Ziv coding (LZ77) 压缩算法,以及32位CRC校验的编码方式。
  • compress:采用 Lempel-Ziv-Welch (LZW) 压缩算法。
  • deflate:采用 zlib 结构 和 deflate 压缩算法。
  • identity:用于指代自身(例如:未经过压缩和修改)。除非特别指明,这个标记始终可以被接受。
  • br: 表示采用 Brotli 算法的编码方式。

探索HTTP传输中gzip压缩的秘密

Content-Encoding

简单聊聊 GZIP 的压缩原理与日常应用

Nginx 之四: Nginx服务器的压缩功能和缓存功能

[译] 理解 zip 和 gzip 压缩格式背后的压缩算法