为何5个API并行调用迭代耗时超预期?代码排查求助
问题排查与解决方案
核心原因分析
你的代码并发逻辑本身是正确的,但实际运行表现出串行耗时,大概率是以下原因之一:
- 目标API的并发限制:多数API服务器会对同一来源IP的并发请求数做限制,强制将请求串行处理,导致5个请求的耗时累加为单请求的5倍左右。
- requests未复用TCP连接:每个
requests.get都会新建TCP连接,握手+传输的时间被重复计算,而单测时的0.2秒包含了连接建立时间,多请求时连接开销叠加。 - 线程池执行的隐性等待:虽然
executor.map会启动并行任务,但如果API响应存在严重的时序依赖(比如服务器端对请求做排队处理),也会表现出串行耗时。
验证方法
在call_api函数中添加时间戳打印,确认请求的执行时序:
def call_api(url): start = time.time() print(f"Start {url} at {start:.4f}") try: response = requests.get(url) end = time.time() print(f"End {url} at {end:.4f}, took {end-start:.4f}s") return response.status_code == 200 except requests.exceptions.RequestException as e: end = time.time() print(f"Fail {url} at {end:.4f}, took {end-start:.4f}s") return False
如果输出的5个请求Start时间几乎一致,说明线程池确实在并行执行,问题出在API服务器端;如果Start时间依次延迟约0.2秒,说明请求被强制串行处理。
解决方案
1. 复用TCP连接(减少连接开销)
使用requests.Session复用连接,避免每次请求都新建TCP连接,大幅降低握手时间开销:
# 在全局创建Session对象 session = requests.Session() def call_api(url): try: response = session.get(url) # 使用session发起请求 return response.status_code == 200 except requests.exceptions.RequestException: return False
2. 调整线程池配置(备选方案)
如果API服务器对线程识别有特殊限制,可以尝试:
- 适度增大
max_workers到10(但不要过大,避免触发更严格的限流) - 改用
ProcessPoolExecutor(适合CPU密集场景,IO密集场景下线程池更高效,此为备选):
with concurrent.futures.ProcessPoolExecutor(max_workers=5) as executor: results = executor.map(call_api, random_urls)
3. 排查API限流规则
查看目标API的官方文档,确认是否有并发请求数、QPS限制:
- 如果有限流,需调整请求策略(比如增加请求间隔、使用合规代理IP分散请求来源)
- 联系API服务商调整权限(若有需求)
4. 代码细节优化(提升效率)
避免使用global变量跟踪统计信息,改用列表批量收集结果后生成DataFrame,提升运行效率:
# 示例:用列表收集结果,替代全局DataFrame的逐行修改 results_list = [] min_time = float('inf') max_time = float('-inf') total_time = 0 def process_iteration(iteration): global min_time, max_time, total_time random_urls = df['url_column'].sample(n=5).tolist() start_time = time.time() with concurrent.futures.ThreadPoolExecutor(max_workers=5) as executor: results = list(executor.map(call_api, random_urls)) iteration_time = time.time() - start_time min_time = min(min_time, iteration_time) max_time = max(max_time, iteration_time) total_time += iteration_time passed = 'Y' if all(results) else 'N' return [iteration, passed, iteration_time] # 批量收集结果 for i in range(1, num_iterations + 1): results_list.append(process_iteration(i)) # 生成DataFrame results_df = pd.DataFrame(results_list, columns=['Iteration', 'Pass', 'Time Taken'])
验证效果
修改后重新运行测试:
- 若为连接复用问题,迭代耗时会明显接近单请求耗时
- 若为API限流问题,耗时可能仍接近串行总和,此时需针对性调整请求策略
内容的提问来源于stack exchange,提问作者Jose Moquiambo
相关产品推荐
相关产品推荐

