docs: record verified HTTP2 proxy and rolling drain acceptance

This commit is contained in:
2026-09-23 11:18:25 +08:00
parent b54a303ccd
commit 59bf04a461
10 changed files with 499 additions and 8 deletions

45
transport/http2/PROXY.md Normal file
View File

@@ -0,0 +1,45 @@
# HTTP/2 代理回源验收
本项验证的链路是:`Go HTTP/2 客户端 → TLS/h2 → Nginx → TLS/h2 → transport/http2`。客户端与回源两段分别检查协议、TLS 和请求内容;它使用真实 Nginx 进程及生成的测试证书。
Nginx 的 `proxy_http_version 2` 从 1.29.4 起可用,见 [官方配置说明](https://nginx.org/en/docs/http/ngx_http_proxy_module.html#proxy_http_version)。本次固定使用 1.30.5。原有 `scripts/verifyProxy.py` 和 Nginx 1.27 环境继续用于 native HTTP/1.1 回源验收。
## 运行
在项目根目录执行:
```sh
python3 scripts/verifyHTTP2Proxy.py --count 3
python3 scripts/verifyHTTP2Proxy.py --race --count 3 --output performance/http2ProxyRaceLinux.json
```
需要指定 Go 模块代理时追加 `--goproxy https://goproxy.cn,direct`。普通模式使用 `nginx:1.30.5-alpine`,race 模式使用 `nginx:1.30.5`,以提供 Go race 二进制需要的 glibc 运行环境;使用 `--nginx-image` 覆盖时需自行保持该兼容性。
脚本先在 `golang:1.25` 中执行 vet 并编译测试二进制,清理编译容器后,再在 Nginx 镜像中执行测试。两个阶段顺序运行,每个容器固定限制为 2 核、2 GiB 内存、禁用 swap、256 个进程/线程。整个运行默认 600 秒总时限,单次测试进程还有 120 秒时限。
集成测试容器使用 `--network none`、只读根文件系统和 128 MiB 临时内存目录,没有发布端口。客户端、Nginx 和新旧两个后端都只监听容器内回环地址。超时、SIGTERM、SIGHUP 会清理当前运行拥有的容器;检测到其他 HTTP/2 验收容器运行时,脚本会拒绝开始。
## 验收内容
| 场景 | 必须满足的结果 |
| --- | --- |
| 双段 TLS/h2 | 客户端验证代理证书;代理验证源服务器证书和主机名。业务 handler 必须看到 HTTP/2、ALPN h2 和预期 SNI。 |
| 回源连接复用 | 32 次顺序 POST 仅建立 1 条回源 TLS 连接;每次校验独立 requestId、方法、请求体长度与 SHA-256。 |
| 证书失败 | 不可信 CA 和错误主机名分别得到 502,均未进入业务 handler;正常路由随后仍可请求成功。 |
| 超时 | 代理 250 ms 读取超时得到 504,后端请求 context 被取消、活动请求回到 0,后续请求恢复。 |
| 客户端取消 | 客户端取消后,后端 handler 能退出并释放请求槽位,后续请求恢复。 |
| GOAWAY 与新旧实例切换 | 旧实例保留一个处理中的 POST;开始 Shutdown 后必须观察到 GOAWAY。随后 20 个独立 POST 进入备用的新实例,旧 POST 释放后仍返回原实例的正确响应。旧实例最终连接/请求归零,业务执行无重复。 |
新旧实例场景使用 Nginx primary/backup 上游与有限重试;测试没有开启 `non_idempotent` 重试。除了检查每次响应,还检查两个实例的实际 handler 次数及重复 requestId,避免重试掩盖重复执行。
本次 Nginx 在超时和取消场景中关闭了相关回源连接,随后重新建连恢复;这里验收的是请求取消与资源回收,不要求代理必须通过 RST_STREAM 保留同一条回源连接。
## 已有结果与边界
2026-09-23,Linux amd64、Go 1.25.14、Nginx 1.30.5:普通模式和 race 模式各连续 3 轮全部通过,共 18 个子场景,无数据竞争。两轮运行后原有 7 个业务容器仍在运行。
- [普通模式报告](../../performance/http2ProxyLinux.json) / [原始日志](../../performance/http2ProxyLinux.txt)
- [race 报告](../../performance/http2ProxyRaceLinux.json) / [原始日志](../../performance/http2ProxyRaceLinux.txt)
- [总时限、SIGTERM、SIGHUP 清理实测](../../performance/http2ProxyCleanupLinux.json)
报告记录测试提交、源码 SHA-256、镜像 ID、完整执行命令、结果与日志校验值。它是功能联调,不是代理吞吐基准;使用单进程 Nginx 测试配置,验证后端实例切换,不覆盖 Nginx master/worker 配置重载、公网链路或 Cloudflare 实网部署。`transport/http2` 仍是独立实验适配器,native 的 HTTP/1.1 默认路径保持现状。

View File

@@ -6,10 +6,12 @@
2026-09-23 已在 Linux amd64 完成单 TLS 连接、128 个 worker 的 30 分钟长测:58,102,476 次请求,0 失败与协议错误,采样 RSS 峰值约 22.6 MiB,退出后连接、活动请求和 FD 回收。测试容器限制为 2 核、2 GiB,测量包含同一进程中的客户端与服务端,见 [报告](../../performance/http2SoakLinux.json)。该场景包含慢 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`,完整采样见 [混合长测报告](../../performance/http2MixedSoakLinux.json)。8 MiB 下载超过客户端的 stream 初始窗口,需要窗口更新,但不代表耗尽了连接级窗口;后者由下述独立帧测试验证。HTTP/2 回源代理联调仍未完成。
同日完成多连接慢客户端混合场景: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`,完整采样见 [混合长测报告](../../performance/http2MixedSoakLinux.json)。8 MiB 下载超过客户端的 stream 初始窗口,需要窗口更新,但不代表耗尽了连接级窗口;后者由下述独立帧测试验证。Nginx HTTP/2 回源联调结果见下方。
连接级流控及关闭边界测试已通过 Linux amd64 的 HTTP/2 全包 race、vet、build 回归,测试提交、执行命令和原始日志校验值见 [边界回归报告](../../performance/http2FlowControlLinux.json)。
Nginx 1.30.5 的真实 HTTP/2 回源联调也已通过:普通与 race 模式各 3 轮,覆盖两段 TLS/h2、证书拒绝、连接复用、超时/取消恢复和 GOAWAY 下的新旧实例切换。完整配置边界、运行方法与报告见 [HTTP/2 代理回源验收](../http2/PROXY.md)。
## 目标边界
- HTTP/2 必须作为显式 opt-in 协议,不改变 `omni.New()`、`transport/nethttp` 或 native HTTP/1.1 的默认行为。
@@ -37,7 +39,7 @@ HTTP/2 版本需要在 native `Stats` 之外至少记录:活动 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/Cloudflare 的 TLS、Keep-Alive、超时、GOAWAY 和优雅发布行为与 HTTP/1.1 回源结果一致。现有 `verifyProxy.py` 检查边缘 HTTP/2 协商与 native HTTP/1.1 TLS 回源,不构成 HTTP/2 上游 GOAWAY/排空的验收依据。
- 代理联调: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 连接慢上传/慢下载混合场景均已通过,原始日志与采样分别保存在两个报告中。
只有上述矩阵完成,才把 `h2` 加入 native 的 TLS ALPN,并考虑把 HTTP/2 作为独立版本能力发布。此前生产环境使用 Nginx/Cloudflare 终止 HTTP/2 后以 HTTP/1.1 回源,或使用支持 HTTP/2 的 `transport/nethttp`。
这些实验结果已经覆盖到 Nginx HTTP/2 回源联调。下一步是统一启动配置、native 的 HTTP/1.1 与 HTTP/2 协议接入回归,以及业务环境试用,不能仅通过添加 `h2` 到 native 的 TLS ALPN 就宣告接入完成。当前继续保持显式选择实验适配器;默认 native 路径仍为 HTTP/1.1,需要成熟 HTTP/2 入口时可使用 `transport/nethttp` 或代理终止后回源 HTTP/1.1。