使用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"}
有没有人遇到过类似的问题?或者知道怎么解决吗?
可能的解决思路(社区经验分享)
我之前在类似场景下碰到过这类问题,分享几个亲测有效的解决方向:
- 调整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)
- 添加异常捕获与重试机制
网络偶发异常很难避免,可以在发送请求的逻辑里捕获连接相关异常,实现重试:
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 # 抛出异常让重试机制触发
- 调整Uvicorn的Keep-alive设置
错误日志里提到了Uvicorn的timeout_keep_alive_handler,可能Uvicorn的keep-alive超时和aiohttp的设置冲突了。启动Uvicorn时可以调整这个参数:
uvicorn main:app --keep-alive 10
- 确保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()
- 升级相关依赖版本
你用的aiohttp 3.10.10是比较旧的版本了,后续小版本可能修复了连接池相关的bug,可以尝试升级到同大版本的最新稳定版(比如3.10.x的后续版本),或者兼容Python3.8的更高版本(比如3.11.x)。
备注:内容来源于stack exchange,提问作者Arran Duff
相关产品推荐
相关产品推荐

