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

显式调用close()仍触发aiohttp未关闭会话警告,上下文管理器则正常

异步API封装显式调用close()出现未关闭会话警告的问题及解决

我参考异步API封装的示例,构建了一个基于aiohttp的异步API封装类AsyncWrapper,代码如下:

import asyncio
import aiohttp

class AsyncWrapper:
    def __init__(self, api_key, useragent="API-Wrapper/0.2"):
        self.url = f"https://example.api/v1"
        self._api_key = api_key
        self._useragent = useragent
        self._headers = {"X-API-Key": self._api_key, "accept": "application/json", "User-Agent": self._useragent}
        self._session = aiohttp.ClientSession()

    async def __aenter__(self):
        return self

    async def __aexit__(self, exc_type, exc_val, exc_tb):
        await self.close()

    async def close(self):
        await self._session.close()

    async def get_endpoint1(self, arg1, arg2):
        endpoint = "/pair"

        async with self._session.get(self.url + endpoint, params={"arg1": arg1, "arg2": arg2}, headers=self._headers) as response:
            return await response.json()
    
    async def get_endpoint2(self, arg1, arg2, optionalarg1=None, optionalarg2=None):
        endpoint = "/token"

        async with self._session.get(self.url + endpoint, params={"arg1": arg1, "arg2": arg2, "optionalarg1": optionalarg1, "optionalarg2": optionalarg2}, headers=self._headers) as response:
            return await response.json()

这个类支持两种使用方式:

  • 显式实例化后手动调用close()关闭会话:
import asyncio
import AsyncWrapper

api_key = "my_key"

async def main():
    client = AsyncWrapper(api_key)

    response1 = await client.get_endpoint1("argument1", "argument2")
    print(response1)

    await client.close()

asyncio.run(main())

运行后控制台会输出警告:

Unclosed client session
client_session: <aiohttp.client.ClientSession object at 0x0000028D1F0E0390>
  • 使用异步上下文管理器(async with):
import asyncio
import AsyncWrapper

api_key = "my_key"

async def main():
    async with AsyncWrapper(api_key) as client:
        response1 = await client.get_endpoint1("argument1", "argument2")
        print(response1)

asyncio.run(main())

这种方式不会出现任何警告。

差异原因

出现警告的核心原因是:

  • 手动调用close()后,self._session仍然持有对aiohttp.ClientSession实例的引用,直到AsyncWrapper对象被Python垃圾回收器回收。
  • aiohttp的ClientSession在其__del__方法中会检查会话状态,即使你已经调用了close(),只要对象还存在,就可能触发误判的“未关闭会话”警告。
  • 而使用async with时,上下文管理器结束后,AsyncWrapper对象会被立即销毁,self._session的引用也随之被清除,不会等到垃圾回收阶段再触发检查。

解决方法

修改close()方法,在关闭会话后主动解除对ClientSession实例的引用:

async def close(self):
    await self._session.close()
    self._session = None  # 解除引用,避免垃圾回收时触发警告

这样,当你手动调用close()后,self._session被置为None,ClientSession实例会被及时回收,不会触发aiohttp的未关闭会话警告。


内容的提问来源于stack exchange,提问作者alb2001

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.29 06:05:02