跳转至

eBPF 排查 TCP 传输卡顿与连接清理

请求变慢不总是握手慢。连接已经建立后,可以用发送窗口、未发送/未确认/未读队列、ACK 推进和内核 RTT 区分排查方向,再与两端应用证据对齐。

查询入口与实测环境见 网络排障实践总览。TCP 队列字段只有在 queue_values_available=true 且快照足够新时才能解释;样本中的 at_ns 与 snapshot_at_ns 都是纳秒时间,差值用于判断观测当时的快照年龄;不要用查询当天的时间判断历史样本是否有效。关闭终态可能将这些字段置零并标为不可用,不能用终态零值证明没有压力。

场景一:对端窗口为零,发送数据堆积

下面是实测日志 message 解析后的关键字段节选。测试对端暂停读取,客户端继续发送:

{
  "schema": "ebpf_network_event/v1",
  "entity": {
    "scope": "connection",
    "netns": "4026531840",
    "pid": 124,
    "process_name": "fixture",
    "service": "fixture",
    "protocol": "tcp",
    "role": "client",
    "role_source": "connect",
    "remote_ip": "10.23.0.1",
    "service_port": 40016
  },
  "problem_type": "tcp_zero_window_probe",
  "occurrences": 4,
  "evidence_partial": false,
  "samples": [
    {
      "local": "10.23.0.1:50330",
      "remote": "10.23.0.1:40016",
      "at_ns": 1791779039232233570,
      "snapshot_at_ns": 1791779038712695250,
      "stage": "transfer",
      "state": "ESTABLISHED",
      "srtt_snapshot_us": 5995,
      "unacked_bytes": 0,
      "unsent_bytes": 259072,
      "unread_bytes": 0,
      "send_window_bytes": 0,
      "queue_values_available": true,
      "probe0_calls": 4
    }
  ]
}
观测 当前值 意义
状态与阶段 ESTABLISHED / transfer 问题发生在已建立连接的传输阶段
unsent_bytes 259072 B,约 253 KiB 已进入本地发送队列、尚未发送的数据
send_window_bytes 0 快照中的对端通告窗口为零
unacked_bytes 0 此次快照没有待确认序列空间
probe0_calls 4 观测到零窗口探测调用
观测时的快照年龄 约 0.52 秒 队列快照新鲜度可用于这次判断
srtt_snapshot_us 5995 μs,约 6 ms 最近内核平滑 RTT;不是应用请求耗时

组合证据把调查方向缩小到接收端窗口与消费能力,而不是仅凭“发送慢”推断线路丢包。TCP 零窗口探测属于正常流量控制机制,参见 RFC 9293 的窗口与探测规则。持续零窗口才需要结合业务受阻进一步排查;单次零窗口既不证明业务故障,也不证明整条路径健康。

建议在对端相同命名空间查看对应连接:

ss -tinmp 'sport = :40016'

结合接收队列、应用读取速度、事件循环、线程阻塞、CPU 节流及 GC 时间。如果对端也有采集器,查看它的 tcp_receive_backlog 样本。确认消费慢后修复阻塞或调整并发;业务需要持续高吞吐时,再评估缓冲区和背压设计。修复后观察窗口重新打开、未发送队列下降、业务请求恢复,不能只以探测次数减少作为验收。

场景二:窗口开放,但数据迟迟没有确认

以下是教学示例,不是客户事故或本次原生实测。假设同一已建立连接的新鲜快照给出:

字段或观测 教学值
queue_values_available true
快照距观测时间 2 秒
unacked_bytes / unsent_bytes 65536 / 0 B
send_window_bytes 262144 B
距最近一次观测到 ACK 推进 12 秒
srtt_snapshot_us 450000 μs,即 450 ms
同连接 RTO 观测增量 3

这组数据与零窗口不同:发送窗口还有空间,但已有数据长期未确认。采集器在有效且不超过 10 秒的快照中,对已建立或 CLOSE_WAIT、仍有未确认序列空间且至少 10 秒未观测到 ACK 推进的连接记录 tcp_ack_stall。这里“未观测到”是证据范围,未确认序列空间也可能包含控制序号,不能换算成同等业务载荷丢失。

优先比较两端证据:服务端是否接收到了数据、对端是否持续推进接收与 ACK、两端重传/RTO 是否增加、网卡或命名空间丢弃是否与故障同窗。接着检查路径拥塞、访问策略、连接跟踪和中间设备;如果必须判断具体哪段丢包,在少量目标连接上短时抓包并结合路径证据。

SRTT 450 ms 与 RTO 增量支持“TCP 响应异常”的方向,不能据此定位某一交换机,也不能代替接口请求 P99。重传和定时器调用次数都不是线路丢包数。修复验收需要业务延迟恢复,并且 ACK 停滞、RTO 和未确认队列回到原有基线。

场景三:本地应用没有及时读取

以下是教学示例:

字段或观测 教学值
连接状态 ESTABLISHED
queue_values_available true
快照年龄 2 秒
unread_bytes 131072 B,即 128 KiB
unsent_bytes / unacked_bytes 0 / 0 B
问题类型 tcp_receive_backlog

本地未读数据达到 64 KiB 时,采集器会保留接收积压线索。它提示先检查本地应用消费,而不是直接要求对端优化发送。一次大响应或应用合法批处理也会产生未读队列;要对齐持续时间、业务延迟和消费速率。

根据异常实体的进程和命名空间用 ss -tinmp 核对连接,关联应用线程、GC、限流和容器 CPU 节流。如果持续消费慢,优化读取和处理路径;缓冲区调整应服务于已确认的吞吐需求。修复后用相同负载确认队列不再持续积压,业务吞吐和延迟改善。

场景四:CLOSE_WAIT 持续较久或连接被 reset

netflow.tcp_close_wait 是聚合窗口内进入该状态的观测次数,不是当前存量。要排查清理慢,查看 tcp_close_wait 异常样本的 state/age_ms;采集器以至少 60 秒作为信息级取样阈值。

CLOSE_WAIT 表示收到对端 FIN,等待本地完成关闭;TCP 允许合法的半关闭,参见 RFC 9293 的连接状态与关闭。持续时间是否异常取决于协议和应用。只有业务不需要长时间半关闭、且样本和 ss 持续显示该状态时,才进一步检查遗漏的关闭路径、引用未释放或应用阻塞。避免把正常短连接的状态转换误当作泄漏。

reset 则要同时查看 problem_type、连接结果、stage/state 和 socket/I/O 错误:

组合证据 优先检查
connect_refused 加收到 reset,连接没有成功结果 建立阶段拒绝,见 连接失败实践
有成功建立事实,传输阶段收到 reset,并有 I/O 错误 对端进程退出、关闭方式、代理或中间设备连接清理
主动 reset 调用 本地关闭/错误处理路径;不能把调用次数当成业务失败次数
样本 evidence_partial=true 或生命周期不完整 先核对 采集可信度,保留阶段判断的不确定性

这类数据的价值是把“网络卡顿”细化成等待连接、等待发送窗口、等待 ACK、等待应用读取或等待关闭。最终根因仍应通过对应一侧的应用与网络证据验证。

文档评价

文档内容是否对您有帮助?