编程 Go 1.28 HTTP/2 parking 实测:5000 个空闲连接少 5000 个 goroutine,RSS 降 85–93 MiB

2026-10-29 21:01:45

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 代码、同一台机器,三种配置对照:

  1. Go 1.27.2,作为旧版本参照。
  2. Go 1.28 开发版,parking 开启(默认行为)。
  3. 同一个 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.2Go 1.28,parking offGo 1.28,parking on同二进制 parking 节省
Run 1249.89218.28129.1989.08
Run 2247.18223.73131.1192.62
Run 3245.60217.94133.2584.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 连接的服务器,值得理解这套机制,并用真实负载重新测量。

复制全文 生成海报 Go HTTP 2 性能优化 内存

推荐文章

程序员茄子在线接单