docs: record verified HTTP2 proxy and rolling drain acceptance
This commit is contained in:
@@ -2,8 +2,10 @@
|
|||||||
|
|
||||||
## Unreleased
|
## Unreleased
|
||||||
|
|
||||||
|
- 新增 `TestNginxHTTP2Proxy` 与 `scripts/verifyHTTP2Proxy.py`:验证两段 TLS/h2、回源连接复用、证书校验拒绝、超时/客户端取消恢复,以及 GOAWAY 下的新旧实例切换和请求排空。Linux amd64、Nginx 1.30.5 普通与 race 模式各 3 轮通过;保留源码/镜像/日志校验信息和实际执行记录。
|
||||||
|
- HTTP/2 代理验收的编译与测试阶段顺序运行,均限制 2 核、2 GiB、256 个进程/线程;集成测试禁用外部网络并使用只读根文件系统。总时限、SIGTERM、SIGHUP 清理实测通过;native 默认协议保持 HTTP/1.1。
|
||||||
- 新增 `TestTLSMixedSoak` 与 `verifyHTTP2.py --scenario mixed`:8 条 TLS 连接、128 个 worker 持续混跑普通请求、慢上传和慢下载,校验 TLS/HTTP2、requestId、响应内容及传输摘要;分别统计三类请求与每条连接覆盖情况,并记录退出/GC 后的回收数据。
|
- 新增 `TestTLSMixedSoak` 与 `verifyHTTP2.py --scenario mixed`:8 条 TLS 连接、128 个 worker 持续混跑普通请求、慢上传和慢下载,校验 TLS/HTTP2、requestId、响应内容及传输摘要;分别统计三类请求与每条连接覆盖情况,并记录退出/GC 后的回收数据。
|
||||||
- 完成 Linux amd64 30 分钟多连接慢客户端混合长测:53,191,687 次请求(含 106,971 次慢上传、5,588 次慢下载),0 失败与协议错误;采样 RSS 峰值约 116.6 MiB,连接/请求、goroutine 和 FD 回收,释放测试数据后存活堆约 450 KiB。保留完整日志、120 份采样及每条连接的分类计数;HTTP/2 回源代理联调仍待验收。
|
- 完成 Linux amd64 30 分钟多连接慢客户端混合长测:53,191,687 次请求(含 106,971 次慢上传、5,588 次慢下载),0 失败与协议错误;采样 RSS 峰值约 116.6 MiB,连接/请求、goroutine 和 FD 回收,释放测试数据后存活堆约 450 KiB。保留完整日志、120 份采样及每条连接的分类计数。
|
||||||
- 新增独立 `transport/http2` 实验 Runner:支持 h2c/TLS h2、连接上限、全局请求并发闸门、GOAWAY 排空、RST_STREAM 单流取消和生命周期统计;不改变 native HTTP/1.1 默认路径。
|
- 新增独立 `transport/http2` 实验 Runner:支持 h2c/TLS h2、连接上限、全局请求并发闸门、GOAWAY 排空、RST_STREAM 单流取消和生命周期统计;不改变 native HTTP/1.1 默认路径。
|
||||||
- HTTP/2 实验 Runner 增加标准 1 MiB 头部上限、默认 8 MiB 请求体上限、128 路同连接多路复用、2 MiB 慢读取流控验收、协议错误、RST_STREAM 和 GOAWAY 帧计数;已声明超限请求在进入业务 handler 前返回 413。
|
- HTTP/2 实验 Runner 增加标准 1 MiB 头部上限、默认 8 MiB 请求体上限、128 路同连接多路复用、2 MiB 慢读取流控验收、协议错误、RST_STREAM 和 GOAWAY 帧计数;已声明超限请求在进入业务 handler 前返回 413。
|
||||||
- 新增可配置时长的 HTTP/2 TLS soak:默认 3 秒,Linux 验收可通过 `GO_OMNI_HTTP2_SOAK_DURATION=30m` 覆盖,混合验证 128 路 stream、慢 handler、请求体、Keep-Alive 和退出回收。
|
- 新增可配置时长的 HTTP/2 TLS soak:默认 3 秒,Linux 验收可通过 `GO_OMNI_HTTP2_SOAK_DURATION=30m` 覆盖,混合验证 128 路 stream、慢 handler、请求体、Keep-Alive 和退出回收。
|
||||||
|
|||||||
@@ -236,7 +236,9 @@ HTTP/2 仍是显式 opt-in 实验适配器,不会改变 native 的 HTTP/1.1
|
|||||||
|
|
||||||
Linux amd64 已完成 30 分钟、单 TLS 连接、128 个 worker 的普通模式长测:58,102,476 次请求,0 失败、0 协议错误,采样 RSS 峰值约 22.6 MiB,连接/活动请求归零且 FD 回到基线。完整采样和原始日志见 [HTTP/2 长测报告](performance/http2SoakLinux.json) 与 [原始日志](performance/http2SoakLinux.txt),此前的 race 对照见 [短测记录](performance/http2SmokeLinux.json)。这项结果覆盖单连接复用。
|
Linux amd64 已完成 30 分钟、单 TLS 连接、128 个 worker 的普通模式长测:58,102,476 次请求,0 失败、0 协议错误,采样 RSS 峰值约 22.6 MiB,连接/活动请求归零且 FD 回到基线。完整采样和原始日志见 [HTTP/2 长测报告](performance/http2SoakLinux.json) 与 [原始日志](performance/http2SoakLinux.txt),此前的 race 对照见 [短测记录](performance/http2SmokeLinux.json)。这项结果覆盖单连接复用。
|
||||||
|
|
||||||
多连接慢客户端场景也已完成 Linux amd64 的 30 分钟长测:8 条 TLS 连接、128 个 worker,53,079,128 次普通请求、106,971 次慢上传、5,588 次慢下载,共 53,191,687 次,0 失败与协议错误。采样 RSS 峰值约 116.6 MiB(包含客户端、服务端和下载缓冲);退出后连接/请求归零,goroutine 回到 2、FD 回到 6;释放测试数据并两次 GC 后存活堆约 450 KiB,RSS 不要求立即归还操作系统。完整 120 份采样见 [混合长测报告](performance/http2MixedSoakLinux.json) 和 [原始日志](performance/http2MixedSoakLinux.txt)。连接级窗口耗尽、WINDOW_UPDATE 恢复及零窗口写阻塞的关闭回收也已有独立测试;HTTP/2 回源代理的 GOAWAY/排空联调仍待完成,适配器保持实验状态。
|
多连接慢客户端场景也已完成 Linux amd64 的 30 分钟长测:8 条 TLS 连接、128 个 worker,53,079,128 次普通请求、106,971 次慢上传、5,588 次慢下载,共 53,191,687 次,0 失败与协议错误。采样 RSS 峰值约 116.6 MiB(包含客户端、服务端和下载缓冲);退出后连接/请求归零,goroutine 回到 2、FD 回到 6;释放测试数据并两次 GC 后存活堆约 450 KiB,RSS 不要求立即归还操作系统。完整 120 份采样见 [混合长测报告](performance/http2MixedSoakLinux.json) 和 [原始日志](performance/http2MixedSoakLinux.txt)。连接级窗口耗尽、WINDOW_UPDATE 恢复及零窗口写阻塞的关闭回收也已有独立测试;Nginx 的 HTTP/2 回源验收结果见下方,适配器保持实验状态。
|
||||||
|
|
||||||
|
`python3 scripts/verifyHTTP2Proxy.py --count 3` 验证真实 Nginx 双段 TLS/h2、回源连接复用、证书拒绝、超时/取消恢复与新旧实例的 GOAWAY 排空;增加 `--race` 可运行并发检查。Linux amd64、Nginx 1.30.5 的普通与 race 模式各 3 轮均已通过。编译和测试容器顺序运行,均限制为 2 核、2 GiB;测试容器禁用外部网络,全部请求在容器内完成。使用方法、报告及当前边界见 [HTTP/2 代理回源验收](transport/http2/PROXY.md)。
|
||||||
|
|
||||||
在框架根目录运行:
|
在框架根目录运行:
|
||||||
|
|
||||||
|
|||||||
@@ -50,13 +50,15 @@
|
|||||||
- 已验证 `MaxConcurrentRequests` 满载返回 503,活动请求结束后槽位释放,后续请求可以恢复处理。
|
- 已验证 `MaxConcurrentRequests` 满载返回 503,活动请求结束后槽位释放,后续请求可以恢复处理。
|
||||||
- native `Stats()` 已记录连接和请求过载拒绝数;访问日志已支持可信代理网段配置,默认拒绝伪造的 `X-Forwarded-For`。
|
- native `Stats()` 已记录连接和请求过载拒绝数;访问日志已支持可信代理网段配置,默认拒绝伪造的 `X-Forwarded-For`。
|
||||||
- native TLS 已明确只宣告 HTTP/1.1 ALPN;HTTP/2 暂由边缘代理终止或使用 `transport/nethttp`,已整理独立协议设计、流控和压测门槛,见 [native HTTP/2 进入条件](transport/native/HTTP2.md),完成后再接入 native。
|
- native TLS 已明确只宣告 HTTP/1.1 ALPN;HTTP/2 暂由边缘代理终止或使用 `transport/nethttp`,已整理独立协议设计、流控和压测门槛,见 [native HTTP/2 进入条件](transport/native/HTTP2.md),完成后再接入 native。
|
||||||
- 已加入独立 `transport/http2` 实验 Runner,验证 h2c/TLS h2、连接上限、全局请求并发拒绝和恢复,以及 GOAWAY 排空、RST_STREAM 单流取消和服务关闭超时;它使用 `x/net/http2` 作为临时帧/流控实现,仍需完成 HTTP/2 回源代理联调,未接入 native 默认路径。
|
- 已加入独立 `transport/http2` 实验 Runner,验证 h2c/TLS h2、连接上限、全局请求并发拒绝和恢复,以及 GOAWAY 排空、RST_STREAM 单流取消和服务关闭超时;它使用 `x/net/http2` 作为临时帧/流控实现,已完成下述 Nginx HTTP/2 回源联调,尚未接入 native 默认路径。
|
||||||
- HTTP/2 实验已补齐头部/请求体上限、128 路同连接多路复用、慢读取流控、协议错误、RST_STREAM 与 GOAWAY 统计,并提供 `scripts/verifyHTTP2.py --duration 30m` 的 Linux TLS 长测入口;长测默认不启用 race,短时并发检查使用 `--race`(默认 60 秒)。测试容器默认限制为 2 核、2 GiB 内存、256 个进程/线程,并设总时限和退出清理;受限长测结果见下方,HTTP/2 回源代理联调仍是进入 native 前的门槛。
|
- HTTP/2 实验已补齐头部/请求体上限、128 路同连接多路复用、慢读取流控、协议错误、RST_STREAM 与 GOAWAY 统计,并提供 `scripts/verifyHTTP2.py --duration 30m` 的 Linux TLS 长测入口;长测默认不启用 race,短时并发检查使用 `--race`(默认 60 秒)。测试容器默认限制为 2 核、2 GiB 内存、256 个进程/线程,并设总时限和退出清理;受限长测和 Nginx 回源联调结果见下方。
|
||||||
- 新 Linux amd64 环境在上述硬上限内完成 HTTP/2 普通模式 2 分钟与 race 模式 60 秒短测,分别为 3,885,273 和 377,788 次请求,均为 0 失败;连接/请求归零、FD 回到基线。SIGTERM、SIGHUP 和总时限退出清理已实测。报告见 [HTTP/2 短测记录](performance/http2SmokeLinux.json);此前无资源上限的 race 长测因 RSS 增长而停止,不作为通过依据。
|
- 新 Linux amd64 环境在上述硬上限内完成 HTTP/2 普通模式 2 分钟与 race 模式 60 秒短测,分别为 3,885,273 和 377,788 次请求,均为 0 失败;连接/请求归零、FD 回到基线。SIGTERM、SIGHUP 和总时限退出清理已实测。报告见 [HTTP/2 短测记录](performance/http2SmokeLinux.json);此前无资源上限的 race 长测因 RSS 增长而停止,不作为通过依据。
|
||||||
- 已完成受限的 HTTP/2 单连接 TLS 30 分钟长测:128 个 worker、58,102,476 次请求、0 失败及协议错误;包含客户端与服务端的采样 RSS 峰值约 22.6 MiB,连接/活动请求归零,FD 由 9 回到 6。120 份定时采样见 [HTTP/2 长测报告](performance/http2SoakLinux.json),测试提交和原始日志校验值保存在报告中。HTTP/2 回源的 GOAWAY/排空联调仍待完成。
|
- 已完成受限的 HTTP/2 单连接 TLS 30 分钟长测:128 个 worker、58,102,476 次请求、0 失败及协议错误;包含客户端与服务端的采样 RSS 峰值约 22.6 MiB,连接/活动请求归零,FD 由 9 回到 6。120 份定时采样见 [HTTP/2 长测报告](performance/http2SoakLinux.json),测试提交和原始日志校验值保存在报告中。Nginx HTTP/2 回源的 GOAWAY/排空结果见下方。
|
||||||
- 已完成 8 条 TLS 连接、128 个 worker 的 30 分钟混合长测:53,079,128 次普通请求、106,971 次慢上传、5,588 次慢下载,0 失败及协议错误。采样 RSS 峰值约 116.6 MiB(包含客户端和服务端),前段与最后 5 分钟 RSS 中位数分别约 111.3/110.4 MiB;退出后活动连接/请求归零,goroutine 2、FD 6 均回到基线,两次 GC 后存活堆约 450 KiB。原始日志、120 份采样、测试提交和校验值见 [混合长测报告](performance/http2MixedSoakLinux.json)。
|
- 已完成 8 条 TLS 连接、128 个 worker 的 30 分钟混合长测:53,079,128 次普通请求、106,971 次慢上传、5,588 次慢下载,0 失败及协议错误。采样 RSS 峰值约 116.6 MiB(包含客户端和服务端),前段与最后 5 分钟 RSS 中位数分别约 111.3/110.4 MiB;退出后活动连接/请求归零,goroutine 2、FD 6 均回到基线,两次 GC 后存活堆约 450 KiB。原始日志、120 份采样、测试提交和校验值见 [混合长测报告](performance/http2MixedSoakLinux.json)。
|
||||||
- 已补连接级窗口耗尽时的暂停、同连接 204/跨连接隔离、WINDOW_UPDATE 恢复和连接复用;已补零窗口写阻塞在关闭超时后的解除阻塞、请求取消与回收。Linux amd64 的 HTTP/2 全包 race、vet、build 已通过,见 [边界回归报告](performance/http2FlowControlLinux.json)。
|
- 已补连接级窗口耗尽时的暂停、同连接 204/跨连接隔离、WINDOW_UPDATE 恢复和连接复用;已补零窗口写阻塞在关闭超时后的解除阻塞、请求取消与回收。Linux amd64 的 HTTP/2 全包 race、vet、build 已通过,见 [边界回归报告](performance/http2FlowControlLinux.json)。
|
||||||
- 新增 HTTP/2 零 stream 窗口时的慢响应隔离、WINDOW_UPDATE 恢复、RST_STREAM 取消与请求槽位回收测试;补充 TLS 半开握手占满连接上限时的及时拒绝、超时回收及正常请求恢复测试。
|
- 新增 HTTP/2 零 stream 窗口时的慢响应隔离、WINDOW_UPDATE 恢复、RST_STREAM 取消与请求槽位回收测试;补充 TLS 半开握手占满连接上限时的及时拒绝、超时回收及正常请求恢复测试。
|
||||||
|
- 已完成 Nginx 1.30.5 → HTTP/2 的 TLS 回源联调:普通与 race 模式各 3 轮,共 18 个子场景通过;覆盖两段证书校验、32 次 POST 复用一条回源连接、不可信 CA/主机名拒绝、504 超时、客户端取消、GOAWAY 后旧 POST 完成及 20 个新 POST 转入新实例,无重复业务执行。见 [验收说明与报告](transport/http2/PROXY.md)。
|
||||||
|
- `scripts/verifyHTTP2Proxy.py` 顺序运行受限编译容器和禁用外部网络的测试容器;总时限、SIGTERM、SIGHUP 清理已在 Linux 实测,原有 7 个业务容器仍运行。HTTP/2 下一步是统一启动配置、native 多协议接入回归与业务试用;当前结果不覆盖 Nginx master/worker 重载或 Cloudflare 实网部署。
|
||||||
- 已用 16 个并发 worker 完成 3,200 次建连/断连 race 验收;MaxConnections 满载拒绝、槽位释放和 TLS 连接上限均已覆盖。
|
- 已用 16 个并发 worker 完成 3,200 次建连/断连 race 验收;MaxConnections 满载拒绝、槽位释放和 TLS 连接上限均已覆盖。
|
||||||
- 新增 `scripts/verifyLinux.py`,固定在 Go 1.25 Linux 容器内运行 race、vet、build 和可选 TLS/非 TLS perfcheck;perfcheck 记录堆、goroutine、连接和 Linux 文件描述符。
|
- 新增 `scripts/verifyLinux.py`,固定在 Go 1.25 Linux 容器内运行 race、vet、build 和可选 TLS/非 TLS perfcheck;perfcheck 记录堆、goroutine、连接和 Linux 文件描述符。
|
||||||
- Linux/arm64 已完成 30 秒、128 并发、512 空闲连接的 TLS 与非 TLS 持续回归,报告见 `performance/linuxSoak.json` 和 `performance/linuxSoakTLS.json`,均为 0 错误且连接、goroutine、FD 回收正常。
|
- Linux/arm64 已完成 30 秒、128 并发、512 空闲连接的 TLS 与非 TLS 持续回归,报告见 `performance/linuxSoak.json` 和 `performance/linuxSoakTLS.json`,均为 0 错误且连接、goroutine、FD 回收正常。
|
||||||
|
|||||||
42
performance/http2ProxyCleanupLinux.json
Normal file
42
performance/http2ProxyCleanupLinux.json
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
{
|
||||||
|
"testedCommit": "b54a303ccdc034cd3062e846c6c6b2c24e55ffaa",
|
||||||
|
"runnerSha256": "9ef2774b1d19c05f1ae8d9527665dba541561a7c677f9984a24e72d5bddb6ad5",
|
||||||
|
"startedAt": "2026-09-23T03:13:01.977797+00:00",
|
||||||
|
"cases": [
|
||||||
|
{
|
||||||
|
"case": "wallTimeout",
|
||||||
|
"status": "passed",
|
||||||
|
"expectedFailureExitCode": 1,
|
||||||
|
"runnerError": "Command '['/usr/bin/docker', 'run', '--rm', '--name', 'go-omni-http2-proxy-ba2aaeecdd00-build', '--cpus', '2', '--memory', '2048m', '--memory-swap', '2048m', '--pids-limit', '256', '--log-opt', 'max-size=10m', '--log-opt', 'max-file=1', '-v', '/root/go-omni-validation:/workspace:ro', '-v', '/tmp/goOmniHTTP2Proxyi0b0o9y6:/output', '-w', '/workspace', '-e', 'GOWORK=off', '-e', 'GOMAXPROCS=2', '-e', 'CGO_ENABLED=0', '-e', 'GOPROXY=https://goproxy.cn,direct', 'golang:1.25', 'sh', '-c', 'go version && go vet ./transport/http2 && go test -c -o /output/http2.test ./transport/http2']' timed out after 1.8251966070383787 seconds",
|
||||||
|
"cleanedContainers": [
|
||||||
|
"go-omni-http2-proxy-ba2aaeecdd00-build"
|
||||||
|
],
|
||||||
|
"preExistingContainersStillRunning": 7,
|
||||||
|
"remainingValidationContainers": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"case": "sigterm",
|
||||||
|
"status": "passed",
|
||||||
|
"expectedFailureExitCode": 1,
|
||||||
|
"runnerError": "KeyboardInterrupt",
|
||||||
|
"cleanedContainers": [
|
||||||
|
"go-omni-http2-proxy-6307142e05ee-build"
|
||||||
|
],
|
||||||
|
"preExistingContainersStillRunning": 7,
|
||||||
|
"remainingValidationContainers": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"case": "sighup",
|
||||||
|
"status": "passed",
|
||||||
|
"expectedFailureExitCode": 1,
|
||||||
|
"runnerError": "KeyboardInterrupt",
|
||||||
|
"cleanedContainers": [
|
||||||
|
"go-omni-http2-proxy-dcf3bd54fbe4-build"
|
||||||
|
],
|
||||||
|
"preExistingContainersStillRunning": 7,
|
||||||
|
"remainingValidationContainers": []
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"status": "passed",
|
||||||
|
"finishedAt": "2026-09-23T03:13:06.934079+00:00"
|
||||||
|
}
|
||||||
142
performance/http2ProxyLinux.json
Normal file
142
performance/http2ProxyLinux.json
Normal file
@@ -0,0 +1,142 @@
|
|||||||
|
{
|
||||||
|
"status": "passed",
|
||||||
|
"startedAt": "2026-09-23T03:10:31.408631+00:00",
|
||||||
|
"testedCommit": "b54a303ccdc034cd3062e846c6c6b2c24e55ffaa",
|
||||||
|
"workingTreeStatus": "",
|
||||||
|
"sourceSha256": {
|
||||||
|
"go.mod": "28d68eb2c0a019cceea884e88ab2335f44ad930e1890e28871638f4b073b4f86",
|
||||||
|
"go.sum": "d9c2a5d0de94f08bd18a14a9626f9dda559c245d89bf8a689e32acbd2136fb05",
|
||||||
|
"scripts/verifyHTTP2Proxy.py": "9ef2774b1d19c05f1ae8d9527665dba541561a7c677f9984a24e72d5bddb6ad5",
|
||||||
|
"transport/http2/admission_test.go": "342a7d02bba508100a207b77246ca92e0fa04dd5f8e4a27091a01866b6750d39",
|
||||||
|
"transport/http2/flowControl_test.go": "595b2e937cef2142ff6b6386116e338d874d0fe49e63d80f6de2e1268378dba2",
|
||||||
|
"transport/http2/frameObserver.go": "fcdf431f8269ad9cfd65dd14102ce5c42ac8c4b8b833ed42c4ae15f2a7c7f14c",
|
||||||
|
"transport/http2/lifecycle_test.go": "370a16de8b20248bb78021ab338b9f62ec51f733f270c0fa92f48b1a3ccb9eae",
|
||||||
|
"transport/http2/mixedSoak_test.go": "2111737fe0405bc809bf06325573f7356160ae7fcaebcce06dcd94b2d9e7dccd",
|
||||||
|
"transport/http2/proxy_test.go": "808d0fa7e6c3b0237b1adf4a71a4f30ae2dbc7d1a9d53fab164cda33b15c9d69",
|
||||||
|
"transport/http2/server.go": "aeecdc3aec42219862d70ec788bef5df332189895555bc5d390849a51bd0fbfb",
|
||||||
|
"transport/http2/server_test.go": "c4613e77fa885b6362c06c393937c7dcf9a7ffb4a9a32f85e21fc2f741de81a6",
|
||||||
|
"transport/http2/soak_test.go": "52baa37e37dba497c579b6b55d723bda866ac57676254dc878c511345acc5af2"
|
||||||
|
},
|
||||||
|
"repetitions": 3,
|
||||||
|
"race": false,
|
||||||
|
"containerLimits": {
|
||||||
|
"cpus": 2,
|
||||||
|
"memoryBytes": 2147483648,
|
||||||
|
"memoryAndSwapBytes": 2147483648,
|
||||||
|
"pidsLimit": 256
|
||||||
|
},
|
||||||
|
"wallTimeoutSeconds": 600,
|
||||||
|
"imageIds": {
|
||||||
|
"golang:1.25": "sha256:699337d620559a59b4a2bb298ad59611e535d2ee755a34cf2d2a98f37578dc80",
|
||||||
|
"nginx:1.30.5-alpine": "sha256:bf3201ab56f23e5954646379c775d511fc466e9f11376d9725361064ad07ed35"
|
||||||
|
},
|
||||||
|
"notes": [
|
||||||
|
"Compiler and integration stages run sequentially; at most one 2-CPU/2-GiB validation container is active.",
|
||||||
|
"The integration container has network=none, no published ports and a read-only root filesystem. Client, Nginx and origins communicate over loopback inside that container.",
|
||||||
|
"Both TLS legs validate certificates; invalid upstream CA and hostname must fail before reaching the handler.",
|
||||||
|
"HTTP/2 is verified independently on both legs. This is a functional integration test, not a throughput benchmark or a live Cloudflare deployment.",
|
||||||
|
"Single-process Nginx fixture with generated test certificates. Rolling drain switches between old and backup origins; this does not test Nginx master/worker configuration reloads or live Cloudflare routing."
|
||||||
|
],
|
||||||
|
"proxyVersions": [
|
||||||
|
"nginx version: nginx/1.30.5"
|
||||||
|
],
|
||||||
|
"results": [
|
||||||
|
"tlsAndReuse reusedRequests=32 upstreamConnections=1 invalidTrustStatus=502 invalidNameStatus=502 recovered=true",
|
||||||
|
"timeoutAndCancellation timeoutStatus=504 canceledHandlers=2 recovered=true stats={AcceptedConnections:3 RejectedConnections:0 RejectedRequests:0 ProtocolErrors:0 GoAwaySent:0 RSTStreams:0 GoAwayFrames:0 ActiveConnections:1 ActiveRequests:0}",
|
||||||
|
"goAwayAndRollingDrain oldRequests=2 newRequests=20 heldPostCompleted=true duplicateHandlers=0 oldStats={AcceptedConnections:1 RejectedConnections:0 RejectedRequests:0 ProtocolErrors:0 GoAwaySent:1 RSTStreams:0 GoAwayFrames:1 ActiveConnections:0 ActiveRequests:0}",
|
||||||
|
"tlsAndReuse reusedRequests=32 upstreamConnections=1 invalidTrustStatus=502 invalidNameStatus=502 recovered=true",
|
||||||
|
"timeoutAndCancellation timeoutStatus=504 canceledHandlers=2 recovered=true stats={AcceptedConnections:3 RejectedConnections:0 RejectedRequests:0 ProtocolErrors:0 GoAwaySent:0 RSTStreams:0 GoAwayFrames:0 ActiveConnections:1 ActiveRequests:0}",
|
||||||
|
"goAwayAndRollingDrain oldRequests=2 newRequests=20 heldPostCompleted=true duplicateHandlers=0 oldStats={AcceptedConnections:1 RejectedConnections:0 RejectedRequests:0 ProtocolErrors:0 GoAwaySent:1 RSTStreams:0 GoAwayFrames:1 ActiveConnections:0 ActiveRequests:0}",
|
||||||
|
"tlsAndReuse reusedRequests=32 upstreamConnections=1 invalidTrustStatus=502 invalidNameStatus=502 recovered=true",
|
||||||
|
"timeoutAndCancellation timeoutStatus=504 canceledHandlers=2 recovered=true stats={AcceptedConnections:3 RejectedConnections:0 RejectedRequests:0 ProtocolErrors:0 GoAwaySent:0 RSTStreams:0 GoAwayFrames:0 ActiveConnections:1 ActiveRequests:0}",
|
||||||
|
"goAwayAndRollingDrain oldRequests=2 newRequests=20 heldPostCompleted=true duplicateHandlers=0 oldStats={AcceptedConnections:1 RejectedConnections:0 RejectedRequests:0 ProtocolErrors:0 GoAwaySent:1 RSTStreams:0 GoAwayFrames:1 ActiveConnections:0 ActiveRequests:0}"
|
||||||
|
],
|
||||||
|
"goVersion": "go1.25.14",
|
||||||
|
"platform": "linux/amd64",
|
||||||
|
"commands": [
|
||||||
|
[
|
||||||
|
"/usr/bin/docker",
|
||||||
|
"run",
|
||||||
|
"--rm",
|
||||||
|
"--name",
|
||||||
|
"go-omni-http2-proxy-52da1679e6df-build",
|
||||||
|
"--cpus",
|
||||||
|
"2",
|
||||||
|
"--memory",
|
||||||
|
"2048m",
|
||||||
|
"--memory-swap",
|
||||||
|
"2048m",
|
||||||
|
"--pids-limit",
|
||||||
|
"256",
|
||||||
|
"--log-opt",
|
||||||
|
"max-size=10m",
|
||||||
|
"--log-opt",
|
||||||
|
"max-file=1",
|
||||||
|
"-v",
|
||||||
|
"/root/go-omni-validation:/workspace:ro",
|
||||||
|
"-v",
|
||||||
|
"/tmp/goOmniHTTP2Proxyk3cc3h1_:/output",
|
||||||
|
"-w",
|
||||||
|
"/workspace",
|
||||||
|
"-e",
|
||||||
|
"GOWORK=off",
|
||||||
|
"-e",
|
||||||
|
"GOMAXPROCS=2",
|
||||||
|
"-e",
|
||||||
|
"CGO_ENABLED=0",
|
||||||
|
"-e",
|
||||||
|
"GOPROXY=https://goproxy.cn,direct",
|
||||||
|
"golang:1.25",
|
||||||
|
"sh",
|
||||||
|
"-c",
|
||||||
|
"go version && go vet ./transport/http2 && go test -c -o /output/http2.test ./transport/http2"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"/usr/bin/docker",
|
||||||
|
"run",
|
||||||
|
"--rm",
|
||||||
|
"--name",
|
||||||
|
"go-omni-http2-proxy-52da1679e6df-check",
|
||||||
|
"--cpus",
|
||||||
|
"2",
|
||||||
|
"--memory",
|
||||||
|
"2048m",
|
||||||
|
"--memory-swap",
|
||||||
|
"2048m",
|
||||||
|
"--pids-limit",
|
||||||
|
"256",
|
||||||
|
"--log-opt",
|
||||||
|
"max-size=10m",
|
||||||
|
"--log-opt",
|
||||||
|
"max-file=1",
|
||||||
|
"--network",
|
||||||
|
"none",
|
||||||
|
"--read-only",
|
||||||
|
"--tmpfs",
|
||||||
|
"/tmp:rw,nosuid,noexec,size=128m",
|
||||||
|
"-v",
|
||||||
|
"/tmp/goOmniHTTP2Proxyk3cc3h1_:/test:ro",
|
||||||
|
"--entrypoint",
|
||||||
|
"/test/http2.test",
|
||||||
|
"-e",
|
||||||
|
"GO_OMNI_HTTP2_PROXY_TEST=1",
|
||||||
|
"-e",
|
||||||
|
"GOMAXPROCS=2",
|
||||||
|
"nginx:1.30.5-alpine",
|
||||||
|
"-test.v",
|
||||||
|
"-test.run",
|
||||||
|
"^TestNginxHTTP2Proxy$",
|
||||||
|
"-test.timeout",
|
||||||
|
"120s",
|
||||||
|
"-test.count",
|
||||||
|
"3"
|
||||||
|
]
|
||||||
|
],
|
||||||
|
"cleanedContainers": [
|
||||||
|
"go-omni-http2-proxy-52da1679e6df-build",
|
||||||
|
"go-omni-http2-proxy-52da1679e6df-check"
|
||||||
|
],
|
||||||
|
"finishedAt": "2026-09-23T03:11:00.363119+00:00",
|
||||||
|
"logSha256": "1263ceb8ce7b65f542898e17031ba56127672d363defd0ee6520199fa1927472",
|
||||||
|
"preExistingContainersStillRunning": 7
|
||||||
|
}
|
||||||
40
performance/http2ProxyLinux.txt
Normal file
40
performance/http2ProxyLinux.txt
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
go version go1.25.14 linux/amd64
|
||||||
|
go: downloading golang.org/x/net v0.25.0
|
||||||
|
go: downloading golang.org/x/text v0.15.0
|
||||||
|
=== RUN TestNginxHTTP2Proxy
|
||||||
|
proxy_test.go:37: proxyVersion=nginx version: nginx/1.30.5
|
||||||
|
=== RUN TestNginxHTTP2Proxy/TLSAndConnectionReuse
|
||||||
|
proxy_test.go:163: proxyResult=tlsAndReuse reusedRequests=32 upstreamConnections=1 invalidTrustStatus=502 invalidNameStatus=502 recovered=true
|
||||||
|
=== RUN TestNginxHTTP2Proxy/TimeoutAndCancellation
|
||||||
|
proxy_test.go:206: proxyResult=timeoutAndCancellation timeoutStatus=504 canceledHandlers=2 recovered=true stats={AcceptedConnections:3 RejectedConnections:0 RejectedRequests:0 ProtocolErrors:0 GoAwaySent:0 RSTStreams:0 GoAwayFrames:0 ActiveConnections:1 ActiveRequests:0}
|
||||||
|
=== RUN TestNginxHTTP2Proxy/GoAwayAndRollingDrain
|
||||||
|
proxy_test.go:281: proxyResult=goAwayAndRollingDrain oldRequests=2 newRequests=20 heldPostCompleted=true duplicateHandlers=0 oldStats={AcceptedConnections:1 RejectedConnections:0 RejectedRequests:0 ProtocolErrors:0 GoAwaySent:1 RSTStreams:0 GoAwayFrames:1 ActiveConnections:0 ActiveRequests:0}
|
||||||
|
--- PASS: TestNginxHTTP2Proxy (1.03s)
|
||||||
|
--- PASS: TestNginxHTTP2Proxy/TLSAndConnectionReuse (0.39s)
|
||||||
|
--- PASS: TestNginxHTTP2Proxy/TimeoutAndCancellation (0.42s)
|
||||||
|
--- PASS: TestNginxHTTP2Proxy/GoAwayAndRollingDrain (0.22s)
|
||||||
|
=== RUN TestNginxHTTP2Proxy
|
||||||
|
proxy_test.go:37: proxyVersion=nginx version: nginx/1.30.5
|
||||||
|
=== RUN TestNginxHTTP2Proxy/TLSAndConnectionReuse
|
||||||
|
proxy_test.go:163: proxyResult=tlsAndReuse reusedRequests=32 upstreamConnections=1 invalidTrustStatus=502 invalidNameStatus=502 recovered=true
|
||||||
|
=== RUN TestNginxHTTP2Proxy/TimeoutAndCancellation
|
||||||
|
proxy_test.go:206: proxyResult=timeoutAndCancellation timeoutStatus=504 canceledHandlers=2 recovered=true stats={AcceptedConnections:3 RejectedConnections:0 RejectedRequests:0 ProtocolErrors:0 GoAwaySent:0 RSTStreams:0 GoAwayFrames:0 ActiveConnections:1 ActiveRequests:0}
|
||||||
|
=== RUN TestNginxHTTP2Proxy/GoAwayAndRollingDrain
|
||||||
|
proxy_test.go:281: proxyResult=goAwayAndRollingDrain oldRequests=2 newRequests=20 heldPostCompleted=true duplicateHandlers=0 oldStats={AcceptedConnections:1 RejectedConnections:0 RejectedRequests:0 ProtocolErrors:0 GoAwaySent:1 RSTStreams:0 GoAwayFrames:1 ActiveConnections:0 ActiveRequests:0}
|
||||||
|
--- PASS: TestNginxHTTP2Proxy (0.81s)
|
||||||
|
--- PASS: TestNginxHTTP2Proxy/TLSAndConnectionReuse (0.17s)
|
||||||
|
--- PASS: TestNginxHTTP2Proxy/TimeoutAndCancellation (0.45s)
|
||||||
|
--- PASS: TestNginxHTTP2Proxy/GoAwayAndRollingDrain (0.20s)
|
||||||
|
=== RUN TestNginxHTTP2Proxy
|
||||||
|
proxy_test.go:37: proxyVersion=nginx version: nginx/1.30.5
|
||||||
|
=== RUN TestNginxHTTP2Proxy/TLSAndConnectionReuse
|
||||||
|
proxy_test.go:163: proxyResult=tlsAndReuse reusedRequests=32 upstreamConnections=1 invalidTrustStatus=502 invalidNameStatus=502 recovered=true
|
||||||
|
=== RUN TestNginxHTTP2Proxy/TimeoutAndCancellation
|
||||||
|
proxy_test.go:206: proxyResult=timeoutAndCancellation timeoutStatus=504 canceledHandlers=2 recovered=true stats={AcceptedConnections:3 RejectedConnections:0 RejectedRequests:0 ProtocolErrors:0 GoAwaySent:0 RSTStreams:0 GoAwayFrames:0 ActiveConnections:1 ActiveRequests:0}
|
||||||
|
=== RUN TestNginxHTTP2Proxy/GoAwayAndRollingDrain
|
||||||
|
proxy_test.go:281: proxyResult=goAwayAndRollingDrain oldRequests=2 newRequests=20 heldPostCompleted=true duplicateHandlers=0 oldStats={AcceptedConnections:1 RejectedConnections:0 RejectedRequests:0 ProtocolErrors:0 GoAwaySent:1 RSTStreams:0 GoAwayFrames:1 ActiveConnections:0 ActiveRequests:0}
|
||||||
|
--- PASS: TestNginxHTTP2Proxy (0.89s)
|
||||||
|
--- PASS: TestNginxHTTP2Proxy/TLSAndConnectionReuse (0.21s)
|
||||||
|
--- PASS: TestNginxHTTP2Proxy/TimeoutAndCancellation (0.44s)
|
||||||
|
--- PASS: TestNginxHTTP2Proxy/GoAwayAndRollingDrain (0.23s)
|
||||||
|
PASS
|
||||||
147
performance/http2ProxyRaceLinux.json
Normal file
147
performance/http2ProxyRaceLinux.json
Normal file
@@ -0,0 +1,147 @@
|
|||||||
|
{
|
||||||
|
"status": "passed",
|
||||||
|
"startedAt": "2026-09-23T03:11:00.470197+00:00",
|
||||||
|
"testedCommit": "b54a303ccdc034cd3062e846c6c6b2c24e55ffaa",
|
||||||
|
"workingTreeStatus": "",
|
||||||
|
"sourceSha256": {
|
||||||
|
"go.mod": "28d68eb2c0a019cceea884e88ab2335f44ad930e1890e28871638f4b073b4f86",
|
||||||
|
"go.sum": "d9c2a5d0de94f08bd18a14a9626f9dda559c245d89bf8a689e32acbd2136fb05",
|
||||||
|
"scripts/verifyHTTP2Proxy.py": "9ef2774b1d19c05f1ae8d9527665dba541561a7c677f9984a24e72d5bddb6ad5",
|
||||||
|
"transport/http2/admission_test.go": "342a7d02bba508100a207b77246ca92e0fa04dd5f8e4a27091a01866b6750d39",
|
||||||
|
"transport/http2/flowControl_test.go": "595b2e937cef2142ff6b6386116e338d874d0fe49e63d80f6de2e1268378dba2",
|
||||||
|
"transport/http2/frameObserver.go": "fcdf431f8269ad9cfd65dd14102ce5c42ac8c4b8b833ed42c4ae15f2a7c7f14c",
|
||||||
|
"transport/http2/lifecycle_test.go": "370a16de8b20248bb78021ab338b9f62ec51f733f270c0fa92f48b1a3ccb9eae",
|
||||||
|
"transport/http2/mixedSoak_test.go": "2111737fe0405bc809bf06325573f7356160ae7fcaebcce06dcd94b2d9e7dccd",
|
||||||
|
"transport/http2/proxy_test.go": "808d0fa7e6c3b0237b1adf4a71a4f30ae2dbc7d1a9d53fab164cda33b15c9d69",
|
||||||
|
"transport/http2/server.go": "aeecdc3aec42219862d70ec788bef5df332189895555bc5d390849a51bd0fbfb",
|
||||||
|
"transport/http2/server_test.go": "c4613e77fa885b6362c06c393937c7dcf9a7ffb4a9a32f85e21fc2f741de81a6",
|
||||||
|
"transport/http2/soak_test.go": "52baa37e37dba497c579b6b55d723bda866ac57676254dc878c511345acc5af2"
|
||||||
|
},
|
||||||
|
"repetitions": 3,
|
||||||
|
"race": true,
|
||||||
|
"containerLimits": {
|
||||||
|
"cpus": 2,
|
||||||
|
"memoryBytes": 2147483648,
|
||||||
|
"memoryAndSwapBytes": 2147483648,
|
||||||
|
"pidsLimit": 256
|
||||||
|
},
|
||||||
|
"wallTimeoutSeconds": 600,
|
||||||
|
"imageIds": {
|
||||||
|
"golang:1.25": "sha256:699337d620559a59b4a2bb298ad59611e535d2ee755a34cf2d2a98f37578dc80",
|
||||||
|
"nginx:1.30.5": "sha256:0aa2d81d65bc0cac0407e738b8f07d312c8685a84225fcb4db7bcbdd8c9bdf11"
|
||||||
|
},
|
||||||
|
"notes": [
|
||||||
|
"Compiler and integration stages run sequentially; at most one 2-CPU/2-GiB validation container is active.",
|
||||||
|
"The integration container has network=none, no published ports and a read-only root filesystem. Client, Nginx and origins communicate over loopback inside that container.",
|
||||||
|
"Both TLS legs validate certificates; invalid upstream CA and hostname must fail before reaching the handler.",
|
||||||
|
"HTTP/2 is verified independently on both legs. This is a functional integration test, not a throughput benchmark or a live Cloudflare deployment.",
|
||||||
|
"Single-process Nginx fixture with generated test certificates. Rolling drain switches between old and backup origins; this does not test Nginx master/worker configuration reloads or live Cloudflare routing."
|
||||||
|
],
|
||||||
|
"proxyVersions": [
|
||||||
|
"nginx version: nginx/1.30.5"
|
||||||
|
],
|
||||||
|
"results": [
|
||||||
|
"tlsAndReuse reusedRequests=32 upstreamConnections=1 invalidTrustStatus=502 invalidNameStatus=502 recovered=true",
|
||||||
|
"timeoutAndCancellation timeoutStatus=504 canceledHandlers=2 recovered=true stats={AcceptedConnections:3 RejectedConnections:0 RejectedRequests:0 ProtocolErrors:0 GoAwaySent:0 RSTStreams:0 GoAwayFrames:0 ActiveConnections:1 ActiveRequests:0}",
|
||||||
|
"goAwayAndRollingDrain oldRequests=2 newRequests=20 heldPostCompleted=true duplicateHandlers=0 oldStats={AcceptedConnections:1 RejectedConnections:0 RejectedRequests:0 ProtocolErrors:0 GoAwaySent:1 RSTStreams:0 GoAwayFrames:1 ActiveConnections:0 ActiveRequests:0}",
|
||||||
|
"tlsAndReuse reusedRequests=32 upstreamConnections=1 invalidTrustStatus=502 invalidNameStatus=502 recovered=true",
|
||||||
|
"timeoutAndCancellation timeoutStatus=504 canceledHandlers=2 recovered=true stats={AcceptedConnections:3 RejectedConnections:0 RejectedRequests:0 ProtocolErrors:0 GoAwaySent:0 RSTStreams:0 GoAwayFrames:0 ActiveConnections:1 ActiveRequests:0}",
|
||||||
|
"goAwayAndRollingDrain oldRequests=2 newRequests=20 heldPostCompleted=true duplicateHandlers=0 oldStats={AcceptedConnections:1 RejectedConnections:0 RejectedRequests:0 ProtocolErrors:0 GoAwaySent:1 RSTStreams:0 GoAwayFrames:1 ActiveConnections:0 ActiveRequests:0}",
|
||||||
|
"tlsAndReuse reusedRequests=32 upstreamConnections=1 invalidTrustStatus=502 invalidNameStatus=502 recovered=true",
|
||||||
|
"timeoutAndCancellation timeoutStatus=504 canceledHandlers=2 recovered=true stats={AcceptedConnections:3 RejectedConnections:0 RejectedRequests:0 ProtocolErrors:0 GoAwaySent:0 RSTStreams:0 GoAwayFrames:0 ActiveConnections:1 ActiveRequests:0}",
|
||||||
|
"goAwayAndRollingDrain oldRequests=2 newRequests=20 heldPostCompleted=true duplicateHandlers=0 oldStats={AcceptedConnections:1 RejectedConnections:0 RejectedRequests:0 ProtocolErrors:0 GoAwaySent:1 RSTStreams:0 GoAwayFrames:1 ActiveConnections:0 ActiveRequests:0}"
|
||||||
|
],
|
||||||
|
"goVersion": "go1.25.14",
|
||||||
|
"platform": "linux/amd64",
|
||||||
|
"commands": [
|
||||||
|
[
|
||||||
|
"/usr/bin/docker",
|
||||||
|
"pull",
|
||||||
|
"nginx:1.30.5"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"/usr/bin/docker",
|
||||||
|
"run",
|
||||||
|
"--rm",
|
||||||
|
"--name",
|
||||||
|
"go-omni-http2-proxy-ca37da979ea6-build",
|
||||||
|
"--cpus",
|
||||||
|
"2",
|
||||||
|
"--memory",
|
||||||
|
"2048m",
|
||||||
|
"--memory-swap",
|
||||||
|
"2048m",
|
||||||
|
"--pids-limit",
|
||||||
|
"256",
|
||||||
|
"--log-opt",
|
||||||
|
"max-size=10m",
|
||||||
|
"--log-opt",
|
||||||
|
"max-file=1",
|
||||||
|
"-v",
|
||||||
|
"/root/go-omni-validation:/workspace:ro",
|
||||||
|
"-v",
|
||||||
|
"/tmp/goOmniHTTP2Proxy8ll9pvvs:/output",
|
||||||
|
"-w",
|
||||||
|
"/workspace",
|
||||||
|
"-e",
|
||||||
|
"GOWORK=off",
|
||||||
|
"-e",
|
||||||
|
"GOMAXPROCS=2",
|
||||||
|
"-e",
|
||||||
|
"CGO_ENABLED=1",
|
||||||
|
"-e",
|
||||||
|
"GOPROXY=https://goproxy.cn,direct",
|
||||||
|
"golang:1.25",
|
||||||
|
"sh",
|
||||||
|
"-c",
|
||||||
|
"go version && go vet ./transport/http2 && go test -race -c -o /output/http2.test ./transport/http2"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"/usr/bin/docker",
|
||||||
|
"run",
|
||||||
|
"--rm",
|
||||||
|
"--name",
|
||||||
|
"go-omni-http2-proxy-ca37da979ea6-check",
|
||||||
|
"--cpus",
|
||||||
|
"2",
|
||||||
|
"--memory",
|
||||||
|
"2048m",
|
||||||
|
"--memory-swap",
|
||||||
|
"2048m",
|
||||||
|
"--pids-limit",
|
||||||
|
"256",
|
||||||
|
"--log-opt",
|
||||||
|
"max-size=10m",
|
||||||
|
"--log-opt",
|
||||||
|
"max-file=1",
|
||||||
|
"--network",
|
||||||
|
"none",
|
||||||
|
"--read-only",
|
||||||
|
"--tmpfs",
|
||||||
|
"/tmp:rw,nosuid,noexec,size=128m",
|
||||||
|
"-v",
|
||||||
|
"/tmp/goOmniHTTP2Proxy8ll9pvvs:/test:ro",
|
||||||
|
"--entrypoint",
|
||||||
|
"/test/http2.test",
|
||||||
|
"-e",
|
||||||
|
"GO_OMNI_HTTP2_PROXY_TEST=1",
|
||||||
|
"-e",
|
||||||
|
"GOMAXPROCS=2",
|
||||||
|
"nginx:1.30.5",
|
||||||
|
"-test.v",
|
||||||
|
"-test.run",
|
||||||
|
"^TestNginxHTTP2Proxy$",
|
||||||
|
"-test.timeout",
|
||||||
|
"120s",
|
||||||
|
"-test.count",
|
||||||
|
"3"
|
||||||
|
]
|
||||||
|
],
|
||||||
|
"cleanedContainers": [
|
||||||
|
"go-omni-http2-proxy-ca37da979ea6-build",
|
||||||
|
"go-omni-http2-proxy-ca37da979ea6-check"
|
||||||
|
],
|
||||||
|
"finishedAt": "2026-09-23T03:12:51.684028+00:00",
|
||||||
|
"logSha256": "aaae7678dd4f882c51c008eaeb78afd897ccf2f7ba31c926a40839de755bb595",
|
||||||
|
"preExistingContainersStillRunning": 7
|
||||||
|
}
|
||||||
67
performance/http2ProxyRaceLinux.txt
Normal file
67
performance/http2ProxyRaceLinux.txt
Normal file
@@ -0,0 +1,67 @@
|
|||||||
|
1.30.5: Pulling from library/nginx
|
||||||
|
757049f14b94: Pulling fs layer
|
||||||
|
af529266d394: Pulling fs layer
|
||||||
|
3a55ec62cf16: Pulling fs layer
|
||||||
|
2aa35770407f: Pulling fs layer
|
||||||
|
f3cb4f951034: Pulling fs layer
|
||||||
|
6b37362b3da7: Pulling fs layer
|
||||||
|
b9614c6dbd86: Pulling fs layer
|
||||||
|
2aa35770407f: Download complete
|
||||||
|
d6363fd2ac35: Download complete
|
||||||
|
3a55ec62cf16: Download complete
|
||||||
|
02ac47b2a7a6: Download complete
|
||||||
|
af529266d394: Download complete
|
||||||
|
757049f14b94: Download complete
|
||||||
|
f3cb4f951034: Download complete
|
||||||
|
6b37362b3da7: Download complete
|
||||||
|
6b37362b3da7: Pull complete
|
||||||
|
b9614c6dbd86: Download complete
|
||||||
|
3a55ec62cf16: Pull complete
|
||||||
|
f3cb4f951034: Pull complete
|
||||||
|
b9614c6dbd86: Pull complete
|
||||||
|
2aa35770407f: Pull complete
|
||||||
|
af529266d394: Pull complete
|
||||||
|
757049f14b94: Pull complete
|
||||||
|
Digest: sha256:0aa2d81d65bc0cac0407e738b8f07d312c8685a84225fcb4db7bcbdd8c9bdf11
|
||||||
|
Status: Downloaded newer image for nginx:1.30.5
|
||||||
|
docker.io/library/nginx:1.30.5
|
||||||
|
go version go1.25.14 linux/amd64
|
||||||
|
go: downloading golang.org/x/net v0.25.0
|
||||||
|
go: downloading golang.org/x/text v0.15.0
|
||||||
|
=== RUN TestNginxHTTP2Proxy
|
||||||
|
proxy_test.go:37: proxyVersion=nginx version: nginx/1.30.5
|
||||||
|
=== RUN TestNginxHTTP2Proxy/TLSAndConnectionReuse
|
||||||
|
proxy_test.go:163: proxyResult=tlsAndReuse reusedRequests=32 upstreamConnections=1 invalidTrustStatus=502 invalidNameStatus=502 recovered=true
|
||||||
|
=== RUN TestNginxHTTP2Proxy/TimeoutAndCancellation
|
||||||
|
proxy_test.go:206: proxyResult=timeoutAndCancellation timeoutStatus=504 canceledHandlers=2 recovered=true stats={AcceptedConnections:3 RejectedConnections:0 RejectedRequests:0 ProtocolErrors:0 GoAwaySent:0 RSTStreams:0 GoAwayFrames:0 ActiveConnections:1 ActiveRequests:0}
|
||||||
|
=== RUN TestNginxHTTP2Proxy/GoAwayAndRollingDrain
|
||||||
|
proxy_test.go:281: proxyResult=goAwayAndRollingDrain oldRequests=2 newRequests=20 heldPostCompleted=true duplicateHandlers=0 oldStats={AcceptedConnections:1 RejectedConnections:0 RejectedRequests:0 ProtocolErrors:0 GoAwaySent:1 RSTStreams:0 GoAwayFrames:1 ActiveConnections:0 ActiveRequests:0}
|
||||||
|
--- PASS: TestNginxHTTP2Proxy (0.92s)
|
||||||
|
--- PASS: TestNginxHTTP2Proxy/TLSAndConnectionReuse (0.31s)
|
||||||
|
--- PASS: TestNginxHTTP2Proxy/TimeoutAndCancellation (0.41s)
|
||||||
|
--- PASS: TestNginxHTTP2Proxy/GoAwayAndRollingDrain (0.19s)
|
||||||
|
=== RUN TestNginxHTTP2Proxy
|
||||||
|
proxy_test.go:37: proxyVersion=nginx version: nginx/1.30.5
|
||||||
|
=== RUN TestNginxHTTP2Proxy/TLSAndConnectionReuse
|
||||||
|
proxy_test.go:163: proxyResult=tlsAndReuse reusedRequests=32 upstreamConnections=1 invalidTrustStatus=502 invalidNameStatus=502 recovered=true
|
||||||
|
=== RUN TestNginxHTTP2Proxy/TimeoutAndCancellation
|
||||||
|
proxy_test.go:206: proxyResult=timeoutAndCancellation timeoutStatus=504 canceledHandlers=2 recovered=true stats={AcceptedConnections:3 RejectedConnections:0 RejectedRequests:0 ProtocolErrors:0 GoAwaySent:0 RSTStreams:0 GoAwayFrames:0 ActiveConnections:1 ActiveRequests:0}
|
||||||
|
=== RUN TestNginxHTTP2Proxy/GoAwayAndRollingDrain
|
||||||
|
proxy_test.go:281: proxyResult=goAwayAndRollingDrain oldRequests=2 newRequests=20 heldPostCompleted=true duplicateHandlers=0 oldStats={AcceptedConnections:1 RejectedConnections:0 RejectedRequests:0 ProtocolErrors:0 GoAwaySent:1 RSTStreams:0 GoAwayFrames:1 ActiveConnections:0 ActiveRequests:0}
|
||||||
|
--- PASS: TestNginxHTTP2Proxy (1.14s)
|
||||||
|
--- PASS: TestNginxHTTP2Proxy/TLSAndConnectionReuse (0.28s)
|
||||||
|
--- PASS: TestNginxHTTP2Proxy/TimeoutAndCancellation (0.56s)
|
||||||
|
--- PASS: TestNginxHTTP2Proxy/GoAwayAndRollingDrain (0.30s)
|
||||||
|
=== RUN TestNginxHTTP2Proxy
|
||||||
|
proxy_test.go:37: proxyVersion=nginx version: nginx/1.30.5
|
||||||
|
=== RUN TestNginxHTTP2Proxy/TLSAndConnectionReuse
|
||||||
|
proxy_test.go:163: proxyResult=tlsAndReuse reusedRequests=32 upstreamConnections=1 invalidTrustStatus=502 invalidNameStatus=502 recovered=true
|
||||||
|
=== RUN TestNginxHTTP2Proxy/TimeoutAndCancellation
|
||||||
|
proxy_test.go:206: proxyResult=timeoutAndCancellation timeoutStatus=504 canceledHandlers=2 recovered=true stats={AcceptedConnections:3 RejectedConnections:0 RejectedRequests:0 ProtocolErrors:0 GoAwaySent:0 RSTStreams:0 GoAwayFrames:0 ActiveConnections:1 ActiveRequests:0}
|
||||||
|
=== RUN TestNginxHTTP2Proxy/GoAwayAndRollingDrain
|
||||||
|
proxy_test.go:281: proxyResult=goAwayAndRollingDrain oldRequests=2 newRequests=20 heldPostCompleted=true duplicateHandlers=0 oldStats={AcceptedConnections:1 RejectedConnections:0 RejectedRequests:0 ProtocolErrors:0 GoAwaySent:1 RSTStreams:0 GoAwayFrames:1 ActiveConnections:0 ActiveRequests:0}
|
||||||
|
--- PASS: TestNginxHTTP2Proxy (0.92s)
|
||||||
|
--- PASS: TestNginxHTTP2Proxy/TLSAndConnectionReuse (0.31s)
|
||||||
|
--- PASS: TestNginxHTTP2Proxy/TimeoutAndCancellation (0.44s)
|
||||||
|
--- PASS: TestNginxHTTP2Proxy/GoAwayAndRollingDrain (0.16s)
|
||||||
|
PASS
|
||||||
45
transport/http2/PROXY.md
Normal file
45
transport/http2/PROXY.md
Normal 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 默认路径保持现状。
|
||||||
@@ -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。
|
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)。
|
连接级流控及关闭边界测试已通过 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 的默认行为。
|
- 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 连接仍可继续。
|
- 流控:慢读取、慢写、窗口耗尽和大 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 与服务关闭,包括客户端零接收窗口造成写阻塞时,关闭超时强制解除写阻塞、取消请求并回收连接。
|
- 取消:RST_STREAM、客户端断开、服务关闭分别只影响应受影响的请求,并能在 metrics 中看到回收;当前实验 Runner 已覆盖 RST_STREAM 与服务关闭,包括客户端零接收窗口造成写阻塞时,关闭超时强制解除写阻塞、取消请求并回收连接。
|
||||||
- 背压:连接上限、全局 stream 上限、单连接 stream 上限和 header/body 上限均能快速拒绝并自动恢复。实验 Runner 已增加 TLS 未完成握手占满连接槽位时的及时拒绝、握手超时回收及后续正常 HTTPS 请求恢复验收。
|
- 背压:连接上限、全局 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 连接慢上传/慢下载混合场景均已通过,原始日志与采样分别保存在两个报告中。
|
- 长测: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。
|
||||||
|
|||||||
Reference in New Issue
Block a user