Python网络请求需分设连接超时(2–5秒)和读取超时(5–15秒),用requests.timeout=(connect,read)精确控制;捕获Timeout异常并重试,结合指数退避与监控提升健壮性。

Python网络请求容易因网络波动、服务不可用或响应缓慢而卡住,设置合理的超时机制是提升程序健壮性的关键一步。不设超时,程序可能无限等待;超时设得太短,又容易误判正常延迟为失败。核心在于分层控制:连接超时(connect timeout)和读取超时(read timeout)需分别设置,且应结合重试与异常分类处理。
明确区分 connect 和 read 超时
使用 requests 库时,timeout 参数接受元组 (connect_timeout, read_timeout)。前者控制建立 TCP 连接的最大等待时间(如 DNS 查询、三次握手),后者控制连接建立后等待服务器返回数据的时间(含响应头和响应体)。例如 requests.get(url, timeout=(3, 10)) 表示 3 秒内必须连上,连上后最多等 10 秒收完响应。
- connect 超时通常设为 2–5 秒:过长会拖慢故障发现,过短易在高延迟网络中误失败
- read 超时需按业务预期设定:纯 API 接口可设 5–15 秒;下载大文件建议用 stream=True + 分块读取,并单独控制每块读取的 timeout
- 避免只传单个数字(如 timeout=10):这会同时作用于 connect 和 read,无法精细调控
捕获具体异常,避免吞掉关键错误
requests 的超时抛出的是 requests.exceptions.Timeout,它继承自 requests.exceptions.RequestException,但不同于 ConnectionError 或 HTTPError。应单独捕获 Timeout 并做针对性处理(如降级、告警、重试),而不是用 except Exception: 一并兜底。
- 区分 requests.exceptions.Timeout 和 urllib3.exceptions.ReadTimeoutError:后者更底层,但在高版本 requests 中通常被统一为前者
- ConnectionError 多由网络中断、DNS 失败或目标拒绝连接引发,与超时逻辑不同,建议分开日志记录和应对策略
- HTTPError(如 404、502)属于服务端响应,不是超时问题,不应混为一谈
引入指数退避重试,提升容错能力
单次超时不代表永久失败。配合 retry 策略可显著提升成功率。推荐使用 urllib3 的 Retry 类或 requests.adapters.HTTPAdapter 配置重试逻辑,而非手写 while 循环。
立即学习“Python免费学习笔记(深入)”;
- 设置 total=3、backoff_factor=1:首次失败后等 1 秒,第二次失败后等 2 秒,第三次失败后等 4 秒(即 2^(n-1) 秒)
- 仅对幂等操作(如 GET、HEAD)启用重试;POST/PUT 等非幂等请求需谨慎,或改用带唯一 ID 的幂等设计
- 将超时异常(Timeout)和连接类异常(ConnectTimeout、ConnectionError)纳入 retryable_exceptions,但排除 HTTPError
监控与可观测性不能少
超时不是静默失败的终点,而是可观测性的起点。记录每次请求的耗时、是否超时、重试次数、最终状态,有助于定位网络瓶颈或服务异常。
- 用 time.perf_counter() 手动打点,或借助 requests hooks(如 response hook)自动统计
- 超时率突增是典型预警信号,应接入 Prometheus + Grafana 或日志告警系统
- 对高频调用接口,可动态调整超时值:根据历史 P95 延迟上浮 20% 作为新 read timeout,实现自适应










