You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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并发反而变慢的可能原因

异步框架不是银弹,尤其是当你的瓶颈不是单纯的网络等待,而是响应解析+客户端资源竞争的时候,协程的优势反而会被抵消:

  1. 协程切换开销:grequests基于gevent,单进程协程模型下,4个请求同时在解析大响应(CPU密集型操作),会互相抢占GIL,导致每个请求的解析时间都被拉长。
  2. 服务端限流:很多服务会对并发请求做速率限制,当你一次性发4个请求时,服务端可能会故意放慢响应速度,避免被压垮。
  3. 客户端带宽瓶颈: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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.26 10:54:13