aiohttp通过异步非阻塞机制将网络等待时间复用,使100个请求整体耗时接近单个最慢请求的2秒,而非串行的200秒;而requests同步阻塞导致cpu长期空闲,性能差距源于此。

因为aiohttp在等待网络响应时不会停住,而是立刻切去处理其他请求;requests则必须等一个响应回来才能发下一个——这中间的“空等”时间,就是性能差距的全部来源。
同步Requests的阻塞本质:线程在干等
Requests每次调用 requests.get(),底层会阻塞当前线程,直到TCP连接建立、请求发出、服务器响应返回、响应体读完。这期间CPU几乎不工作,线程处于休眠状态。
常见错误现象:RuntimeWarning: coroutine 'ClientSession.get' was never awaited,往往是因为把 aiohttp.ClientSession.get() 当成 requests.get() 直接调用了,没加 await,也没跑在事件循环里。
- 每个请求平均耗时2秒,其中真正干活(发包、收包头)不到0.1秒,其余1.9秒都在等
- 爬100个页面 → 串行等待 ≈ 200秒,CPU利用率长期低于5%
- 加多线程/多进程能缓解,但资源开销大、管理复杂、易触发反爬
aiohttp + asyncio 的非阻塞机制:复用等待时间
aiohttp不自己做IO,而是把socket操作交给asyncio事件循环统一调度。当某个请求卡在“等响应”阶段时,事件循环自动切走,执行另一个已就绪的协程任务。
关键不是“更快”,而是“不让CPU闲着”。100个请求并发发出去,响应谁先到谁先处理,整体耗时接近单个最慢请求的延迟(比如2秒),而不是所有请求延迟之和。
- 必须用
aiohttp.ClientSession()复用连接,否则每次新建session会重连、重握手,反而更慢 - 不能在协程里混用
time.sleep(),得用await asyncio.sleep() - 避免在
async with session.get(...)内部做耗时同步操作(如正则解析、写文件),否则拖垮整个事件循环
真实瓶颈常不在HTTP库本身
很多人换了aiohttp发现提速不明显,问题往往出在周边配置上:
- DNS解析默认用系统阻塞 resolver,高并发下成为瓶颈 → 改用
TCPConnector(resolver=AsyncResolver()) - 没限制并发数,瞬间发几百请求被目标站限流或断连 → 加
asyncio.Semaphore(10) - SSL验证、重定向、Cookie处理默认开启,增加开销 → 按需关掉,比如
ssl=False(仅测试环境)、allow_redirects=False - 目标网站强制HTTP/2(如部分CDN),而aiohttp不支持 → 得换
httpx.AsyncClient
真正决定快慢的,从来不是库名,而是你有没有让等待时间被其他请求填满。一旦漏掉连接复用、并发控制或DNS优化中任意一环,异步优势就会打五折甚至归零。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











