7.7 KiB
native HTTP/2 进入条件
当前 transport/native 是经过 Linux 长连接和反向代理验收的 HTTP/1.1 实现。HTTP/2 暂不直接打开,原因是它把一个 TCP 连接变成多个独立请求流,连接级的 Keep-Alive、超时和并发限制不能直接复用现有 HTTP/1.1 状态机。
当前已提供独立的 transport/http2 实验 Runner。它已经验证 TLS/h2、h2c、本地连接和请求闸门、GOAWAY 排空以及 RST_STREAM 取消;这些结果用于验证生命周期设计,不代表 native 已经启用 HTTP/2。实验 Runner 还覆盖了默认 1 MiB 头部上限、8 MiB 请求体上限、128 路同连接 stream 多路复用,以及 2 MiB 慢读取 body 在窗口耗尽后的恢复。TestTLSSoak 默认运行 3 秒,Linux 长测通过 python3 scripts/verifyHTTP2.py --duration 30m 启动。
2026-09-23 已在 Linux amd64 完成单 TLS 连接、128 个 worker 的 30 分钟长测:58,102,476 次请求,0 失败与协议错误,采样 RSS 峰值约 22.6 MiB,退出后连接、活动请求和 FD 回收。测试容器限制为 2 核、2 GiB,测量包含同一进程中的客户端与服务端,见 报告。该场景包含慢 Handler 与 64 KiB body。
同日完成多连接慢客户端混合场景:8 条独立 TLS 连接,每条 14 个普通请求 worker、1 个慢上传 worker、1 个慢下载 worker,持续 30 分钟。256 KiB 上传每 4 KiB 等待 2 ms;8 MiB 下载每次最多读取 64 KiB 后等待 20 ms。共完成 53,191,687 次请求,其中慢上传 106,971 次、慢下载 5,588 次,0 失败与协议错误;每条连接均覆盖三类请求且没有重连。采样 RSS 峰值约 116.6 MiB,包含客户端、服务端与下载缓冲;退出后活动连接/请求归零、goroutine 回到 2、FD 回到 6,释放测试数据并两次 GC 后存活堆约 450 KiB。运行命令为 python3 scripts/verifyHTTP2.py --scenario mixed --duration 30m,完整采样见 混合长测报告。8 MiB 下载超过客户端的 stream 初始窗口,需要窗口更新,但不代表耗尽了连接级窗口;后者由下述独立帧测试验证。Nginx HTTP/2 回源联调结果见下方。
连接级流控及关闭边界测试已通过 Linux amd64 的 HTTP/2 全包 race、vet、build 回归,测试提交、执行命令和原始日志校验值见 边界回归报告。
Nginx 1.30.5 的真实 HTTP/2 回源联调也已通过:普通与 race 模式各 3 轮,覆盖两段 TLS/h2、证书拒绝、连接复用、超时/取消恢复和 GOAWAY 下的新旧实例切换。完整配置边界、运行方法与报告见 HTTP/2 代理回源验收。
目标边界
- HTTP/2 必须作为显式 opt-in 协议,不改变
omni.New()、transport/nethttp或 native HTTP/1.1 的默认行为。 - TLS ALPN 只在 HTTP/2 实现通过全部验收后增加
h2;当前 native 只宣告http/1.1,误配h2会在启动时失败。 - Handler、Service、统一响应、requestId、可信代理和业务日志继续复用现有
httpx.Context与http.Handler边界。 - h2c 只用于本地测试,生产入口使用 TLS + ALPN;不能把明文升级路径默认为公网协议。
连接和流的生命周期
- accept loop 先执行连接上限检查,再执行 TLS 握手;握手受独立超时约束,不能占满连接槽位后无限等待。
- TLS 协商得到
h2后进入 HTTP/2 connection state;其他 ALPN 直接关闭,不能把 HTTP/2 preface 交给 HTTP/1.1 解析器。 - 每个 stream 创建独立的
http.Request和取消 context。RST_STREAM只取消当前请求,TCP 断开才取消该连接上的所有请求。 MaxConcurrentRequests是所有连接共享的全局请求闸门;MaxConcurrentStreams限制单连接并发流;两者都必须在收到完整请求体前生效。MaxHeaderBytes沿用标准库默认 1 MiB 上限,MaxBodyBytes默认限制请求体为 8 MiB;已声明超限的请求在进入业务 handler 前返回 413,未声明长度的 body 通过受限 reader 读取,避免业务层无意间把整个请求体加载到内存。- 服务关闭时停止 accept,发送 GOAWAY 拒绝新流,等待活动流排空;超过
ShutdownTimeout后关闭连接并取消剩余请求。实验 Runner 已覆盖这条顺序。
必须可观测的状态
HTTP/2 版本需要在 native Stats 之外至少记录:活动 stream、被拒绝 stream、连接级 protocol error、RST_STREAM、GOAWAY 发送次数,以及因流控或并发闸门触发的拒绝。实验 Runner 当前已通过 ActiveRequests、RejectedRequests、ProtocolErrors、RSTStreams、GoAwaySent 和 GoAwayFrames 暴露活动/拒绝/协议错误/客户端重置/优雅关闭快照;帧观察器只解析 9 字节头部并跳过 payload,不会缓存大 body。计数必须是原子快照,不能在 metrics handler 中锁住连接状态机。
进入实现前的验收矩阵
- TLS ALPN:只协商
h2,HTTP/1.1 客户端仍由现有 native 路径处理;不支持的 ALPN 在握手前后都不会误入错误解析器。 - 多路复用:一个连接并发至少 128 个 stream,响应和 requestId 不串线,在流与连接级发送额度充足时,单个慢 stream 不阻塞其他可发送 stream(实验 Runner 已覆盖 128 路同连接 stream)。
- 流控:慢读取、慢写、窗口耗尽和大 body 都能恢复;内存不会随未消费 body 无限增长。实验 Runner 已覆盖 2 MiB 慢读取 body 和请求体上限,以及客户端将 stream 窗口设为零时的大响应阻塞、同连接 204、其他连接带 body 响应、WINDOW_UPDATE 恢复发送和 RST_STREAM 取消回收;连接级独立测试将初始 stream 窗口设为 128 KiB,仅消耗默认 65,535 字节连接窗口,确认暂停与隔离,连接级 WINDOW_UPDATE 后恢复并复用连接;持续慢客户端混合长测也已通过。连接级窗口耗尽时,同连接的其他 DATA 也受该窗口约束,HEADERS 和其他 TCP 连接仍可继续。
- 取消:RST_STREAM、客户端断开、服务关闭分别只影响应受影响的请求,并能在 metrics 中看到回收;当前实验 Runner 已覆盖 RST_STREAM 与服务关闭,包括客户端零接收窗口造成写阻塞时,关闭超时强制解除写阻塞、取消请求并回收连接。
- 背压:连接上限、全局 stream 上限、单连接 stream 上限和 header/body 上限均能快速拒绝并自动恢复。实验 Runner 已增加 TLS 未完成握手占满连接槽位时的及时拒绝、握手超时回收及后续正常 HTTPS 请求恢复验收。
- 代理联调:Nginx 的 TLS/h2 回源、Keep-Alive、超时/取消及 GOAWAY 排空已由
verifyHTTP2Proxy.py验证。旧实例处理中的 POST 能正常完成,新请求转入备用实例,旧实例连接与请求归零。此测试使用单进程 Nginx,未验证 Nginx master/worker 重载或 Cloudflare 实网部署。verifyProxy.py继续承担 native HTTP/1.1 TLS 回源验收。 - 长测:Linux amd64 至少 30 分钟 TLS、多连接、多 stream、慢客户端混合场景;要求 0 协议错误、0 请求串线,连接、stream、goroutine 和 FD 在停止后回收。单连接场景与 8 连接慢上传/慢下载混合场景均已通过,原始日志与采样分别保存在两个报告中。
这些实验结果已经覆盖到 Nginx HTTP/2 回源联调。下一步是统一启动配置、native 的 HTTP/1.1 与 HTTP/2 协议接入回归,以及业务环境试用,不能仅通过添加 h2 到 native 的 TLS ALPN 就宣告接入完成。当前继续保持显式选择实验适配器;默认 native 路径仍为 HTTP/1.1,需要成熟 HTTP/2 入口时可使用 transport/nethttp 或代理终止后回源 HTTP/1.1。