Python 生态中Requests 凭借简洁优雅的 API 成为 HTTP 请求的首选库几乎是所有 Python 开发者接触网络编程的第一选择。但不少人在长期使用、业务量增长后会遇到非常直观的性能衰减同样的接口起初请求仅需几十毫秒慢慢变成几百毫秒甚至数秒单次请求尚可接受批量调用时更是卡顿严重耗时成倍增长。事实上Requests 的核心逻辑非常轻量绝大多数性能问题并非库的底层缺陷而是使用姿势不当、配置不合理、网络环境变化共同导致的。本文从连接管理、IO 模型、资源释放等 8 个核心维度拆解 Requests 变慢的根因并给出可直接落地的优化方案。一、最容易忽略的坑没有复用连接每次都重新握手1.1 TCP/TLS 握手的隐形开销一次 HTTP 请求的总耗时里网络握手往往占了极高比例。普通 HTTP 请求需要 TCP 三次握手HTTPS 还要额外加上 TLS 密钥交换握手在高延迟网络跨地域、跨国下握手的耗时甚至远超接口本身的业务处理时间。1.2 单次请求的默认行为用完即毁很多开发者习惯直接调用requests.get()/requests.post()发起请求这种写法下每一次请求都会创建全新的 TCP 连接请求结束后立即释放连接完全没有复用机制。单条请求的差异微乎其微但循环调用几十上百次时累积的握手开销会让整体速度出现肉眼可见的下降。1.3 解决方案用 Session 实现连接复用Requests 提供的Session对象本质是一个自带连接池的请求实例会自动维护同域名的长连接。相同域名的多次请求可以直接复用已建立的 TCP/TLS 连接省去重复握手的开销这是性价比最高的优化手段。python运行import requests # 错误写法每次请求新建连接握手开销重复累积 for _ in range(100): requests.get(https://example.com/api) # 正确写法Session 复用连接仅首次握手 session requests.Session() for _ in range(100): session.get(https://example.com/api) session.close()1.4 进阶调整连接池适配高并发Session 默认的连接池配置为pool_connections10最多维护 10 个不同域名的连接池、pool_maxsize10单个域名最多保留 10 条连接。如果是多线程并发请求连接池满载后新请求会排队等待连接释放直接表现为请求卡顿、耗时变长。高并发场景下可以手动放大连接池python运行from requests.adapters import HTTPAdapter session requests.Session() # 自定义连接池大小匹配业务并发量 adapter HTTPAdapter(pool_connections50, pool_maxsize100) session.mount(http://, adapter) session.mount(https://, adapter)二、DNS 解析的隐性延迟解析叠加与 IPv6 回退2.1 DNS 解析的累积耗时Requests 本身不具备 DNS 缓存能力完全依赖操作系统的 DNS 缓存。当系统缓存过期、或者请求大量不同域名时每次请求都要发起完整的 DNS 查询单次几毫秒到几十毫秒的耗时在批量请求场景下会形成可观的性能损耗。2.2 IPv6 优先导致的超时回退绝大多数操作系统默认优先解析域名的 IPv6 地址但国内很多网络环境并不具备 IPv6 连通性。客户端会先等待 IPv6 连接超时才回退尝试 IPv4 地址这个超时窗口通常在数百毫秒直接让单次请求的耗时凭空增加一大截。2.3 优化方向本地配置低延迟的公共 DNS或搭建本地 DNS 缓存服务纯 IPv4 环境下可在系统 hosts 文件中绑定域名与 IPv4 地址跳过解析高频请求场景下提前解析域名 IP直接通过 IP 请求并手动设置 Host 请求头三、HTTPS 场景TLS 握手与证书验证的性能损耗3.1 TLS 握手的算力与延迟成本HTTPS 请求的 TLS 握手涉及非对称加密、密钥交换算力开销远大于普通 TCP 连接。TLS 1.2 版本的完整握手需要 2 个 RTT网络往返跨国请求下仅握手就可能消耗数百毫秒即使是 TLS 1.3首次握手也需要 1 个 RTT。3.2 证书验证的额外开销Requests 默认开启证书验证verifyTrue会校验服务端证书的合法性、证书链完整性部分环境下还会触发证书吊销状态检查额外增加耗时。3.3 优化建议必须配合 Session 使用Session 会自动复用 TLS 会话避免重复握手内网可信环境、测试场景下可临时关闭证书验证verifyFalse公网环境不推荐存在中间人攻击风险升级 Requests、urllib3 到最新稳定版新版对 TLS 1.3 有完善支持握手延迟可降低一半四、大体积传输非流式处理的内存与速度双损耗4.1 请求端全量加载大文件上传大文件时如果先把文件内容全部读入内存再发送不仅会占用大量内存还会增加内存拷贝、序列化的耗时文件体积越大性能下降越明显。4.2 响应端全量加载响应体Requests 默认会把整个响应体一次性加载到内存中。当接口返回大文件、超长列表数据时完整下载 内存加载的过程会让请求耗时飙升极端情况下还会触发内存溢出导致程序整体卡顿。4.3 流式处理方案python运行# 流式上传直接传递文件对象分块读取传输 with open(large_file.zip, rb) as f: session.post(https://example.com/upload, dataf) # 流式下载分块写入文件不一次性加载全部响应 response session.get(https://example.com/large_file.zip, streamTrue) with open(download.zip, wb) as f: for chunk in response.iter_content(chunk_size8192): f.write(chunk) response.close()五、同步阻塞模型串行请求的天然瓶颈5.1 同步 IO 的本质缺陷Requests 是典型的同步阻塞库每发起一个请求当前线程就会阻塞等待响应返回网络 IO 等待的时间里 CPU 完全闲置。如果批量发起几十上百个请求串行执行的总耗时是所有请求耗时的总和请求数量越多整体速度越慢。5.2 优化方案多线程并发对于 IO 密集型的 HTTP 请求使用线程池可以大幅提升整体吞吐充分利用网络等待时间发起更多请求是 Requests 场景下成本最低的并发方案。python运行from concurrent.futures import ThreadPoolExecutor urls [fhttps://example.com/api/{i} for i in range(100)] session requests.Session() def fetch(url): return session.get(url).text # 10 线程并发总耗时可降低至串行的 1/8 ~ 1/10 with ThreadPoolExecutor(max_workers10) as executor: results list(executor.map(fetch, urls))如果是超大规模的并发请求更推荐使用异步 HTTP 库如 aiohttp但 Requests 配合线程池足以覆盖绝大多数业务场景。六、资源泄漏连接不释放导致连接池耗尽6.1 连接不归还的连锁反应使用streamTrue开启流式响应时如果请求结束后不手动关闭响应对象对应的连接不会被归还到连接池而是一直被占用。随着请求次数增加连接池的可用连接越来越少新请求只能排队等待空闲连接直观表现就是 “程序越跑越慢请求越来越卡”。6.2 Cookie 与请求头膨胀长期复用同一个 Session 时服务端返回的 Cookie 会不断累积请求头体积越来越大。这不仅会增加传输耗时部分服务端还会对过大的请求头做限流或延迟处理进一步拉低请求速度。6.3 修复方案所有流式请求都使用with上下文管理自动关闭响应对象定期清理 Session 中的冗余 Cookiesession.cookies.clear()长生命周期的 Session 定期重建避免连接池老化、资源碎片化七、环境与依赖版本、代理、系统限制的隐性影响7.1 依赖版本过低Requests 的底层核心依赖是 urllib3官方会持续修复性能 bug、优化连接管理逻辑。老旧版本可能存在连接复用失效、内存泄漏等问题直接导致性能下降。7.2 系统代理的额外绕路Requests 会默认读取系统的代理配置。如果环境中配置了无效、高延迟的代理所有请求都会经过代理转发速度会大幅下降甚至出现偶发超时。7.3 系统资源限制Linux 环境下默认的文件描述符上限通常为 1024高并发请求时连接数超过上限会导致无法新建连接出现卡顿、报错。7.4 排查与修复升级 Requests 和 urllib3 到最新稳定版不需要代理时显式关闭session.trust_env False禁止读取系统代理高并发场景调整系统文件描述符上限适配连接数量八、不要忽略服务端限流、链路波动与接口退化很多时候请求变慢并非客户端的问题而是服务端或网络链路的变化服务端限流熔断绝大多数接口都有频率限制短时间内请求过多会被服务端限流返回延迟响应、甚至直接丢包表现为 “请求越频繁速度越慢”。网络链路波动跨网链路拥塞、丢包重传、运营商路由变化都会直接增加请求的往返耗时。接口性能退化服务端接口本身的业务逻辑变更、数据库压力变大都会导致响应耗时变长。排查时可以先用 curl、Postman 等工具测试同一接口对比耗时先区分是客户端问题还是服务端 / 网络问题避免盲目优化。优化排查的优先级建议遇到 Requests 变慢的问题不用盲目调整参数按照从易到难的顺序排查即可优先检查是否使用了 Session 复用连接这是性价比最高的优化确认大文件、大数据量场景是否使用了流式处理排查系统代理、DNS、服务端限流等环境问题批量请求场景引入线程池并发提升整体吞吐最后再调整连接池参数、系统资源限制等深层配置Requests 的性能上限远高于绝大多数业务的需求90% 以上的慢请求问题都源于错误的使用姿势。修正这些问题后通常就能把请求速度提升数倍甚至数十倍。
Requests 为什么速度越来越慢?
Python 生态中Requests 凭借简洁优雅的 API 成为 HTTP 请求的首选库几乎是所有 Python 开发者接触网络编程的第一选择。但不少人在长期使用、业务量增长后会遇到非常直观的性能衰减同样的接口起初请求仅需几十毫秒慢慢变成几百毫秒甚至数秒单次请求尚可接受批量调用时更是卡顿严重耗时成倍增长。事实上Requests 的核心逻辑非常轻量绝大多数性能问题并非库的底层缺陷而是使用姿势不当、配置不合理、网络环境变化共同导致的。本文从连接管理、IO 模型、资源释放等 8 个核心维度拆解 Requests 变慢的根因并给出可直接落地的优化方案。一、最容易忽略的坑没有复用连接每次都重新握手1.1 TCP/TLS 握手的隐形开销一次 HTTP 请求的总耗时里网络握手往往占了极高比例。普通 HTTP 请求需要 TCP 三次握手HTTPS 还要额外加上 TLS 密钥交换握手在高延迟网络跨地域、跨国下握手的耗时甚至远超接口本身的业务处理时间。1.2 单次请求的默认行为用完即毁很多开发者习惯直接调用requests.get()/requests.post()发起请求这种写法下每一次请求都会创建全新的 TCP 连接请求结束后立即释放连接完全没有复用机制。单条请求的差异微乎其微但循环调用几十上百次时累积的握手开销会让整体速度出现肉眼可见的下降。1.3 解决方案用 Session 实现连接复用Requests 提供的Session对象本质是一个自带连接池的请求实例会自动维护同域名的长连接。相同域名的多次请求可以直接复用已建立的 TCP/TLS 连接省去重复握手的开销这是性价比最高的优化手段。python运行import requests # 错误写法每次请求新建连接握手开销重复累积 for _ in range(100): requests.get(https://example.com/api) # 正确写法Session 复用连接仅首次握手 session requests.Session() for _ in range(100): session.get(https://example.com/api) session.close()1.4 进阶调整连接池适配高并发Session 默认的连接池配置为pool_connections10最多维护 10 个不同域名的连接池、pool_maxsize10单个域名最多保留 10 条连接。如果是多线程并发请求连接池满载后新请求会排队等待连接释放直接表现为请求卡顿、耗时变长。高并发场景下可以手动放大连接池python运行from requests.adapters import HTTPAdapter session requests.Session() # 自定义连接池大小匹配业务并发量 adapter HTTPAdapter(pool_connections50, pool_maxsize100) session.mount(http://, adapter) session.mount(https://, adapter)二、DNS 解析的隐性延迟解析叠加与 IPv6 回退2.1 DNS 解析的累积耗时Requests 本身不具备 DNS 缓存能力完全依赖操作系统的 DNS 缓存。当系统缓存过期、或者请求大量不同域名时每次请求都要发起完整的 DNS 查询单次几毫秒到几十毫秒的耗时在批量请求场景下会形成可观的性能损耗。2.2 IPv6 优先导致的超时回退绝大多数操作系统默认优先解析域名的 IPv6 地址但国内很多网络环境并不具备 IPv6 连通性。客户端会先等待 IPv6 连接超时才回退尝试 IPv4 地址这个超时窗口通常在数百毫秒直接让单次请求的耗时凭空增加一大截。2.3 优化方向本地配置低延迟的公共 DNS或搭建本地 DNS 缓存服务纯 IPv4 环境下可在系统 hosts 文件中绑定域名与 IPv4 地址跳过解析高频请求场景下提前解析域名 IP直接通过 IP 请求并手动设置 Host 请求头三、HTTPS 场景TLS 握手与证书验证的性能损耗3.1 TLS 握手的算力与延迟成本HTTPS 请求的 TLS 握手涉及非对称加密、密钥交换算力开销远大于普通 TCP 连接。TLS 1.2 版本的完整握手需要 2 个 RTT网络往返跨国请求下仅握手就可能消耗数百毫秒即使是 TLS 1.3首次握手也需要 1 个 RTT。3.2 证书验证的额外开销Requests 默认开启证书验证verifyTrue会校验服务端证书的合法性、证书链完整性部分环境下还会触发证书吊销状态检查额外增加耗时。3.3 优化建议必须配合 Session 使用Session 会自动复用 TLS 会话避免重复握手内网可信环境、测试场景下可临时关闭证书验证verifyFalse公网环境不推荐存在中间人攻击风险升级 Requests、urllib3 到最新稳定版新版对 TLS 1.3 有完善支持握手延迟可降低一半四、大体积传输非流式处理的内存与速度双损耗4.1 请求端全量加载大文件上传大文件时如果先把文件内容全部读入内存再发送不仅会占用大量内存还会增加内存拷贝、序列化的耗时文件体积越大性能下降越明显。4.2 响应端全量加载响应体Requests 默认会把整个响应体一次性加载到内存中。当接口返回大文件、超长列表数据时完整下载 内存加载的过程会让请求耗时飙升极端情况下还会触发内存溢出导致程序整体卡顿。4.3 流式处理方案python运行# 流式上传直接传递文件对象分块读取传输 with open(large_file.zip, rb) as f: session.post(https://example.com/upload, dataf) # 流式下载分块写入文件不一次性加载全部响应 response session.get(https://example.com/large_file.zip, streamTrue) with open(download.zip, wb) as f: for chunk in response.iter_content(chunk_size8192): f.write(chunk) response.close()五、同步阻塞模型串行请求的天然瓶颈5.1 同步 IO 的本质缺陷Requests 是典型的同步阻塞库每发起一个请求当前线程就会阻塞等待响应返回网络 IO 等待的时间里 CPU 完全闲置。如果批量发起几十上百个请求串行执行的总耗时是所有请求耗时的总和请求数量越多整体速度越慢。5.2 优化方案多线程并发对于 IO 密集型的 HTTP 请求使用线程池可以大幅提升整体吞吐充分利用网络等待时间发起更多请求是 Requests 场景下成本最低的并发方案。python运行from concurrent.futures import ThreadPoolExecutor urls [fhttps://example.com/api/{i} for i in range(100)] session requests.Session() def fetch(url): return session.get(url).text # 10 线程并发总耗时可降低至串行的 1/8 ~ 1/10 with ThreadPoolExecutor(max_workers10) as executor: results list(executor.map(fetch, urls))如果是超大规模的并发请求更推荐使用异步 HTTP 库如 aiohttp但 Requests 配合线程池足以覆盖绝大多数业务场景。六、资源泄漏连接不释放导致连接池耗尽6.1 连接不归还的连锁反应使用streamTrue开启流式响应时如果请求结束后不手动关闭响应对象对应的连接不会被归还到连接池而是一直被占用。随着请求次数增加连接池的可用连接越来越少新请求只能排队等待空闲连接直观表现就是 “程序越跑越慢请求越来越卡”。6.2 Cookie 与请求头膨胀长期复用同一个 Session 时服务端返回的 Cookie 会不断累积请求头体积越来越大。这不仅会增加传输耗时部分服务端还会对过大的请求头做限流或延迟处理进一步拉低请求速度。6.3 修复方案所有流式请求都使用with上下文管理自动关闭响应对象定期清理 Session 中的冗余 Cookiesession.cookies.clear()长生命周期的 Session 定期重建避免连接池老化、资源碎片化七、环境与依赖版本、代理、系统限制的隐性影响7.1 依赖版本过低Requests 的底层核心依赖是 urllib3官方会持续修复性能 bug、优化连接管理逻辑。老旧版本可能存在连接复用失效、内存泄漏等问题直接导致性能下降。7.2 系统代理的额外绕路Requests 会默认读取系统的代理配置。如果环境中配置了无效、高延迟的代理所有请求都会经过代理转发速度会大幅下降甚至出现偶发超时。7.3 系统资源限制Linux 环境下默认的文件描述符上限通常为 1024高并发请求时连接数超过上限会导致无法新建连接出现卡顿、报错。7.4 排查与修复升级 Requests 和 urllib3 到最新稳定版不需要代理时显式关闭session.trust_env False禁止读取系统代理高并发场景调整系统文件描述符上限适配连接数量八、不要忽略服务端限流、链路波动与接口退化很多时候请求变慢并非客户端的问题而是服务端或网络链路的变化服务端限流熔断绝大多数接口都有频率限制短时间内请求过多会被服务端限流返回延迟响应、甚至直接丢包表现为 “请求越频繁速度越慢”。网络链路波动跨网链路拥塞、丢包重传、运营商路由变化都会直接增加请求的往返耗时。接口性能退化服务端接口本身的业务逻辑变更、数据库压力变大都会导致响应耗时变长。排查时可以先用 curl、Postman 等工具测试同一接口对比耗时先区分是客户端问题还是服务端 / 网络问题避免盲目优化。优化排查的优先级建议遇到 Requests 变慢的问题不用盲目调整参数按照从易到难的顺序排查即可优先检查是否使用了 Session 复用连接这是性价比最高的优化确认大文件、大数据量场景是否使用了流式处理排查系统代理、DNS、服务端限流等环境问题批量请求场景引入线程池并发提升整体吞吐最后再调整连接池参数、系统资源限制等深层配置Requests 的性能上限远高于绝大多数业务的需求90% 以上的慢请求问题都源于错误的使用姿势。修正这些问题后通常就能把请求速度提升数倍甚至数十倍。