Python asyncio 并发请求实践小结:从 gather 到信号量限流
在日常开发中,我们经常需要同时调用多个 HTTP 接口,比如批量拉取数据、并行检查服务状态等。如果使用同步的 requests 逐个请求,总耗时等于所有请求耗时之和。借助 Python 标准库 asyncio 配合 aiohttp,可以把这些 I/O 等待时间重叠起来,大幅缩短整体耗时。
一、基本用法:asyncio.gather
最简单的方式是为每个请求创建一个协程,再用 asyncio.gather 一次性并发执行。gather 会按传入顺序返回结果,写法直观。需要注意的是,默认情况下只要有一个协程抛出异常,gather 就会把异常向上抛出;如果希望单个失败不影响整体,可以传入 return_exceptions=True,再逐个判断结果是否为异常对象。
二、控制并发数:asyncio.Semaphore
当请求数量达到成百上千时,一次性全部发出容易触发对端限流,甚至耗尽本地连接资源。此时可以创建一个 asyncio.Semaphore(10) 之类的信号量,在每个请求协程内部用 async with sem 包裹真正的请求逻辑,这样同一时刻最多只有 10 个请求在进行,其余请求会排队等待。
三、超时与重试
网络请求一定要设置超时。aiohttp 可以通过 ClientTimeout 统一设置总超时时间,也可以用 asyncio.wait_for 为单个协程设置超时。对于偶发失败,可以写一个简单的重试循环,每次失败后用 asyncio.sleep 做指数退避,避免在对端故障时持续加压。
四、复用会话
不要为每个请求单独创建 ClientSession。会话内部维护了连接池,整个批量任务共用一个 session,可以复用 TCP 连接,减少握手开销。通常的做法是在主协程中 async with aiohttp.ClientSession() as session,然后把 session 作为参数传给各个请求协程。
五、小结
asyncio 并不会让单个请求变快,它的价值在于让多个 I/O 等待并行发生。gather 负责并发调度,Semaphore 负责限流,超时与重试保证健壮性,共享会话降低开销。把这几点组合起来,就能写出既高效又稳定的批量请求代码。
- 点赞
- 收藏
- 关注作者
评论(0)