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

在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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.21 17:53:17