三亩地 三亩地SAN MU DI · CODE DIARY
ARTICLE DETAIL

日记详情

真实记录编程学习的某一天,欢迎挑你感兴趣的翻一翻。

ALB Streaming 接口:HTTP/1.1 和 HTTPS/HTTP2 的 time_starttransfer 差异

ALB Streaming 接口:HTTP/1.1 和 HTTPS/HTTP2 的 time_starttransfer 差异

文章目录

  1. 环境与现象
  2. 验证命令
  3. 20 次测试结果
  4. 为什么 HTTP/2 的 starttransfer 会更早
  5. 用另一个 streaming 接口做交叉验证
  6. 如何无损新增 HTTPS/HTTP2
  7. 验证 HTTPS/HTTP2 是否生效
  8. 快速参考

1. 环境与现象

这次排查的是一个 streaming 接口的入口差异。公网入口走 HTTPS 443 + HTTP/2,VPC 内网入口走 HTTP 80 + HTTP/1.1。业务请求都是 stream=true,后端生成内容的总耗时接近,但 curl 看到的 time_starttransfer 差很多。

Streaming 接口公网和 VPC 内网入口对比

环境如下:

项目 公网入口 VPC 内网入口
协议 HTTPS HTTP
端口 443 80
HTTP 版本 HTTP/2 HTTP/1.1
ALB Gzip 开启 开启
ALB IdleTimeout 60s 60s
ALB RequestTimeout 300s 300s
请求类型 streaming streaming

这类现象容易被误判为“内网比公网慢 2 秒”。实际要先拆开两个指标:

  • time_starttransfer:curl 收到第一个字节的时间。
  • time_total:整个请求结束的总耗时。

对 streaming 接口来说,time_starttransfer 不一定等于“模型开始吐第一个有效 content chunk 的时间”。

2. 验证命令

验证时响应体全部丢弃,只看状态码、HTTP 版本和 timing:

curl -sS -o /dev/null \--connect-timeout 5 \--max-time 20 \-w 'code=%{http_code} version=%{http_version} namelookup=%{time_namelookup} connect=%{time_connect} appconnect=%{time_appconnect} starttransfer=%{time_starttransfer} total=%{time_total} remote=%{remote_ip} err=%{errormsg}\n' \-H "Content-Type: application/json" \-H "Authorization: Bearer <TOKEN>" \-d '{"model":"<MODEL>","messages":[{"role":"user","content":"ping"}],"stream":true}' \"https://<PUBLIC_HOST>/v1/chat/completions"

VPC 内网入口同样测试:

curl -sS -o /dev/null \--connect-timeout 5 \--max-time 20 \-w 'code=%{http_code} version=%{http_version} namelookup=%{time_namelookup} connect=%{time_connect} appconnect=%{time_appconnect} starttransfer=%{time_starttransfer} total=%{time_total} remote=%{remote_ip} err=%{errormsg}\n' \-H "Content-Type: application/json" \-H "Authorization: Bearer <TOKEN>" \-d '{"model":"<MODEL>","messages":[{"role":"user","content":"ping"}],"stream":true}' \"http://<VPC_HOST>/v1/chat/completions"

注意两点:

  • 不要把 token 打到终端或日志里。
  • streaming 接口要同时看 versionstarttransfertotal,不要只看一个字段。

3. 20 次测试结果

从源 ECS 跑 20 次,结果如下。

公网入口:

20/20 成功,HTTP 200
starttransfer: 0.047s - 0.064s
total 平均值: 约 1.99s

VPC 内网入口:

20/20 成功,HTTP 200
starttransfer: 1.37s - 2.23s
total 平均值: 约 1.89s

结论很直接:

  • VPC 内网入口总耗时没有比公网慢,基本持平。
  • 差异主要集中在 time_starttransfer
  • 公网 HTTPS + HTTP/2starttransfer 约 0.05s。
  • VPC HTTP + HTTP/1.1starttransfer 约 1.3s - 2.4s。

4. 为什么 HTTP/2 的 starttransfer 会更早

先区分三个概念:

写法 TLS HTTP 版本 说明
HTTP/1.1 无 TLS HTTP/1.1 明文 HTTP,常见 80 端口
HTTPS/1.1 有 TLS HTTP/1.1 加密了,但应用层还是 HTTP/1.1
HTTPS/2 有 TLS HTTP/2 TLS 握手里通过 ALPN 协商 HTTP/2

HTTPS 不等于 HTTP/2。HTTPS 只是 TLS 加密;HTTP/2 是应用层协议版本。一般浏览器和 curl 访问 HTTPS 时会通过 ALPN 协商,如果服务端支持,才会走 HTTP/2。

对普通非流式接口来说,time_starttransfer 通常接近后端开始返回响应的时间。对 streaming 接口来说,情况更复杂:

time_starttransfer 在 HTTP/1.1 和 HTTP/2 下的差异

  • HTTP/2 可以更早返回响应头或 HTTP/2 frame。
  • curl 收到第一个响应字节后,就会记录 time_starttransfer
  • 这个“第一个字节”可能还不是业务上第一个有效 content chunk。
  • HTTP/1.1 下常见表现是更接近首个有效 chunk 到达时才记录首字节。

所以这次看到的现象不是“内网链路慢 2 秒”,而是两套入口的协议行为不同。

更准确的业务体感指标应该是:

  • 总耗时 time_total
  • 首个有效 content chunk 时间
  • 端到端应用日志里的首 token / 首 chunk 时间

5. 用另一个 streaming 接口做交叉验证

为了确认这不是单个服务偶然现象,又用另一个 streaming 接口做了验证。该接口的内网入口原本是 HTTP/1.1,后来给同一个内网 ALB 新增了 HTTPS 443 + HTTP/2

新增后同一域名同时支持:

http://<INTERNAL_HOST>/...   -> 80  HTTP/1.1
https://<INTERNAL_HOST>/...  -> 443 HTTPS + HTTP/2

验证结果:

HTTP 80:
code=200
version=1.1
starttransfer=1.71s - 2.51sHTTPS 443:
code=200
version=2
starttransfer=0.053s - 0.054s

这说明在同一个内网入口上,新增 HTTPS/HTTP2 后,time_starttransfer 确实会从秒级降到几十毫秒级。

6. 如何无损新增 HTTPS/HTTP2

最小影响方案是只新增 443,不动 80:

80  HTTP/1.1   保留,兼容旧调用
443 HTTPS/2    新增,给需要 streaming 体感一致的客户端使用

如果 ALB 是手工管理,新增 HTTPS listener 时配置:

协议: HTTPS
端口: 443
HTTP/2: 开启
Gzip: 保持开启
证书: 覆盖内网域名
转发动作: 指向与 80 Host 规则相同的 ServerGroup

如果 ALB 是 ACK ALB Ingress Controller 管理,不建议直接在 ALB 控制台改。应改 AlbConfigIngress

Ingress 上常见写法:

metadata:annotations:alb.ingress.kubernetes.io/listen-ports: '[{"HTTP":80},{"HTTPS":443}]'

TLS host:

spec:tls:- hosts:- <VPC_HOST>

原则:

  • 不删除 80。
  • 不配置 HTTP 到 HTTPS 强制跳转。
  • 不改 DNS。
  • 不改后端 Service。
  • 443 先和 80 指向同一个后端,再做验证。

7. 验证 HTTPS/HTTP2 是否生效

新增 443 后,从源 ECS 测:

curl --http2 -sS -o /dev/null \-w 'code=%{http_code} version=%{http_version} starttransfer=%{time_starttransfer} total=%{time_total}\n' \-H "Content-Type: application/json" \-H "Authorization: Bearer <TOKEN>" \-d '{"model":"<MODEL>","messages":[{"role":"user","content":"ping"}],"stream":true}' \"https://<VPC_HOST>/v1/chat/completions"

预期:

code=200
version=2
starttransfer 接近 0.05s - 0.1s
total 不明显变差

再回归 HTTP:

curl -sS -o /dev/null \-w 'code=%{http_code} version=%{http_version} starttransfer=%{time_starttransfer} total=%{time_total}\n' \-H "Content-Type: application/json" \-H "Authorization: Bearer <TOKEN>" \-d '{"model":"<MODEL>","messages":[{"role":"user","content":"ping"}],"stream":true}' \"http://<VPC_HOST>/v1/chat/completions"

预期:

HTTP 仍可用
version=1.1

8. 快速参考

判断一个 streaming 入口是不是 HTTP/2:

curl --http2 -sS -o /dev/null \-w 'code=%{http_code} version=%{http_version} starttransfer=%{time_starttransfer} total=%{time_total}\n' \"https://<HOST>/<PATH>"

如果 version=2,说明 HTTP/2 生效。

排查顺序:

1. 看 DNS 指向哪个 ALB
2. 看 listener: 80 HTTP 还是 443 HTTPS
3. 看 HTTPS listener 是否 Http2Enabled=true
4. 看 Host 转发规则,不要只看默认转发
5. 确认 80 和 443 是否转发到同一个 ServerGroup
6. 跑 20 次 curl,比较 version/starttransfer/total

几个容易踩的点:

  • HTTPS 不等于 HTTP/2。
  • time_starttransfer 不等于首个有效 content chunk。
  • ALB listener 默认转发不一定是实际命中的转发,Host 规则会覆盖默认转发。
  • 新增 443 时先保留 80,避免影响旧客户端。
  • ACK 管理的 ALB 要改 AlbConfig/Ingress,不要只在 ALB 控制台手工改。
← 返回列表