3. Brotli实战:Brotli vs Gzip性能对比、浏览器兼容性、服务端配置、预压缩策略

聊到资源压缩,很多人第一反应就是 Gzip。确实,Gzip 统治了 Web 压缩领域快二十年。但今天我要跟你聊聊它的继任者——Brotli。说实话,我第一次接触 Brotli 是在 Google 的某次技术分享上,当时觉得这名字挺怪,试过之后才发现,这玩意儿是真能打。

一句话总结:Brotli 不是 Gzip 的替代品,而是 Gzip 的进化版。它用更复杂的算法,换来了更高的压缩率。

3.1 Brotli vs Gzip:到底差在哪?

先看一组我实际项目中的压测数据。一个 300KB 的 jQuery 库,Gzip 压缩后大概 100KB,Brotli 能压到 85KB 左右。别小看这 15KB,在移动端弱网环境下,这就是 200ms 的差距。

对比维度 Gzip Brotli
压缩算法 Deflate (LZ77 + Huffman) LZ77 + 上下文建模 + 熵编码
压缩比(文本) 约 3:1 约 4:1 ~ 5:1
压缩速度 快(级别 6 约 50MB/s) 慢(级别 11 约 5MB/s)
解压速度 快(约 250MB/s) 快(约 200MB/s)
字典支持 无 内置静态字典(HTML/JS/CSS 专用)
浏览器支持 所有浏览器 Chrome/Firefox/Edge/Safari 11+

为什么会这样?Brotli 内置了一个针对 Web 资源的静态字典。你想想看,HTML 里的 <div>、<span>,JS 里的 function、return,这些高频词根本不需要重复编码。Gzip 是每次从头开始建字典,Brotli 直接拿现成的用。

我的建议:静态资源(JS/CSS/HTML)用 Brotli 级别 5-6 就够了。级别 11 压缩率只高 2-3%,但压缩时间翻了好几倍,不适合线上实时压缩。

3.2 浏览器兼容性:别踩坑

嗯,这里要注意。Brotli 虽然好,但不是所有浏览器都认。我去年帮一个客户做性能优化,全站上了 Brotli,结果发现他们的用户还有不少在用 Safari 10。那叫一个尴尬。

目前主流浏览器的支持情况:

  • Chrome 49+:完全支持
  • Firefox 44+:完全支持
  • Edge 15+:完全支持
  • Safari 11+:支持(但只支持 HTTPS)
  • Opera 36+:完全支持
  • IE 11:不支持

我曾经踩过的坑:Safari 在 HTTP 协议下不会发送 Accept-Encoding: br,只认 HTTPS。如果你还在用 HTTP,Brotli 对 Safari 用户就是白费功夫。

最佳实践是内容协商。服务端根据请求头 Accept-Encoding 决定返回哪种压缩格式:

# 请求头
Accept-Encoding: gzip, deflate, br

# 服务端响应
Content-Encoding: br
Content-Type: text/javascript
Vary: Accept-Encoding

注意那个 Vary 头。少了它,CDN 缓存可能会乱套。我见过一个案例,CDN 把 Brotli 版本缓存给了不支持 Brotli 的浏览器,结果页面直接白屏。

3.3 服务端配置:Nginx 实战

我个人习惯用 Nginx,配置起来最顺手。先确保你安装了 Brotli 模块:

# 编译安装
./configure --with-compat --add-module=/path/to/ngx_brotli
make && make install

# 或者用动态模块
load_module modules/ngx_http_brotli_filter_module.so;
load_module modules/ngx_http_brotli_static_module.so;

然后配置压缩规则:

http {
    # 开启 Brotli
    brotli on;
    brotli_comp_level 6;
    brotli_types text/plain text/css application/json application/javascript
                application/xml application/xml+rss text/javascript
                image/svg+xml application/vnd.ms-fontobject
                application/x-font-ttf font/opentype;

    # 保留 Gzip 作为降级
    gzip on;
    gzip_comp_level 5;
    gzip_types text/plain text/css application/json application/javascript
               application/xml application/xml+rss text/javascript;
    
    # 重要:根据客户端能力选择
    gzip_vary on;
    brotli_vary on;
}

避坑指南:不要把 brotli_comp_level 设到 11。我在生产环境试过,CPU 直接飙到 80%,QPS 掉了一半。级别 6 是性价比最高的选择。

3.4 预压缩策略:让性能再飞一会儿

实时压缩虽然灵活,但每次请求都要算一遍,浪费 CPU。预压缩就是提前把文件压好,直接存两份——一份原文件,一份 .br 文件。

我常用的做法是在 CI/CD 流程里加一步:

# 使用 brotli 命令行工具
brotli -q 6 -i dist/app.js -o dist/app.js.br
brotli -q 6 -i dist/style.css -o dist/style.css.br

# 或者用 Node.js 脚本
const zlib = require('zlib');
const fs = require('fs');

const input = fs.readFileSync('dist/app.js');
const compressed = zlib.brotliCompressSync(input, {
    params: {
        [zlib.constants.BROTLI_PARAM_QUALITY]: 6
    }
});
fs.writeFileSync('dist/app.js.br', compressed);

Nginx 静态模块会自动匹配:

location /static/ {
    brotli_static on;  # 优先使用 .br 文件
    gzip_static on;    # 降级使用 .gz 文件
    expires 365d;
    add_header Cache-Control "public, immutable";
}

这样配置后,Nginx 会先找 .br 文件,找不到再找 .gz,都没有才实时压缩。性能开销几乎为零。

核心收益:预压缩 + 静态模块,CPU 占用降低 90% 以上,响应时间稳定在 5ms 以内。我之前的项目从实时压缩切到预压缩后,服务器负载直接降了一半。

3.5 整体流程一览

下面这张图展示了 Brotli 从构建到交付的完整链路。我建议你对照着看,理解每一步的作用。

Brotli 预压缩与交付流程 构建产物 JS / CSS / HTML 预压缩(CI/CD) brotli -q 6 → .br 静态文件存储 app.js + app.js.br 客户端请求 Accept-Encoding: br, gzip Nginx 内容协商 brotli_static → gzip_static 响应返回 Content-Encoding: br 关键配置点 1. brotli_static on; 优先使用预压缩 .br 文件 2. gzip_static on; 作为降级方案 3. Vary: Accept-Encoding 保证 CDN 缓存正确 性能收益 预压缩后 CPU 占用降低 90%+,响应时间稳定在 5ms 以内

3.6 实战检查清单

最后,给你一份我每次上线 Brotli 都会过的检查清单:

  1. 确认浏览器覆盖:检查用户设备分布,Safari 11+ 和 Chrome 49+ 是底线
  2. 配置内容协商:Nginx 同时开启 brotli 和 gzip,用 Vary 头区分
  3. 选择压缩级别:静态预压缩用级别 6,实时压缩用级别 4-5
  4. 验证 CDN 兼容:确保 CDN 支持 Brotli 透传,否则会二次压缩
  5. 监控 CPU 负载:上线后观察一周,确认没有异常飙升
  6. 测试降级路径:模拟不支持 Brotli 的浏览器,确认能回退到 Gzip

我曾经翻过车:有次上线忘了配 Vary: Accept-Encoding,CDN 把 Brotli 版本缓存给了所有用户。结果 IE 用户拿到乱码,线上事故持续了 20 分钟。从那以后,我每次上线前都会用 curl 手动验证响应头。

Brotli 不是银弹,但在文本压缩这个场景下,它确实比 Gzip 强一截。配置不复杂,收益却很实在。你想想看,同样的带宽,能多传 20% 的资源,何乐而不为?