在aiohttp服务端调用aiohttp客户端方法触发RuntimeError求助
问题原因与解决方案
问题根源
你遇到的RuntimeError: Timeout context manager should be used inside a task异常,核心原因是**ClientSession在aiohttp事件循环启动前就被初始化了**。
你的路由模块在导入阶段就执行了mono_client = MonoAsyncClient(),此时服务端的事件循环尚未启动。ClientSession内部的超时管理依赖活跃的事件循环任务,提前初始化会导致后续请求时上下文绑定失败,触发异常。
修复方案
1. 重构异步客户端,延迟初始化Session
修改MonoAsyncClient,不在__init__中直接创建ClientSession,改为通过异步方法延迟初始化:
class MonoAsyncClient: """异步调用银行API的客户端""" ROOT_URL = bank_settings.bank_config.root_bank_url def __init__(self) -> None: self.headers = { "X-Token": bank_settings.bank_config.token } self._session = None # 先置空,延迟初始化 async def initialize(self) -> None: """必须在事件循环内调用,初始化ClientSession""" if self._session is None or self._session.closed: self._session = ClientSession(headers=self.headers) async def close(self) -> None: """安全关闭ClientSession""" if self._session and not self._session.closed: await self._session.close() async def create_payment( self, payment_data: NewPaymentSchema ) -> NewPaymentResponseSchema: """发送POST请求创建支付账单""" # 确保Session已初始化 if self._session is None or self._session.closed: await self.initialize() input_data = payment_data.model_dump_json(by_alias=True) async with self._session.post( url=f"{self.ROOT_URL}/invoice/create", data=input_data ) as response: response_data = await response.json() valid_data = NewPaymentResponseSchema.model_validate(response_data) return valid_data
2. 利用aiohttp应用生命周期管理客户端
通过服务端的on_startup和on_cleanup钩子,在事件循环启动后初始化客户端,关闭前清理资源:
# 子应用配置 v1_app = web.Application() async def on_startup(app): """应用启动时初始化客户端""" mono_client = MonoAsyncClient() await mono_client.initialize() app["mono_client"] = mono_client async def on_cleanup(app): """应用关闭时清理客户端""" mono_client = app["mono_client"] await mono_client.close() v1_app.on_startup.append(on_startup) v1_app.on_cleanup.append(on_cleanup)
3. 修改视图代码,从应用实例获取客户端
不再使用全局客户端实例,而是从request.app中获取已初始化的客户端:
routes = web.RouteTableDef() @routes.post("/invoices/create") async def create_payment(request: Request): """创建新支付账单的视图""" # 获取POST请求体数据 data = await request.post() try: validated_data = NewPaymentSchema.model_validate(data) except ValidationError as e: return web.json_response(e.errors()) else: # 从应用实例中获取客户端 mono_client = request.app["mono_client"] bank_api_response = await mono_client.create_payment(validated_data) return web.json_response(bank_api_response.model_dump(by_alias=True)) # 将路由添加到子应用 v1_app.add_routes(routes)
4. 主应用配置保持不变
主应用的挂载逻辑无需修改,确保子应用的生命周期钩子正常生效即可。
关键说明
- 所有异步资源(如
ClientSession)必须在事件循环启动后初始化,避免上下文绑定问题 - 用aiohttp应用对象存储客户端实例,保证全局唯一且生命周期与服务端一致
- 必须通过
on_cleanup钩子关闭ClientSession,防止资源泄漏
内容的提问来源于stack exchange,提问作者Antony_K
相关产品推荐
相关产品推荐

