使用aiohttp进行异步调用无法扩展的性能问题求助
优化aiohttp异步请求吞吐量的方案
看起来你的aiohttp异步请求遇到了明显的性能瓶颈啊,结合你给出的性能分析数据和场景,我来帮你拆解问题并给出针对性的优化建议:
问题核心分析
你的服务端是立即响应的,RTT仅7ms,理论上2000个异步请求应该在几毫秒内完成,但实际耗时超2秒,性能分析里poll调用占了0.9秒——这说明默认的事件循环IO多路复用机制效率不足,再加上可能的连接池限制,导致请求排队、IO等待耗时被放大。
具体优化步骤
1. 替换为更高效的事件循环后端
Python默认的select selector在高并发场景下性能很差,换成uvloop(基于libuv的高性能事件循环)能大幅降低poll调用的耗时:
- 先安装依赖:
pip install uvloop - 在代码开头替换事件循环策略:
import asyncio import uvloop # 全局替换为uvloop asyncio.set_event_loop_policy(uvloop.EventLoopPolicy())
uvloop会自动使用系统最优的多路复用机制(Linux下是epoll,macOS是kqueue),比默认实现快数倍,能直接减少IO等待的开销。
2. 调整aiohttp连接池参数
aiohttp默认的TCPConnector连接池大小(limit)是100,limit_per_host也是100——这意味着你发起2000个请求时,大部分请求会排队等待可用连接,直接拉长了总耗时。
- 创建ClientSession时调大连接池限制:
from aiohttp import ClientSession, TCPConnector # 根据需求设置合适的连接数,2000的话可以直接设为对应值 connector = TCPConnector(limit=2000, limit_per_host=2000) async with ClientSession(connector=connector) as session: # 你的请求逻辑 tasks = [asyncio.create_task(session.get("http://your-server")) for _ in range(2000)] await asyncio.gather(*tasks)
注意:不要设置过大导致系统端口耗尽,2000的量级在大多数系统下是安全的。
3. 优化任务调度方式
确保请求任务是被正确异步调度的:
- 使用
asyncio.create_task提前创建任务,而不是直接将协程对象传入asyncio.gather,这样事件循环能更早地调度任务执行,减少启动延迟。
4. 系统层面的辅助优化
- 调大文件描述符限制:Python进程默认的文件描述符限制可能不足以支撑2000个并发连接,临时调整:
ulimit -n 65535 - 优化TCP参数:减少TIME_WAIT状态的连接占用端口,提升连接复用效率:
sysctl -w net.ipv4.tcp_tw_reuse=1 sysctl -w net.ipv4.tcp_fin_timeout=30
预期效果
做完这些优化后,2000个请求的总耗时应该会接近RTT的7ms左右,吞吐量也能突破当前1000req/s的限制,达到更理想的水平。
内容的提问来源于stack exchange,提问作者rajan sthapit




