第 7 章 · 超时与重试机制
本章目标:掌握 requests 和 httpx 的超时配置与重试策略,避免请求永远挂起或无限重试。
7.1 为什么要设置超时
没有超时的请求是最常见的生产事故来源之一。想象一下:你的服务端调用第三方支付 API,对方服务器宕机但 TCP 连接不关闭——你的请求会一直挂在那里,直到耗尽所有 worker 线程,整个服务随之雪崩。
python
import requests
# 危险:没有超时,可能永远挂起
resp = requests.get('https://slow-api.example.com/data')
# 正确:设置超时
resp = requests.get('https://slow-api.example.com/data', timeout=5)超时分为两个维度:
- 连接超时(connect timeout):等待 TCP 握手完成的最长时间;
- 读取超时(read timeout):等待服务器返回数据的最长时间。
7.2 requests 超时配置
requests 的 timeout 参数支持两种形式:
python
import requests
# 方式一:单一超时(连接 + 读取共享)
resp = requests.get('https://httpbin.org/delay/1', timeout=5)
# 方式二:元组(connect_timeout, read_timeout)
resp = requests.get('https://httpbin.org/get', timeout=(3, 10))
# 3 秒内建立连接,10 秒内读取响应如果超时发生,requests 会抛出 requests.exceptions.Timeout 异常:
python
try:
resp = requests.get('https://httpbin.org/delay/10', timeout=2)
except requests.exceptions.Timeout:
print('请求超时')7.3 重试机制:HTTPAdapter
requests 通过 urllib3 的 Retry 机制实现自动重试。首先需要注册一个 HTTPAdapter:
python
import requests
from urllib3.util.retry import Retry
from requests.adapters import HTTPAdapter
# 创建重试策略
retry_strategy = Retry(
total=3, # 最大重试次数
backoff_factor=1, # 重试间隔:0s, 1s, 2s...
status_forcelist=[429, 500, 502, 503, 504], # 仅重试这些状态码
allowed_methods=["GET", "POST"] # 仅重试这些方法
)
# 挂载到 session
session = requests.Session()
session.mount('https://', HTTPAdapter(max_retries=retry_strategy))
session.mount('http://', HTTPAdapter(max_retries=retry_strategy))
# 使用 session 发送请求,自动重试
resp = session.get('https://httpbin.org/status/500')backoff_factor 控制重试等待时间:第 N 次重试等待 backoff_factor × (2^(N-1)) 秒。设置为 1 时,重试间隔为 0、1、2、4 秒。
7.4 httpx 超时配置
httpx 的超时系统更精细,支持四种超时类型:
python
import httpx
# 方式一:单一超时
with httpx.Client(timeout=10.0) as client:
resp = client.get('https://httpbin.org/get')
# 方式二:精细超时配置
timeout = httpx.Timeout(
connect=3.0, # 连接超时 3 秒
read=10.0, # 读取超时 10 秒
write=30.0, # 写入超时 30 秒
pool=5.0 # 连接池等待超时 5 秒
)
with httpx.Client(timeout=timeout) as client:
resp = client.get('https://httpbin.org/get')httpx 在超时后会抛出对应的异常:ConnectTimeout、ReadTimeout、WriteTimeout、PoolTimeout。
7.5 重试与超时的组合策略
生产环境通常同时需要超时和重试:
python
import httpx
from httpx import Timeout
# 配置精细超时 + 重试
timeout = Timeout(connect=5.0, read=30.0, write=10.0, pool=5.0)
limits = httpx.Limits(max_connections=100, max_keepalive_connections=20)
with httpx.Client(timeout=timeout, limits=limits) as client:
try:
resp = client.get('https://api.example.com/data')
resp.raise_for_status()
except httpx.ConnectTimeout:
print('连接超时')
except httpx.ReadTimeout:
print('读取超时')
except httpx.HTTPStatusError as e:
print(f'HTTP 错误: {e.response.status_code}')7.6 本章小结
- 超时是生产环境的必备防护,防止请求无限挂起;
- requests 使用元组
(connect, read)配置双超时; - httpx 提供
Timeout对象,支持 connect/read/write/pool 四种超时; - 通过
HTTPAdapter+Retry可实现智能重试,配合backoff_factor避免雪崩。
🧪 随堂测验
点击你认为正确的选项。答错时会展示正确答案与原因解析。
1. requests 的 timeout 参数接受哪种格式?
2. urllib3 的 Retry 中 backoff_factor=1 时,第三次重试的等待时间是?
3. httpx 的 Timeout 不支持以下哪种超时类型?
4. 以下哪种做法最适合生产环境的 HTTP 请求?
🛠️ 动手实践
- 编写代码测试超时效果:访问
https://httpbin.org/delay/5,设置 2 秒超时,捕获并打印异常。 - 实现一个带重试的 session:对
https://httpbin.org/status/500最多重试 3 次,间隔 1 秒,打印重试次数。 - 用 httpx 配置精细超时,对比四种超时异常的区别。
完成练习后,进入下一章:下一章。