connectionrefusederror 不能仅靠 try/except 一捕了之,因其不带重试时机信息、网络库秒抛异常,且常与 timeouterror 等混发;应结合 backoff 指数退避、显式 connect timeout 和多异常类型捕获。

ConnectionRefusedError 为什么不能靠 try/except 一捕了之
直接 try/except ConnectionRefusedError 捕获后重试,大概率会立刻失败——因为错误本身不带重试时机信息,且多数网络库(如 requests、urllib)在连接被拒时根本不会等,而是秒抛异常。更关键的是,ConnectionRefusedError 和 TimeoutError、ConnectionResetError 常混发,只盯一个容易漏掉真实可恢复的场景。
用 backoff 库做指数退避重试最省心
手动写 time.sleep(2 ** attempt) 容易出错(比如忘记加 jitter 防止雪崩),推荐用 backoff 库,它专为这类临时性网络错误设计:
import backoff import requests <p>@backoff.on_exception( backoff.expo, (ConnectionRefusedError, requests.ConnectionError, requests.Timeout), max_tries=5, jitter=backoff.random_jitter ) def fetch_data(url): return requests.get(url, timeout=5) </p>
-
backoff.expo自动按 1s → 2s → 4s → 8s → 16s 指数增长,避免重试风暴 - 必须把
requests.ConnectionError和requests.Timeout一起列进元组,否则超时或 DNS 失败时不会重试 -
jitter=backoff.random_jitter让每次退避时间在 ±0.5s 内浮动,防止多实例同步重试压垮服务
requests 里要显式控制 connect timeout
requests.get(url, timeout=10) 的 timeout 是总超时,包含 DNS 解析 + 连接 + 响应全过程。但 ConnectionRefusedError 发生在「尝试建连」阶段,如果 DNS 解析卡住 8 秒,剩下 2 秒根本不够建连,结果还是报 TimeoutError 而非 ConnectionRefusedError,导致重试逻辑失效。
- 改用
timeout=(connect_timeout, read_timeout)元组:例如timeout=(3, 7)表示最多等 3 秒连上,连上后再等 7 秒收响应 - 把
connect_timeout设小(1–3 秒),才能快速暴露ConnectionRefusedError,让重试机制及时介入 - 注意:某些旧版
requests(
别忽略底层 socket 层的 SO_KEEPALIVE 和 TCP_FASTOPEN
如果重试频繁失败,可能不是应用层问题,而是连接池复用时底层 TCP 状态异常。比如服务端主动断连后,客户端 socket 还处于 ESTABLISHED 状态,下次复用就直接报 ConnectionRefusedError。
- 给
requests.Session()加连接池配置:pool_connections=10、pool_maxsize=10,避免默认的单连接瓶颈 - 启用 keepalive(需配合服务端):
session.mount('http://', requests.adapters.HTTPAdapter(pool_connections=10, pool_maxsize=10)) - Linux 下可全局开启
TCP_FASTOPEN(内核 3.7+):减少 SYN 包往返,降低连接拒绝概率,但需服务端也支持
真正难处理的不是单次拒绝,而是重试策略和底层 TCP 参数没对齐——比如退避时间比服务端连接空闲超时还短,或者连接池一直复用坏连接。这些细节不调,光包一层 try/except 没用。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











