You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用aiohttp 3.10.10+Python3.8+FastAPI时出现h11 LocalProtocolError连接异常的求助

使用aiohttp 3.10.10+Python3.8+FastAPI时出现h11 LocalProtocolError连接异常的求助

我的问题场景

我目前碰到了一个连接异常的麻烦,先跟大家说下我的技术栈和使用情况:

  • 用的是 aiohttp==3.10.10 + Python 3.8
  • 运行着一个FastAPI应用,每收到一个请求,就会创建一个异步后台任务,给外部API发送POST请求
  • 为了对外部API做连接池复用,我给这些后台任务创建了一个共享的 aiohttp.ClientSession

大部分情况都正常,但偶尔会碰到连接在消息传输过程中关闭的错误,错误日志如下:

"ERROR", "name": "asyncio", "lineno": 1707, "message": "Exception in callback H11Protocol.timeout_keep_alive_handler()
handle: <TimerHandle when=423.140775385 H11Protocol.timeout_keep_alive_handler()>", "exc_info": "Traceback (most recent call last):
  File "/usr/local/lib/python3.8/asyncio/events.py", line 81, in _run
    self._context.run(self._callback, *self._args)
  File "/usr/local/lib/python3.8/site-packages/uvicorn/protocols/http/h11_impl.py", line 383, in timeout_keep_alive_handler
    self.conn.send(event)
  File "/usr/local/lib/python3.8/site-packages/h11/_connection.py", line 512, in send
    data_list = self.send_with_data_passthrough(event)
  File "/usr/local/lib/python3.8/site-packages/h11/_connection.py", line 537, in send_with_data_passthrough
    self._process_event(self.our_role, event)
  File "/usr/local/lib/python3.8/site-packages/h11/_connection.py", line 272, in _process_event
    self._cstate.process_event(role, type(event), server_switch_event)
  File "/usr/local/lib/python3.8/site-packages/h11/_state.py", line 293, in process_event
    self._fire_event_triggered_transitions(role, _event_type)
  File "/usr/local/lib/python3.8/site-packages/h11/_state.py", line 311, in _fire_event_triggered_transitions
    raise LocalProtocolError(
h11._util.LocalProtocolError: can't handle event type ConnectionClosed when role=SERVER and state=SEND_BODY"}

有没有人遇到过类似的问题?或者知道怎么解决吗?


可能的解决思路(社区经验分享)

我之前在类似场景下碰到过这类问题,分享几个亲测有效的解决方向:

  1. 调整ClientSession的超时配置
    默认的keep-alive超时可能和外部API或Uvicorn的设置不兼容,导致连接被外部主动关闭后还被复用。你可以显式设置更合理的超时参数:
from aiohttp import ClientTimeout, ClientSession

# 自定义超时,根据外部API的实际情况调整
timeout = ClientTimeout(
    total=10,        # 整个请求的总超时时间
    connect=5,       # 建立连接的超时
    sock_read=5,     # 读取数据的超时
    sock_connect=5,  # Socket连接建立超时
    keepalive_timeout=5  # Keep-alive连接的空闲超时
)
# 用自定义超时创建共享Session
session = ClientSession(timeout=timeout)
  1. 添加异常捕获与重试机制
    网络偶发异常很难避免,可以在发送请求的逻辑里捕获连接相关异常,实现重试:
from tenacity import retry, stop_after_attempt, wait_exponential
import aiohttp
import h11

@retry(stop=stop_after_attempt(3), wait=wait_exponential(multiplier=1, min=2, max=10))
async def send_external_request(session: ClientSession, url: str, data: dict):
    try:
        async with session.post(url, json=data) as resp:
            return await resp.json()
    except (h11.LocalProtocolError, aiohttp.ClientConnectionError) as e:
        print(f"连接异常,正在重试: {str(e)}")
        raise  # 抛出异常让重试机制触发
  1. 调整Uvicorn的Keep-alive设置
    错误日志里提到了Uvicorn的timeout_keep_alive_handler,可能Uvicorn的keep-alive超时和aiohttp的设置冲突了。启动Uvicorn时可以调整这个参数:
uvicorn main:app --keep-alive 10
  1. 确保ClientSession在应用 shutdown时正确关闭
    共享Session要在应用停止时妥善关闭,避免残留连接引发异常,用FastAPI的事件钩子实现:
from fastapi import FastAPI
import aiohttp

app = FastAPI()
session: aiohttp.ClientSession = None

@app.on_event("startup")
async def startup():
    global session
    session = aiohttp.ClientSession()

@app.on_event("shutdown")
async def shutdown():
    await session.close()
  1. 升级相关依赖版本
    你用的aiohttp 3.10.10是比较旧的版本了,后续小版本可能修复了连接池相关的bug,可以尝试升级到同大版本的最新稳定版(比如3.10.x的后续版本),或者兼容Python3.8的更高版本(比如3.11.x)。

备注:内容来源于stack exchange,提问作者Arran Duff

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.14 13:54:50