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

为何要开启gZip

图片描述

咱们给某人发送邮件时,咱们在传输以前把本身的文件压缩一下,接收方收到文件后再去解压获取文件。这中操做对于咱们来讲都已经司空见惯。咱们压缩文件的目的就是为了把传输文件的体积减少,加快传输速度。咱们在 http 传输中开启 gZip 的目的也是如此,可是通常文章介绍 gZip 时候老是结合一些服务端配置(nginx)或者构建工具插件(webpack)来讲,列出一大堆配置让人看的云里雾里,以致于到最后还没搞懂 为何用怎么用 这些问题。javascript

http 与 gZip

咱们下面去探讨一下这些问题css

gZip 文件怎么通信

咱们传输压缩文件给别人时候通常都带着后缀名 .rar, .zip之类,对方在拿到文件后根据相应的后缀名选择不一样的解压方式而后去解压文件。咱们在 http 传输时候解压文件的这个角色的扮演者就是咱们使用的浏览器,可是浏览器怎么分辨这个文件是什么格式,应该用什么格式去解压呢?html

http/1.0 协议中关于服务端发送的数据能够配置一个 Content-Encoding 字段,这个字段用于说明数据的压缩方法前端

Content-Encoding: gzip
Content-Encoding: compress
Content-Encoding: deflate

客户端在接受到返回的数据后去检查对应字段的信息,而后根据对应的格式去作相应的解码。客户端在请求时,能够用 Accept-Encoding 字段说明本身接受哪些压缩方法。vue

Accept-Encoding: gzip, deflate


咱们在浏览器的控制台中能够看到请求的相关信息java

图片描述

兼容性

提到浏览器做为一个前端就不禁自主的会想一个问题,会不会有浏览器不支持呢。HTTP/1.0 是1996年5月发布的。好消息是基本不用考虑兼容性的问题,几乎全部浏览器都支持它。值得一提的是 ie6的早起版本中存在一个会破坏 gZip的错误,后面 ie6自己在 WinXP SP2 中修复了这个问题,并且用这个版本的用户数量也不多。node

谁去压缩文件

这件事看起来貌似只能服务端来作,咱们在网上看到最多的也是诸如 nginx 开启 gZip 配置之类的文章,可是如今前端流行 spa 应用, 用 react, vue 之类的框架时候总伴随这一套本身的脚手架,通常用 webpack 做为打包工具,其中能够配置插件 如compression-webpack-plugin 可让咱们把生成文件进行 gZip 等压缩并生成对应的压缩文件,而咱们应用在构架时候有可能也会在服务区和前端文件中放置一层 node 应用来进行接口鉴权和文件转发。nodejs中咱们熟悉的express 框架中也有一个compression 中间件,能够开启gZip,一时间看的人眼花缭乱,到底应该用谁怎么用呢?react

服务端响应请求时候压缩

其实 nginx 压缩和 node 框架中用中间件去压缩都是同样的,当咱们点击网页发送一个请求时候,咱们的服务端会找到对应的文件,而后对文件进行压缩返回压缩后的内容【固然能够利用缓存减小压缩次数】,并配置好咱们上面提到的 Content-Encoding 信息。对于一些应用在构架时候并无上游代理层,好比服务端就一层 node 就能够直接用本身自己的压缩插件对文件进行压缩,若是上游配有有 nginx 转发处理层,最好交给 nginx 来处理这些,由于它们有专门为此构建的内容,能够更好的利用缓存并减少开销(不少使用c语言编写的)。linux

咱们看一些 nginx 中开启 gZip 压缩的一部分配置webpack

# 开启gzip
gzip on;
# 启用gzip压缩的最小文件,小于设置值的文件将不会压缩
gzip_min_length 1k;
# gzip 压缩级别,1-10,数字越大压缩的越好,也越占用CPU时间,后面会有详细说明
gzip_comp_level 2;
# 进行压缩的文件类型。javascript有多种形式。其中的值能够在 mime.types 文件中找到。
gzip_types text/plain application/javascript application/x-javascript text/css application/xml text/javascript;
应用构建时候压缩

既然服务端均可以作了为何 webpack 在打包前端应用时候还有这样一个压缩插件呢,咱们能够在上面 nginx 配置中看到 gzip_comp_level 2 这个配置项,上面也有注释写道 1-10 数字越大压缩效果越好,可是会耗费更多的CPU和时间,咱们压缩文件除了减小文件体积大小外,也是为了减小传输时间,若是咱们把压缩等级配置的很高,每次请求服务端都要压缩好久才回返回信息回来,不只服务器开销会增大不少,请求方也会等的不耐烦。可是如今的 spa 应用既然文件都是打包生成的,那若是咱们在打包时候就直接生成高压缩等级的文件,做为静态资源放在服务器上,接收到请求后直接把压缩的文件内容返回回去会怎么样呢?

webpackcompression-webpack-plugin 就是作这个事情的,配置起来也很简单只须要在装置中加入对应插件,简单配置以下

const CompressionWebpackPlugin = require('compression-webpack-plugin');

webpackConfig.plugins.push(
    new CompressionWebpackPlugin({
      asset: '[path].gz[query]',
      algorithm: 'gzip',
      test: new RegExp('\\.(js|css)$'),
      threshold: 10240,
      minRatio: 0.8
    })
)

webpack 打包完成后生成打包文件外还会额外生成 .gz 后缀的压缩文件

图片描述

那么这个插件的压缩等级是多少呢,咱们能够在源码中看到默认的 level9

...
const zlib = require('zlib');
this.options.algorithm = zlib[this.options.algorithm];
...
this.options.compressionOptions = {
    level: options.level || 9,
    flush: options.flush
    ...
}

能够看到压缩使用的是 zlib 库,而 zlib 分级来讲,默认是 6 ,最高的级别就是9 Best compression (also zlib.Z_BEST_COMPRESSION),由于咱们只有在上线项目时候才回去打包构建一次,因此咱们在构建时候使用最高级的压缩方式压缩多耗费一些时间对咱们来讲根本没任何损耗,而咱们在服务器上也不用再去压缩文件,只须要找到相应已经压缩过的文件直接返回就能够了。

服务端怎么找到这些文件

在应用层面解决这个问题仍是比较简单的,好比上述压缩文件会产生index.css, index.js的压缩文件,在服务端简单处理能够判断这两个请求而后给予相对应的压缩文件。以 nodeexpress 为例

...
app.get(['/index.js','/index.css'], function (req, res, next) {
  req.url = req.url + '.gz'
  res.set('Content-Encoding', 'gzip')
  res.setHeader("Content-Type", generateType(req.path)) // 这里要根据请求文件设置content-type
  next()
})

上面咱们能够给请求返回 gZip 压缩后的数据了,固然上面的局限性太强也不可取,可是对于处理这个方面需求也已经有不少库存在,expressexpress-static-gzip 插件 koakoa-static 则默认自带对 gZip 文件的检测,基本原理就是对请求先检测 .gz后缀的文件是否存在,再去根据结果返回不一样的内容。

哪些文件能够被 gZip 压缩

gZip 能够压缩全部的文件,可是这不表明咱们要对全部文件进行压缩,咱们写的代码(css,js)之类的文件会有很好的压缩效果,可是图片之类文件则不会被 gzip 压缩太多,由于它们已经内置了一些压缩,一些文件(好比一些已经被压缩的像.zip文件那种)再去压缩可能会让生成的文件体积更大一些。固然已经很小的文件也没有去压缩的必要了。

实践

能开启 gZip 确定是要开启的,具体使用在请求时候实时压缩仍是在构建时候去生成压缩文件,就要看本身具体业务状况。

参考资料

相关文章
相关标签/搜索