asyncio.gather不能与requests混用,因requests是同步阻塞库,会阻塞事件循环;必须使用aiohttp或httpx等异步客户端,并正确管理会话、超时、重试和错误聚合。

asyncio.gather 能并发发起多个 HTTP 请求,但必须配合支持异步的 HTTP 客户端(如 aiohttp 或 httpx),不能直接用 requests —— 否则会阻塞整个事件循环。
为什么不能用 requests + asyncio.gather?
requests 是同步阻塞库,调用时会卡住事件循环,即使包在 async def 里也无效。你看到的“并发”只是假象,实际是串行执行,还可能引发 RuntimeWarning: coroutine 'XXX' was never awaited。
- 错误写法:
await asyncio.gather(requests.get(url1), requests.get(url2))——requests.get返回的是Response对象,不是协程 - 正确前提:所有任务必须是真正的协程对象(即返回
awaitable) - 常见症状:CPU 占用低、响应时间接近串行总和、日志顺序混乱但耗时不减
用 aiohttp 实现真正并发请求
aiohttp.ClientSession 是标准选择,它复用连接、自动处理 cookie 和 headers,比手动创建每个 aiohttp.get() 更高效。
- 必须用
async with管理ClientSession,否则连接不释放,后续请求可能卡住或报Unclosed client session - 每个请求应包装成独立协程函数,方便传参和错误隔离
-
asyncio.gather默认不取消失败任务;加参数return_exceptions=True可避免一个失败导致全部中断
import asyncio import aiohttp <p>async def fetch(session, url): async with session.get(url) as resp: return await resp.text()</p><p>async def main(): urls = ["<a href="https://www.php.cn/link/5f69e19efaba426d62faeab93c308f5c">https://www.php.cn/link/5f69e19efaba426d62faeab93c308f5c</a>", "<a href="https://www.php.cn/link/ef246753a70fce661e16668898810624">https://www.php.cn/link/ef246753a70fce661e16668898810624</a>"] async with aiohttp.ClientSession() as session: results = await asyncio.gather( *[fetch(session, url) for url in urls], return_exceptions=True ) print([type(r).<strong>name</strong> for r in results]) # ['str', 'str'] 或 ['str', 'ClientConnectorError']</p><p>asyncio.run(main())</p><div class="aritcle_card flexRow artxards"> <div class="artcardd flexRow"> <a class="aritcle_card_img" rel="nofollow" href="/xiazai/skill11317" title="Python 编程规范"><img src="https://img.php.cn/upload/skill/001/246/273/6adaf20e695c2374.jpg" alt="Python 编程规范" onerror="this.onerror='';this.src='/static/lhimages/moren/morentu.png'" ></a> <div class="aritcle_card_info flexColumn"> <a rel="nofollow" href="/xiazai/skill11317" title="Python 编程规范" class="overflowclass">Python 编程规范</a> <p class="overflowclass">Python 编程规范与最佳实践。强制 PEP 8 代码风格、语法校验、单元测试执行,适用于代码编写、审查和重构。触发词:Python、代码规范、py_compile、单元测试。</p> </div> <a rel="nofollow" href="/xiazai/skill11317" title="Python 编程规范" class="aritcle_card_btn flexRow flexcenter"><b></b><span>下载</span> </a> </div> </div>
用 httpx 替代 aiohttp 的注意事项
httpx.AsyncClient 接口更接近 requests,适合快速迁移,但它默认启用 HTTP/2,某些老旧服务可能不兼容,返回 RemoteProtocolError。
- 若遇到连接问题,显式禁用 HTTP/2:
httpx.AsyncClient(http2=False) -
httpx的timeout参数是对象(如httpx.Timeout(10.0)),不是数字,填错会抛TypeError - 与
aiohttp不同,httpx允许在gather外单独await某个请求,但共用AsyncClient实例时仍需注意连接池竞争
超时、重试和错误聚合怎么加?
别在 gather 外套 asyncio.wait_for——它只控制总耗时,不解决单个请求超时。应该把超时逻辑下沉到每个请求协程内部。
- 对每个请求设独立超时:
session.get(url, timeout=5)(aiohttp)或client.get(url, timeout=5.0)(httpx) - 重试建议用
tenacity库的@retry装饰异步函数,不要在gather内手动写while循环,否则容易阻塞 - 错误聚合推荐用字典记录:
{url: result or error},而不是靠下标对应,因为return_exceptions=True后结果顺序虽保持,但类型混杂难调试
真正并发的关键不在 gather,而在底层客户端是否异步、连接是否复用、错误是否被吞掉。很多人调通了就以为完成了,但线上跑半小时后出现大量 ConnectionResetError 或内存缓慢增长,往往是因为忘了关 ClientSession 或没设连接池上限。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!










