HTTP/2 随机连接失败问题总结

0 阅读2分钟

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 队头阻塞)
  • 网络环境不好时:零容错

3.jpg

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个文件全部失败

解决方案

配置项设置作用
sendfileoff让内核精确控制切包,避免网卡Bug
use-http2false改用 HTTP/1.1 多连接,分散风险

为什么不修底层网络?

  • K8s 集群网络配置复杂
  • 可能涉及物理交换机、网卡驱动
  • 在应用层打补丁成本最低

代价:

  • 稍微慢一点点
  • 多占点服务器资源

收益:

  • 稳定性大幅提升
  • 不再随机失败

核心知识点

MTU(最大传输单元)

网卡一次最多能发多大的数据包,就像快递箱的尺寸限制。

DF 位(Don't Fragment)

IP包上的标记,意思是"不许切割"。如果包太大又不让切,就只能丢弃。

TCP 队头阻塞

TCP 保证数据按顺序到达。如果第 10 个包丢了,后面第 11-50 个包即使收到了也不给应用层,必须等第 10 个包重传成功。

HTTP/2 单连接复用

所有请求共用 1 个 TCP 连接,好网络下很高效,烂网络下一损俱损。


总结

这是一个网络环境质量差 + HTTP/2 零容错的组合问题。

最佳实践:

  1. 好网络环境 → 用 HTTP/2(快)
  2. 烂网络环境 → 用 HTTP/1.1(稳)

本案例选择了用 HTTP/1.1 牺牲一点速度,换取稳定性。