为什么aiohttp弃用ClientSession的loop参数?如何正确使用?
刚好对aiohttp的这个变动比较熟悉,来给你拆解一下这两个问题:
为什么loop参数被弃用?
核心原因是asyncio自身的API演进以及aiohttp对最佳实践的跟进:
- 从Python 3.7开始,asyncio推出了
asyncio.run()这个高层API,它会自动完成事件循环的创建、运行和关闭,不再需要开发者手动获取或传递loop对象。aiohttp开发者为了对齐asyncio的设计方向,降低用户的使用门槛,决定弃用loop参数。 - 手动传递loop容易引发隐蔽问题:比如不小心在多个事件循环间共享ClientSession,或者循环关闭后session仍在使用,导致资源泄漏。去掉loop参数后,aiohttp会自动绑定当前协程上下文的事件循环,从根源上减少这类错误。
如何正确使用ClientSession?
关键原则是:必须在协程内部创建ClientSession,绝对不要在协程外部初始化,这就是你收到警告的直接原因。下面是几种常见的正确用法:
1. 基础场景:用asyncio.run()管理整个异步流程
这是Python 3.7+最推荐的方式,asyncio.run()会自动处理事件循环,你只需要在协程里用async with管理session生命周期:
import asyncio import aiohttp async def fetch_example_data(): # 在协程内部创建并使用session async with aiohttp.ClientSession() as session: async with session.get("https://example.com") as response: return await response.text() if __name__ == "__main__": result = asyncio.run(fetch_example_data()) print(result)
async with会自动帮你关闭session,无需手动调用close(),避免资源泄漏。
2. 兼容旧版本Python(3.6及以下)
如果还在使用Python 3.6,需要手动管理事件循环,但同样要在协程内部创建session:
import asyncio import aiohttp async def main(): async with aiohttp.ClientSession() as session: # 在这里执行所有需要session的请求 async with session.get("https://example.com") as resp: print(await resp.status) if __name__ == "__main__": loop = asyncio.get_event_loop() loop.run_until_complete(main()) loop.close()
3. 长时间运行的服务(如Web框架)
在FastAPI、aiohttp-web这类长时间运行的服务中,可以把session作为全局对象,但要在协程上下文的启动事件中初始化,确保绑定到正确的事件循环:
from fastapi import FastAPI import aiohttp app = FastAPI() # 全局session变量 session = None @app.on_event("startup") async def startup(): global session # 在启动协程中创建session session = aiohttp.ClientSession() @app.on_event("shutdown") async def shutdown(): # 在关闭协程中手动关闭session await session.close() @app.get("/fetch") async def fetch_data(): async with session.get("https://example.com") as resp: return await resp.json()
内容的提问来源于stack exchange,提问作者CSM
相关产品推荐
相关产品推荐

