Python-Requests处理大响应速度慢,grequests并发请求耗时更长
嘿,我来帮你拆解下这个问题——我之前做接口性能测试时也碰到过类似的坑,尤其是大响应体+异步并发的组合场景,咱们一步步捋:
先聊聊
requests.content分块解析慢的根源 requests默认用10KB的分块(对应iter_content(chunk_size=10240))读取响应,对于1-1.5MB的响应来说,要分100多次IO操作,每次小分块的IO开销累加起来就很可观。而且content方法还会自动处理解压缩(比如gzip),这也会额外占点时间。
针对这个问题的优化方案:
- 直接用原始套接字读取:跳过requests的分块逻辑,用
response.raw.read()一次性读取整个响应,速度会快很多。如果服务端返回的是压缩内容,记得手动处理解压缩:import gzip import requests url = "你的API地址" with requests.get(url, stream=True, headers={"Accept-Encoding": "gzip"}) as r: r.raise_for_status() with gzip.GzipFile(fileobj=r.raw) as f: content = f.read() - 增大分块大小:如果不想绕开requests的封装,直接把
chunk_size设为和响应体接近的大小,比如1.5MB:with requests.get(url, stream=True) as r: r.raise_for_status() content = b''.join(r.iter_content(chunk_size=1536*1024))
再说说grequests并发反而变慢的可能原因
异步框架不是银弹,尤其是当你的瓶颈不是单纯的网络等待,而是响应解析+客户端资源竞争的时候,协程的优势反而会被抵消:
- 协程切换开销:grequests基于gevent,单进程协程模型下,4个请求同时在解析大响应(CPU密集型操作),会互相抢占GIL,导致每个请求的解析时间都被拉长。
- 服务端限流:很多服务会对并发请求做速率限制,当你一次性发4个请求时,服务端可能会故意放慢响应速度,避免被压垮。
- 客户端带宽瓶颈:4个并发请求同时下载1-1.5MB的响应,总流量在4-6MB左右,如果你的客户端网络带宽刚好到顶,每个请求的下载时间都会增加。
针对grequests的优化或替代方案:
- 先验证服务端是否限流:单独测1个请求的耗时,再测4个并发下单个请求的耗时,如果后者明显变长,那大概率是服务端限流了,要么降低并发数,要么和服务端团队沟通调整规则。
- 改用多进程模型:如果响应解析是CPU密集型任务,单进程协程不如多进程(绕开GIL限制),用
concurrent.futures.ProcessPoolExecutor试试:from concurrent.futures import ProcessPoolExecutor import requests def fetch_url(url): with requests.get(url, stream=True) as r: r.raise_for_status() return r.raw.read() urls = ["你的API地址"] * 4 with ProcessPoolExecutor(max_workers=4) as executor: results = list(executor.map(fetch_url, urls)) - 换用原生异步框架aiohttp:grequests的gevent协程在某些场景下不如asyncio原生的aiohttp高效,试试这个替代方案:
import aiohttp import asyncio async def fetch_url(session, url): async with session.get(url) as r: r.raise_for_status() return await r.read() async def main(): urls = ["你的API地址"] * 4 async with aiohttp.ClientSession() as session: tasks = [fetch_url(session, url) for url in urls] results = await asyncio.gather(*tasks) if __name__ == "__main__": asyncio.run(main())
额外的调试小技巧
- 用
cProfile定位耗时点:运行python -m cProfile -s cumulative 你的测试脚本.py,看看到底是网络请求、解压缩还是协程切换占了最多时间。 - 测试不同chunk_size的耗时:分别用10KB、100KB、1MB的分块大小跑测试,找到最适合你场景的数值。
内容的提问来源于stack exchange,提问作者user2797437
相关产品推荐
相关产品推荐

