HTTP/2 随机连接失败问题总结
问题现象
第一阶段:连接直接断
- 报错:
net::ERR_CONNECTION_RESET - 原因:sendfile 开启,网卡硬件有Bug,数据包被丢弃
第二阶段:随机失败
- 报错:关闭 sendfile 后,变成偶发的
net::ERR_FAILED - 表现:有时能打开,有时打不开
- 原因:HTTP/2 太脆弱
根本原因
1. K8s 网络把"管道"变窄了
graph LR
A[物理网卡 MTU: 1500字节] --> B[K8s隧道头占用: 50字节]
B --> C[应用实际可用: 1450字节]
style A fill:#90EE90
style B fill:#FFB6C1
style C fill:#87CEEB
大白话解释:
- 原本有 1500 字节的通道
- K8s 的网络封装(像快递包装盒)占了 50 字节
- 应用只能用剩下的 1450 字节
- 就像电梯限重 1500 斤,管理员占了 50 斤,乘客只能用 1450 斤
2. sendfile 的作用
| 配置 | 数据怎么发 | 优缺点 |
|---|---|---|
| sendfile on | 文件直接从磁盘发到网卡 | 快,但网卡可能切包切错 |
| sendfile off | 文件先读到内存,Linux内核控制切包 | 慢一点,但切包精准安全 |
大白话:
sendfile on= 网卡自己随便切蛋糕,可能切太大sendfile off= 内核用尺子量好再切,不会超标
3. HTTP/2 的双刃剑
HTTP/2 的问题:
- 所有资源走 1 个连接(独木桥)
- 丢 1 个包 → 全部卡死(TCP 队头阻塞)
- 网络环境不好时:零容错
HTTP/1.1 的优势:
- 开 6 个连接(6 座小桥)
- 丢 1 个包 → 只影响 1/6
- 容错性强
故障发生流程
sequenceDiagram
participant 浏览器
participant Nginx
participant 网络
浏览器->>Nginx: HTTP/2 请求50个文件
Nginx->>网络: 发送数据包1-50
Note over 网络: 网络抖动/MTU问题
网络--xNginx: 第10个包丢了
Note over Nginx: TCP等待第10个包的ACK<br/>后面49个包都堵住了
浏览器->>浏览器: 连接卡住不动
浏览器->>Nginx: 超时,断开连接
Note over 浏览器: ERR_FAILED<br/>所有50个文件全部失败
解决方案
| 配置项 | 设置 | 作用 |
|---|---|---|
| sendfile | off | 让内核精确控制切包,避免网卡Bug |
| use-http2 | false | 改用 HTTP/1.1 多连接,分散风险 |
为什么不修底层网络?
- K8s 集群网络配置复杂
- 可能涉及物理交换机、网卡驱动
- 在应用层打补丁成本最低
代价:
- 稍微慢一点点
- 多占点服务器资源
收益:
- 稳定性大幅提升
- 不再随机失败
核心知识点
MTU(最大传输单元)
网卡一次最多能发多大的数据包,就像快递箱的尺寸限制。
DF 位(Don't Fragment)
IP包上的标记,意思是"不许切割"。如果包太大又不让切,就只能丢弃。
TCP 队头阻塞
TCP 保证数据按顺序到达。如果第 10 个包丢了,后面第 11-50 个包即使收到了也不给应用层,必须等第 10 个包重传成功。
HTTP/2 单连接复用
所有请求共用 1 个 TCP 连接,好网络下很高效,烂网络下一损俱损。
总结
这是一个网络环境质量差 + HTTP/2 零容错的组合问题。
最佳实践:
- 好网络环境 → 用 HTTP/2(快)
- 烂网络环境 → 用 HTTP/1.1(稳)
本案例选择了用 HTTP/1.1 牺牲一点速度,换取稳定性。