
怎么判断 Go HTTP 服务是否真的启用了 Gzip 压缩
Go 的 http.ServeMux 默认不压缩,必须显式加中间件或用 gzip.Handler 包裹。光看响应头 Content-Encoding: gzip 不够——有些代理(如 Nginx)会自己压,而 Go 后端实际没压;也有些客户端(比如旧版 curl)没发 Accept-Encoding: gzip,导致服务端根本不会触发压缩逻辑。
实操建议:
立即学习“go语言免费学习笔记(深入)”;
- 用
curl -H "Accept-Encoding: gzip" -I http://localhost:8080/api/data检查响应头是否含Content-Encoding: gzip且Content-Length明显变小 - 在 handler 中打印
r.Header.Get("Accept-Encoding"),确认请求确实带了gzip - 禁用所有反向代理,直连 Go 服务测试,排除 Nginx/Caddy 等中间层干扰
- 注意:Go 1.19+ 的
net/http对小于 1.5KB 的响应默认跳过压缩,这是硬编码阈值,无法通过配置关闭
用 gzip.Handler 还是手动调用 gzip.NewWriter
gzip.Handler 是最简单的方式,但它是“全量开关”——对所有响应统一处理,不区分 Content-Type、状态码或 body 大小;而手动用 gzip.NewWriter 可以精细控制,比如只压缩 application/json、跳过 204 或空响应、或结合 http.MaxBytesReader 防止恶意大包触发高压缩。
实操建议:
立即学习“go语言免费学习笔记(深入)”;
- 若只需基础压缩,直接套
gzip.Handler:http.ListenAndServe(":8080", gzip.Handler(myMux)) - 若需过滤,别用
gzip.Handler,改写中间件,在WriteHeader后、Write前检查w.Header().Get("Content-Type")和len(body) - 手动压缩时务必调用
gz.Write后跟gz.Close(),否则末尾 CRC 和长度字段缺失,客户端解压失败 -
gzip.NewWriterLevel的第二个参数建议用gzip.BestSpeed(1)或gzip.DefaultCompression(6),避免用BestCompression(9)——CPU 升高 3–5 倍,压缩率仅多 5% 左右
为什么开了 Gzip,CPU 却飙升而带宽降得不多
典型原因是响应体太小(gzip.Writer 内部锁争用严重,尤其在复用 sync.Pool 不当的情况下。
实操建议:
立即学习“go语言免费学习笔记(深入)”;
- 用
go tool pprof抓 CPU profile,重点看compress/gzip.(*Writer).Write和compress/flate.(*compressor).write占比 - 给小响应加 size 门限:body 长度
- 不要全局复用一个
*gzip.Writer,要用sync.Pool按请求分配;但 pool 的New函数里别传固定io.Discard,应传当前 responseWriter - 考虑用
zstd替代(viagithub.com/klauspost/compress/zstd),同压缩率下 CPU 低 40%,Go 原生不支持,但落地成本不高
HTTP/2 下 Gzip 是否还有效、要不要关
HTTP/2 本身不压缩 header(用 HPACK),但 body 压缩和 HTTP/1.1 完全一致,Accept-Encoding 和 Content-Encoding 行为无变化。所以 Gzip 该开还得开,HTTP/2 不会自动帮你省带宽。
实操建议:
立即学习“go语言免费学习笔记(深入)”;
- 不要因为上了 HTTP/2 就关 Gzip——实测 JSON API 开启后 body 体积仍可减少 60–70%
- 注意:某些 TLS 终止点(如 ALB、Cloudflare)可能把 HTTP/2 降级成 HTTP/1.1 再转发,此时 Gzip 头可能被 strip,需检查最终到达 Go 服务的请求头
- Go 的
http.Server.TLSConfig若启用NextProtos: []string{"h2", "http/1.1"},确保客户端真实协商到了 h2(可用curl --http2 -v验证)
真正难调的是「动态阈值」——不同接口响应结构差异大,统一设 1KB 可能误伤大文本,放宽松又浪费 CPU。线上建议按路由路径打点统计平均 body size 和压缩增益,再分组配置,而不是靠一个全局开关硬扛。










