HTTP/2头部压缩由HPACK自动处理,但动态表大小需显式配置:MaxDecoderHeaderTableSize应≤服务端SETTINGS_HEADER_TABLE_SIZE,MaxEncoderHeaderTableSize建议与服务端对齐;默认64KB流量窗口易致假性阻塞,应调大InitialConnWindowSize和InitialStreamWindowSize至1MB;MaxConcurrentStreams需匹配服务端SETTINGS_MAX_CONCURRENT_STREAMS,避免RST_STREAM;DisableCompression仅禁用body压缩,不影响HPACK。

HTTP/2 头部压缩由 HPACK 自动处理,但动态表大小必须显式配置
Go 标准库的 http.Transport 默认启用 HPACK,但不会自动适配服务端的 SETTINGS_HEADER_TABLE_SIZE。如果服务端将该值设为较小(如 2KB),而客户端仍按默认 4KB 解码,就可能触发 HEADERS frame too large 错误或静默截断 header。
实际配置需在 Transport 初始化时设置:
-
MaxDecoderHeaderTableSize:控制接收方解码器动态表上限,应 ≤ 服务端通告的SETTINGS_HEADER_TABLE_SIZE -
MaxEncoderHeaderTableSize:控制发送方编码器动态表上限,建议与服务端对齐,避免 header 被降级为 Huffman 编码传输 - 若服务端未明确限制,可保留默认值(4096),但高并发场景下建议统一设为 2048 或 1024,减少内存碎片和 GC 压力
示例:
tr := &http.Transport{
MaxDecoderHeaderTableSize: 2048,
MaxEncoderHeaderTableSize: 2048,
}
流量控制窗口不调优会导致连接吞吐量卡死在 64KB
Go 的 http2 实现中,连接级流量控制窗口默认是 65535 字节(64KB)。一旦发送方发满这个额度,就必须等待接收方发回 WINDOW_UPDATE 帧才能继续——哪怕网络空闲、服务端处理极快,客户端也会“假性阻塞”。
这不是 bug,而是协议设计;但默认值对现代高带宽链路明显偏小:
- 调大
InitialConnWindowSize和InitialStreamWindowSize是最直接的优化点 - 二者需通过
http2.ConfigureTransport注入,不能直接设在http.Transport - 常见安全且有效的值是
1 (1MB);超过 4MB 可能增加内存占用,且收益递减 - 注意:服务端也可能限制其窗口大小,最终生效值取 min(客户端设置, 服务端通告)
示例:
tr := &http.Transport{}
http2.ConfigureTransport(tr)
tr.TLSClientConfig = &tls.Config{NextProtos: []string{"h2"}}
tr.MaxConnsPerHost = 100
// 注入自定义窗口值
tr.DialContext = func(ctx context.Context, netw, addr string) (net.Conn, error) {
conn, err := tls.Dial(netw, addr, tr.TLSClientConfig, &tls.Config{
NextProtos: []string{"h2"},
})
if err != nil {
return nil, err
}
// 手动设置初始窗口(需在首帧前完成)
http2.ConfigureTransport(tr)
return conn, nil
}
// 更推荐:用 http2.TransportSettings 显式传入
http2.TransportSettings{
InitialConnWindowSize: 1 << 20,
InitialStreamWindowSize: 1 << 20,
}
MaxConcurrentStreams 设置不当会引发请求排队或 RST_STREAM
Go 客户端默认不限制单连接上的并发流数(MaxConcurrentStreams: 0),但服务端通常有硬限制(如 nginx 默认 128)。当客户端发起第 129 个请求时,服务端会直接返回 RST_STREAM,错误信息为 stream closed 或 http2: stream ID x was reset。
解决方式不是盲目加大,而是匹配服务端策略:
- 先通过抓包或
curl -v --http2 https://your.api/查看服务端SETTINGS_MAX_CONCURRENT_STREAMS值 - 在
http2.TransportSettings中设MaxConcurrentStreams为该值的 80%~90%,留出余量应对突发 - 若服务端未通告该值(罕见),保守设为 100;超过 256 后收益趋缓,且可能加剧流抢占竞争
- 严格模式(
StrictMaxConcurrentStreams: true)会让客户端主动拒绝超出限制的新流,比等 RST 更可控
DisableCompression = true 不影响 HPACK,只禁用 gzip
这是高频误解:DisableCompression 控制的是 HTTP body 的 gzip/deflate 压缩,和 HPACK 完全无关。HPACK 压缩 header 是 HTTP/2 协议强制行为,无法关闭。
如果你观察到 header 没被压缩,真正原因通常是:
- 请求使用了明文 h2c(HTTP/2 over TCP),而某些代理或中间件不支持 HPACK
- header 中包含大量动态、非重复字段(如随机 token、时间戳),导致 HPACK 压缩率低
- 服务端返回了
SETTINGS_ENABLE_PUSH = 0并同时重置了 header table,但这种情况极少
验证 HPACK 是否生效,最可靠方式是用 Wireshark 抓包,过滤 http2.header,观察 header block 是否以索引或 Huffman 编码形式出现,而非明文 key-value。


















