Go 1.28 HTTP/2 parking 实测:5000 个空闲连接少 5000 个 goroutine,RSS 降 85–93 MiB
Go 1.28 草案 release notes 里,HTTP/2 server 有一处改动:负责服务单个连接的 goroutine 在没有工作时可以退出,而不是原地等待;有新的工作时,再由新的 goroutine 接管 serveLoop。实现里把这套机制叫 parking,内部名字包括 tryPark,另有一个临时 GODEBUG 开关 http2serveparking。它不是公开 API,草案 release notes 也没有使用 parking 这个词。
parking 并不只限于没有 active stream 的连接:当 handler 仍在运行、serveLoop 暂时没有立即要做的工作,并且满足安全条件时,服务 goroutine 也可以被 park。
测试设置
同一份 benchmark 代码、同一台机器,三种配置对照:
- Go 1.27.2,作为旧版本参照。
- Go 1.28 开发版,parking 开启(默认行为)。
- 同一个 Go 1.28 二进制,parking 关闭,设置
GODEBUG=http2serveparking=0。
测试的 Go 1.28 revision 是 5c51011e82ed754af072baaf4338ff35e76fe705,benchmark 源码固定在 commit 86753bd4d1f79621a4f3c6927c8239419e54bde2。
每种配置测量 0、1000、5000 个空闲 HTTP/2 连接,每个 case 跑十轮,整轮实验在同一台 Linux 上重复三次,取中位数。机器为 Intel Core i7-1265U,Linux 6.18.56,约 32 GiB RAM;benchmark 设置 GOMAXPROCS=12、GOGC=100,不强制 GC。
记录四个指标:
- RSS:Linux 报告的进程常驻内存。
- Goroutines:runtime 报告的 goroutine 数量。
- StackInUse:goroutine 栈占用。
- HeapAlloc:runtime 报告的已分配堆。
5000 个空闲连接的 RSS
单位 MiB,数值为中位数:
| 运行 | Go 1.27.2 | Go 1.28,parking off | Go 1.28,parking on | 同二进制 parking 节省 |
|---|---|---|---|---|
| Run 1 | 249.89 | 218.28 | 129.19 | 89.08 |
| Run 2 | 247.18 | 223.73 | 131.11 | 92.62 |
| Run 3 | 245.60 | 217.94 | 133.25 | 84.70 |
同一个 Go 1.28 二进制,开启 parking 相比关闭 parking,RSS 降低 84.7–92.6 MiB,约 38.9%–41.4%。
1000 个空闲连接时降幅小一些,但方向明确,约 15.7–16.6 MiB。零连接基线在 8.5–8.7 MiB 之间。
goroutine 与栈
5000 个空闲连接下,parking off 约 10007 个 goroutine,parking on 约 5007 个,差距正好是 5000。1000 个连接时,差距是 1000。
StackInUse 在 5000 个连接时,parking off 约 99 MiB,parking on 约 21 MiB,三次运行都省下约 78 MiB。
RSS 的下降并不全部来自栈,RSS 还包含 Go 栈以外的内存。
HeapAlloc 的不一致
HeapAlloc 没有呈现一致方向:有的 run 里开启 parking 用得更少,有一个 run 反而略多。所以结论只采用 RSS、goroutine 数和 StackInUse。一个内存指标不能替代另一个。
局限
- 单机测试,单一 benchmark 设计。
- 使用特定的 Go 1.28 开发版 revision,不是最终发布版本。
- 主要测空闲连接,没有测真实流量混合场景。
GODEBUG=http2serveparking=0是临时且未文档化的设置,不应视为稳定选项。
三次完整运行说明方向可复现并给出区间,但不是通用保证。
结论
在这个 benchmark 里,HTTP/2 parking 对内存的影响明确:5000 个空闲连接时,同一个二进制少约 85–93 MiB RSS、少 5000 个 goroutine、少约 78 MiB 栈。
更值得注意的是,一条看起来安静的连接会占住多少内存。改「等待时做什么」,和优化「做的事」同样重要。
这些数字不适合直接套到所有 Go 服务;但对于长期持有大量空闲 HTTP/2 连接的服务器,值得理解这套机制,并用真实负载重新测量。