asyncio.gather() 是最直接的并发请求方式,需配合异步HTTP客户端(如aiohttp)、信号量限流、return_exceptions=True容错、显式分层超时及针对性异常处理。

asyncio.gather() 是最直接的并发请求方式
用 asyncio.gather() 同时发起多个协程,它会等待全部完成并按顺序返回结果。适合你已有一组独立、无依赖的 API 请求任务。
常见错误是把同步 HTTP 库(比如 requests)直接丢进协程里——这会导致整个事件循环被阻塞。必须换用异步客户端,如 aiohttp 或 httpx.AsyncClient。
示例要点:
- 每个请求封装成一个
async def fetch(url)协程 - 用
await asyncio.gather(*[fetch(u) for u in urls])并发执行 - 别忘了在
aiohttp.ClientSession()上下文内做请求,否则连接复用失效、可能触发RuntimeError: Session is closed
控制并发数避免被限流或超时
asyncio.gather() 默认全量并发,但多数第三方 API 有 QPS 限制或连接池上限。一次性发 100 个请求,大概率触发 429 或 TCP 连接拒绝。
立即学习“Python免费学习笔记(深入)”;
推荐用 asyncio.Semaphore 限流:
- 初始化一个信号量:
sem = asyncio.Semaphore(5)表示最多 5 个并发 - 每个
fetch()内部用async with sem:包裹实际请求逻辑 - 注意:信号量要定义在协程外(如全局或函数内
await前),否则每次新建就失去限流意义 - 某些 API 对 header(如
User-Agent、Authorization)敏感,漏传会直接返回 401/403
异常处理不能只靠 try/except 外层包裹
asyncio.gather() 遇到任一协程抛异常,默认会立刻中断其余请求,并把第一个异常往上抛——这不符合“尽量拿到尽可能多响应”的实际需求。
正确做法是启用 return_exceptions=True 参数:
results = await asyncio.gather(*tasks, return_exceptions=True)- 此时失败任务返回的是
Exception实例(如aiohttp.ClientConnectorError),而非抛出 - 后续遍历
results时需手动判断:if isinstance(r, Exception): ... - 典型错误响应包括:
aiohttp.ServerDisconnectedError(服务端主动断连)、asyncio.TimeoutError(未设 timeout)、json.decoder.JSONDecodeError(空响应或 HTML 错误页)
超时设置必须显式声明,不能依赖默认
aiohttp 默认无超时,网络卡住时协程会永远挂起;httpx 虽有默认 5 秒,但通常不够用且不区分连接/读取阶段。
建议分层设超时:
- 用
aiohttp.ClientTimeout(total=10, connect=3, sock_read=7)明确各阶段上限 - 或用
httpx.AsyncClient(timeout=httpx.Timeout(10.0, connect=3.0)) - 避免在
fetch()内用asyncio.wait_for(..., timeout=...)套一层——它和客户端自身的 timeout 机制叠加,行为难预测 - 特别注意 DNS 解析超时:某些内网环境解析慢,
connect超时需略放宽
真正麻烦的不是并发本身,而是每个 API 的重试策略、鉴权刷新逻辑、响应结构差异——这些没法靠 gather 一键解决,得逐个适配。


















