使用aiohttp+ProxyConnector遇Session关闭与未关闭连接器异常
解决aiohttp复用ProxyConnector时的会话关闭与未关闭连接器问题
问题根源
- 初始报错
RuntimeError: Session is closed:默认情况下,ClientSession会成为传入的connector的所有者,当async with块结束关闭会话时,会同时关闭连接器。第二次请求复用已关闭的连接器,就会触发该错误。 - 添加
connector_owner=False后出现的Unclosed connector警告:该参数让会话不再管理连接器的生命周期,但程序结束前没有手动关闭连接器,导致aiohttp抛出资源未释放的警告。
最优解决方案:复用ClientSession
aiohttp的ClientSession设计初衷就是复用,复用会话不仅能避免连接器的生命周期问题,还能利用连接池提升请求效率。修改代码如下:
import asyncio from typing import Optional import aiohttp from aiohttp_socks import ProxyConnector async def async_get(session: aiohttp.ClientSession, url: str, **kwargs) -> str: async with session.get(url=url, **kwargs) as response: status_code = response.status if status_code <= 201: return await response.text() raise Exception(f'Unsuccessful request: {status_code}') async def main(): proxy = 'socks5://username:password@0.0.0.0:1111' connector = ProxyConnector.from_url(url=proxy, rdns=True) # 只创建一次会话,循环内复用 async with aiohttp.ClientSession(connector=connector) as session: print(session) tasks = [async_get(session, 'https://www.google.com/') for _ in range(3)] # 也可以顺序执行:for _ in range(3): await async_get(session, ...) await asyncio.gather(*tasks) if __name__ == '__main__': loop = asyncio.get_event_loop() loop.run_until_complete(main())
备选方案:手动管理连接器生命周期
如果必须每次创建新的ClientSession,则需要在所有请求完成后手动关闭连接器:
import asyncio from typing import Optional import aiohttp from aiohttp_socks import ProxyConnector async def async_get(url: str, connector: Optional[ProxyConnector] = None, **kwargs) -> str: # 指定connector_owner=False,让会话不关闭连接器 async with aiohttp.ClientSession(connector=connector, connector_owner=False) as session: print(session) async with session.get(url=url, **kwargs) as response: status_code = response.status if status_code <= 201: return await response.text() raise Exception(f'Unsuccessful request: {status_code}') async def main(): proxy = 'socks5://username:password@0.0.0.0:1111' connector = ProxyConnector.from_url(url=proxy, rdns=True) try: for _ in range(3): await async_get(url='https://www.google.com/', connector=connector) finally: # 所有请求完成后手动关闭连接器 await connector.close() if __name__ == '__main__': loop = asyncio.get_event_loop() loop.run_until_complete(main())
说明
- 优先选择复用
ClientSession的方案,这是aiohttp官方推荐的最佳实践,能有效提升性能并避免资源管理问题。 - 手动关闭连接器时,务必确保在所有请求完成后执行,否则可能导致正在进行的请求失败。
内容的提问来源于stack exchange,提问作者SecorD
相关产品推荐
相关产品推荐

