因为ClientSession内部维护连接池和事件循环引用,不显式关闭会导致TCP连接、DNS缓存、SSL上下文等资源未释放,事件循环关闭时触发ResourceWarning: unclosed client session。

为什么 aiohttp.ClientSession() 不关会报 ResourceWarning?
因为 aiohttp.ClientSession 内部维护了连接池和事件循环引用,不显式关闭时,Python 垃圾回收可能在事件循环已关闭或即将关闭时才清理它,触发 ResourceWarning: unclosed client session。这不是“没影响”,而是连接池里的 TCP 连接、DNS 缓存、SSL 上下文等都没被释放,尤其在短生命周期脚本(如 CLI 工具、测试用例)里特别明显。
- 常见于忘记加
await session.close(),或用了async with但提前return/raise跳出了上下文 - 在 Jupyter 或 pytest 中反复运行异步代码时,警告容易累积,干扰调试
-
aiohttp3.8+ 默认启用warn=True,所以即使你没设loop.set_debug(True)也会看到
必须用 async with,但要注意提前退出的陷阱
async with aiohttp.ClientSession() 是最推荐的方式,但它只在进入和退出 async with 块时自动调用 __aenter__ 和 __aexit__。一旦在块内 return、break 或未捕获异常,__aexit__ 仍会被调用——这点没问题;但如果你在 async with 外层又套了个 try/finally 并手动调用了 session.close(),反而可能重复关闭引发 RuntimeError: Session is closed。
- ✅ 正确:直接在
async with块里写所有请求逻辑,不手动调close() - ❌ 错误:在
async with块外再写await session.close() - ⚠️ 隐患:在
async with块里await asyncio.sleep(10)后再发请求——连接空闲超时可能被服务端断开,但 session 本身仍是 open 状态,不影响资源释放逻辑
全局 session 复用时怎么安全关闭?
有些场景(如 FastAPI 的 lifespan、长期运行的后台任务)需要复用一个 ClientSession 实例。这时不能依赖 async with,必须自己管理生命周期,并确保它只被关闭一次。
- 用模块级变量 +
asyncio.Lock防止并发 close - 在应用 shutdown 钩子(如 FastAPI 的
@app.on_event("shutdown"))中调用await session.close() - 避免在每次请求都新建 session:频繁创建销毁 session 会抵消连接复用优势,还可能触发
Too many open files - 可选:设置
connector=aiohttp.TCPConnector(limit=100, limit_per_host=30, keepalive_timeout=30)控制连接行为,让空闲连接更早释放
测试环境里 ResourceWarning 总是冒出来怎么办?
pytest + aiohttp 测试常因 fixture 生命周期和事件循环复用问题导致 session 残留。不要 suppress 所有 ResourceWarning,而应定位具体 session 实例。
立即学习“Python免费学习笔记(深入)”;
- 给每个测试用的 session 加唯一标识,比如
ClientSession(trace_configs=[MyTraceConfig()]),在 trace 中打日志确认关闭时机 - 用
pytest-asyncio的event_loopfixture 时,确保 session 在 loop 关闭前已关闭(即 fixture scope 设为"function",并在teardown里 await close) - 如果只是想临时屏蔽(仅限 CI 或调试),用
pytest -W ignore::ResourceWarning,但别提交到pyproject.toml里当成解决方案
anyio.TaskGroup 或 asyncio.create_task + asyncio.shield)来保证 cleanup 一定执行,而不是靠“应该没人会提前退出”这种假设。


















