You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Python中结合asyncio.as_completed使用Semaphore限制API并发?

解决思路

1. 修正Semaphore的作用范围

你当前的代码把Semaphore放在asyncio.as_completed的循环里,只限制了任务结果的处理步骤,而非实际的API请求并发。100+个任务已经提前启动,连接池早被占满,导致部分请求因连接耗尽失败。

正确的做法是把Semaphore绑定到每个API请求的执行阶段,包括初始请求和后续分页请求:

async def fetch_data(sem, client, initial_url):
    async with sem:
        res = await client.get_json_async(initial_url)
        all_data = res["data"].copy()
        progress.update(len(all_data))
        
        # 分页请求也受Semaphore控制
        while res["links"].get("next"):
            async with sem:
                res = await client.get_json_async(res["links"]["next"])
                all_data.extend(res["data"])
                progress.update(len(res["data"]))
        return all_data

# 初始化Semaphore,设置合理并发数(比如10-20,根据服务器承受能力调整)
sem = asyncio.Semaphore(10)
# 重新创建受Semaphore控制的任务列表
tasks = [fetch_data(sem, client, url) for url in your_initial_urls]

q = Queue(-1)
progress = tqdm(total=total_hits)

for task in asyncio.as_completed(tasks):
    try:
        data = await task
        q.put_nowait(data)
    except Exception as e:
        print(f"请求失败: {str(e)}")
        # 可选:添加重试逻辑

2. 检查并调整客户端连接池配置

客户端连接池(比如aiohttp的TCPConnector)默认有最大连接数限制,若超过服务器允许的TCP连接数,会导致请求被拒绝或超时:

  • 查看客户端文档,找到连接池的最大连接数配置项(比如aiohttp的limit参数)
  • 让连接池的最大连接数与Semaphore的并发数匹配,避免连接池耗尽

3. 添加错误处理与重试机制

未获取全量数据大概率是因为部分请求失败(超时、限流返回429、服务器错误),但你的代码没有异常捕获,失败的任务直接跳过导致数据丢失:

  • 给所有await client.get_json_async添加try-except块,捕获网络异常、HTTP错误码
  • 对429(限流)这类可重试的错误,添加指数退避重试逻辑

4. 验证服务器限流规则

很多API服务器会限制并发请求数或请求速率(比如每分钟最多100次请求),超过限制会返回错误或丢弃请求:

  • 查看API文档的限流说明
  • 检查响应头中的Retry-After、X-RateLimit-Limit等字段,确认限流规则
  • 调整Semaphore的并发数,使其低于服务器的限流阈值

5. 调整Semaphore的并发数值

当前设置Semaphore(1)会让所有请求串行执行,效率极低且没必要。建议从10开始测试,逐步调整到能稳定获取全量数据的最大值。

内容的提问来源于stack exchange,提问作者j7skov

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.05 10:40:44