目前我们大多数网站都是启用的 gzip 来进行 资源传输的压缩,这有利于我们更加快速的访问我们的网站;但是除了 gzip 外,我们或许还有别的选项,而且性能会更好,比如 Facebook 便是启用的 Brotli 算法;
Brotli
Brotli 最初发布于2015年,用于网络字体的离线压缩。Google软件工程师在2015年9月发布了包含通用无损数据压缩的Brotli增强版本,特别侧重于HTTP压缩。新版本还展现了跨平台的性能改进,以及减少解码所需的内存。
与常见的通用压缩算法不同,Brotli使用一个预定义的120千字节字典。该字典包含超过13000个常用单词、短语和其他子字符串,这些来自一个文本和HTML文档的大型语料库。预定义的算法可以提升较小文件的压缩密度。
对全球1000个访问量比较大的地址进行测试,使用 Brotli 算法可以得到明显的性能改善:
- 14% smaller than gzip for JavaScript
- 21% smaller than gzip for HTML
- 17% smaller than gzip for CSS
使用brotli取代deflate来对文本文件压缩通常可以增加20%的压缩密度,而压缩与解压缩速度则大致不变。
当然如同 HTTP2, 我们还需要考虑浏览器的兼容性:
因此我们在设计自己程序还需要考虑到对于不支持 Brotli 压缩的进行 gzip 的降级处理;
Nginx 启用 Brotli
你可以参考 这篇文章 实现 ngx_brotli 的下载安装;
ngx_brotli 配置有如下指令:
- brotli 是否启用 Brotli on 为开启 off 为关闭
- brotli_types 允许的压缩 mime 类型比如 text/css, text/javascript
- brotli_comp_level 压缩级别 可选值范围为0~11,默认值为6
配置参考
http {
...
gzip on;
gzip_vary on;
gzip_min_length 1024;
gzip_buffers 128 32k;
gzip_comp_level 6;
gzip_http_version 1.1;
gzip_proxied expired no-cache no-store private auth;
gzip_types text/plain text/css text/xml application/xml application/json text/javascript application/javascript application/x-javascript;
brotli on;
brotli_types text/plain text/css text/xml application/xml application/json text/javascript application/javascript application/x-javascript;
brotli_static off;
brotli_comp_level 11;
brotli_buffers 16 8k;
brotli_window 512k;
brotli_min_length 20;
...
}
改完之后可以记得 重启 nginx 查看 header 头是不是有这样的值:
Content-Encoding: br
Node.js 中使用
npm i iltorb zlib accepts --save
我们以 egg.js 中间件为案例。
const isJSON = require('koa-is-json')
const accepts = require('accepts')
const zlib = require('zlib')
const brotli = require('iltorb')
module.exports = options => {
return async function gzip (ctx, next) {
await next()
let body = ctx.body
if (!body) return
const encodings = new Set(accepts(ctx.req).encodings())
// 支持 options.threshold
if (options.threshold && ctx.length < options.threshold) return
if (isJSON(body)) {
body = JSON.stringify(body)
}
// 判断 accept 是否支持 brotli 压缩
if (encodings.has('br')) {
const stream = brotli.compressStream()
stream.flush = function () {}
stream.end(body)
ctx.body = stream
ctx.set('Content-Encoding', 'br')
} else {
// 设置 gzip body,修正响应头
const stream = zlib.createGzip()
stream.end(body)
ctx.body = stream
ctx.set('Content-Encoding', 'gzip')
}
}
}
记得在配置中启用这个中间件。
还有一点需要提醒大家,brotli 压缩只能在 https 中生效,因为 在 http 请求中 request header 里的
Accept-Encoding: gzip, deflate 是没有 br 的。