如何彻底禁用Python requests与urllib3的请求重试?
无论我采取何种操作,都无法停止请求重试。我尝试了ScrapeOps文档中介绍的HTTPAdapter方法,也按照urllib3官方文档设置了retries=False,但当模拟目标URL的连接故障时(期望超时后直接失败,不重试),根据耗时判断程序仍重试了3次(总耗时为超时时间的3倍)。
我尝试了以下几种配置方式:
代码示例1(requests + HTTPAdapter)
import requests import urllib3 from requests.adapters import HTTPAdapter from requests.packages.urllib3.util.retry import Retry http = requests.Session() retries = Retry(total=0, status_forcelist=[ 502, 503, 504 ]) adapter = HTTPAdapter(max_retries=retries) http.mount("http://", adapter) r = http.get(url, timeout=mytimeout)
代码示例2(urllib3 PoolManager全局禁用重试)
http = urllib3.PoolManager(retries=False) r = http.request('GET', url, timeout=mytimeout)
代码示例3(urllib3 请求级禁用重试)
http = urllib3.PoolManager() r = http.request('GET', url, retries=False, timeout=mytimeout)
即便如此,设置2秒超时后最终失败耗时6秒,3秒超时耗时12秒,显然仍存在重试行为。请问我存在哪些误解,或是有更深层的默认配置导致该问题?
1. 漏禁连接/读取重试,且未适配HTTPS请求
代码示例1中,Retry(total=0)仅禁用了HTTP状态码触发的重试,但urllib3的Retry默认会对连接错误(如DNS失败、连接超时)和读取超时进行重试。必须显式设置connect=0和read=0来完全禁用所有重试类型;另外你只挂载了http://的适配器,若目标URL是https://,会使用带重试逻辑的默认适配器,必须同时挂载https://:
import requests from requests.adapters import HTTPAdapter from requests.packages.urllib3.util.retry import Retry http = requests.Session() # 完全禁用所有类型的重试 retries = Retry( total=0, connect=0, read=0, status_forcelist=[502, 503, 504] ) adapter = HTTPAdapter(max_retries=retries) http.mount("http://", adapter) http.mount("https://", adapter) # 关键:HTTPS请求也应用无重试适配器 r = http.get(url, timeout=mytimeout)
2. urllib3的retries=False未完全覆盖底层重试逻辑
urllib3中retries=False等价于Retry(total=0),但同样没禁用连接/读取重试。旧版本urllib3(<1.26)对retries=False的处理存在歧义,建议显式创建无重试策略:
import urllib3 from urllib3.util.retry import Retry # 创建完全禁用重试的策略 no_retry = Retry(total=0, connect=0, read=0) # 全局配置 http = urllib3.PoolManager(retries=no_retry) r = http.request('GET', url, timeout=mytimeout) # 或请求级临时配置 http = urllib3.PoolManager() r = http.request('GET', url, retries=no_retry, timeout=mytimeout)
3. 超时参数的误解导致耗时计算偏差
你设置的timeout=mytimeout如果是单个数值,requests/urllib3会将其同时作为连接超时和读取超时的上限。连接故障时,每个重试周期都会消耗一次连接超时时间,所以3次重试总耗时就是3 * 连接超时,这和你观察到的2秒超时耗时6秒完全匹配——只有完全禁用重试才能让超时后直接失败。
4. 排查全局默认配置是否被修改
部分第三方库或环境可能全局修改urllib3的默认重试参数,可添加以下代码确认:
import urllib3 print(urllib3.Retry.DEFAULT) # 查看默认重试参数 print(urllib3.Retry.DEFAULT.connect) # 默认连接重试次数
如果默认connect值大于0,说明必须显式覆盖该配置才能生效。
内容的提问来源于stack exchange,提问作者Greg Pierro

