Files
zhuangang/topics/cs/network-os/scenarios/connection-troubleshooting.md
T

4.0 KiB
Raw Blame History

id, title, type, domain, stack, tags, level, prerequisites, est_hours, related, week
id title type domain stack tags level prerequisites est_hours related week
cs.network-os.scenario.connection-troubleshooting 线上连接问题排查:大量 TIME_WAIT、连接池耗尽、fd 用尽和事件循环阻塞 scenario cs
network-os
linux
fastapi
TIME_WAIT
连接池
文件描述符
事件循环阻塞
超时
排查
intermediate
cs.network-os.http-tcp
cs.network-os.os-basics
2
cs.network-os.interview
devops.linux.scenario.incident-troubleshooting
14
16

线上连接问题排查:大量 TIME_WAIT、连接池耗尽、fd 用尽和事件循环阻塞

业务背景与问题

AI Agent 服务(FastAPI)高峰期偶发大量超时,日志中出现:

  • 调用模型网关报 “Cannot assign requested address”;
  • 偶尔出现 “Too many open files”;
  • 所有请求的延迟同时变高,包括健康检查。

约束

  • 不能停机排查。
  • 运维只给了容器内的只读排查权限。

方案设计

  1. 看连接状态:ss -s、ss -tan state time-wait | wc -l 发现几万个 TIME_WAIT,目标都是模型网关 → 代码中每次请求都新建 httpx.AsyncClient,用完就关闭,没有复用连接。修复:应用级共享一个客户端(连接池),设置池大小和 keep-alive。
  2. 看 fd:ls /proc/<pid>/fd | wc -l 与 ulimit -n 对比 → 文件上传后临时文件没有关闭。修复:使用 with 管理资源;在监控中加入 fd 数量。
  3. 看事件循环:所有请求同时变慢,说明事件循环被阻塞 → 用 asyncio 调试模式 / 采样分析发现同步的 PDF 解析。修复:移到进程池(run_in_executor)或任务队列。
  4. 超时与隔离:所有外部调用设置连接超时和读超时;对模型网关设置并发上限(信号量),防止一个慢依赖耗尽所有资源。
  5. 验证:压测对比修复前后的 TIME_WAIT 数量、fd 数量、P99 延迟。

关键代码

from contextlib import asynccontextmanager

import httpx
from fastapi import FastAPI

@asynccontextmanager
async def lifespan(app: FastAPI):
    # 整个应用共享一个客户端:复用 TCP / TLS 连接
    app.state.llm = httpx.AsyncClient(
        base_url=LLM_GATEWAY,
        timeout=httpx.Timeout(connect=3, read=60, write=10, pool=5),
        limits=httpx.Limits(max_connections=100, max_keepalive_connections=20),
    )
    yield
    await app.state.llm.aclose()

app = FastAPI(lifespan=lifespan)

@app.post("/parse")
async def parse(file: UploadFile):
    data = await file.read()
    loop = asyncio.get_running_loop()
    return await loop.run_in_executor(process_pool, parse_pdf, data)   # CPU 密集任务不阻塞事件循环

相关原理:网络基础、操作系统基础;Linux 排查命令见 Linux 线上排查。

取舍

选择 优点 缺点
复用连接(本方案) 根治 TIME_WAIT,同时降低延迟(省去握手) 要管理客户端生命周期
调整内核参数(端口范围、TIME_WAIT 复用) 立刻见效 治标不治本,有副作用,容器内往往无权限
增加实例 简单 掩盖问题,成本上升

易错点

  • 看到 TIME_WAIT 就认为是服务器的问题——它出现在主动关闭的一方。
  • 共享的客户端没有设置池上限,慢依赖拖垮整个服务。
  • 只设置了总超时,没有区分连接超时和读超时(流式响应的读超时要考虑 token 间隔)。

面试怎么讲

“Agent 服务高峰期出现‘Cannot assign requested address’,用 ss 看到几万个 TIME_WAIT 指向模型网关,原因是每次请求新建 HTTP 客户端。改成应用级共享客户端并配置连接池和分阶段超时后,TIME_WAIT 基本消失,延迟也下降了。另外发现同步 PDF 解析阻塞事件循环,导致所有请求同时变慢,移到进程池解决。修复后用压测对比了连接数、fd 数和 P99。”