为什么aiohttp异步请求代码运行时表现为同步状态?
问题原因及修复方案
1. 替换阻塞同步调用
你用time.sleep()测试时表现为同步是核心的常识性错误:time.sleep()是阻塞同步函数,调用时会直接卡住整个asyncio事件循环,不会释放执行权给其他协程,自然所有任务都是串行执行的。要模拟异步等待场景必须使用异步等待函数asyncio.sleep()。
同理,所有async函数内都不能直接调用同步IO逻辑(比如普通文件读写、同步HTTP请求、CPU密集计算等),要么替换为对应异步库实现,要么将同步逻辑扔到线程/进程池中执行。
2. 高并发场景的限制问题
你原代码一次性将数十万行对应的协程任务全部提交给asyncio.gather()执行,会触发两层瓶颈,导致看起来接近同步执行:
- aiohttp的
ClientSession默认有连接池限制,默认最大并发连接数为100,超出的请求会排队等待空闲连接,不会真的并发发送 - 你的本地API服务端本身也有并发承载上限,同一时间只能处理有限个请求,多余请求会在服务端队列等待,客户端视角看就是串行返回
3. 修复后的代码示例
import asyncio import aiohttp import sys # 调整并发数为你本地API可承载的最大值,建议从100~500逐步测试调整 MAX_CONCURRENT = 200 semaphore = asyncio.Semaphore(MAX_CONCURRENT) async def process_line_async(session, line): async with semaphore: # 用信号量限制并发数 async with session.put('http://localhost:8887/load', data=line) as response: r = await response.json() sys.stdout.write(f"some info about the response\n") # 测试时用下面的异步sleep替换请求逻辑 # await asyncio.sleep(1) # print(line) async def looper(lines): async with aiohttp.ClientSession( # 手动调大连接池上限,和MAX_CONCURRENT对齐即可 connector=aiohttp.TCPConnector(limit=MAX_CONCURRENT) ) as session: tasks = [] for line in lines: task = asyncio.ensure_future(process_line_async(session, line)) tasks.append(task) await asyncio.gather(*tasks) # 假设你的main函数是调用looper async def main(lines): await looper(lines) # asyncio.run(main(lines))
额外优化建议
如果数据量达到数十万级别,不建议一次性创建所有任务存到列表,会占用大量内存,可以按批次处理:每批创建200个任务,跑完一批再创建下一批,内存占用会低很多。
内容的提问来源于stack exchange,提问作者Guillermo
相关产品推荐
相关产品推荐

