应区分可恢复与不可恢复的SocketException,对连接重置等网络抖动异常采用do-while+指数退避(基值100ms、最多4次、含随机抖动)重试,并动态延长超时时间,失败时记录日志并抛出包装异常。

遇到网络抖动导致的 SocketException(比如连接被重置、远程主机强迫关闭等),不能简单捕获后立即重试,而应结合异常分类、指数退避和最大次数限制,让重试既有效又不加重服务压力。
只对可恢复的 SocketException 重试
不是所有 SocketException 都适合重试。重点区分以下两类:
-
建议重试的场景:如
Connection reset、Broken pipe(常因服务端瞬时重启或负载过高)、Network is unreachable(临时路由问题)——这些多由网络抖动引起,具备恢复可能; -
不建议重试的场景:如
Permission denied(本地防火墙拦截)、Address already in use(端口冲突)、Too many open files(系统资源耗尽)——属于本地配置或资源瓶颈,重试无效,需排查环境。
实际判断时,可通过 e.getMessage().toLowerCase() 或 e.getClass().getSimpleName() 辅助识别,避免误重试。
用 do-while 实现带退避的重试循环
推荐用 do-while 结构,天然保证至少执行一次请求,逻辑更清晰。关键参数建议设为:
立即学习“Java免费学习笔记(深入)”;
在 Java 中初始化和管理阿里云 SDK客户端。包括单例模式、线程安全、endpoint 与 region 配置、VPC 终端节点、同步与异步等。
-
基础延迟:100 毫秒起始(
baseDelayMs = 100); - 最大尝试次数:含首次共 4 次(即最多重试 3 次);
-
退避公式:
delay = baseDelayMs * (1L << attempt)(位运算比Math.pow更快更准); -
加随机抖动:乘以
0.9 ~ 1.1的随机因子,防止重试时间对齐造成“雪崩”。
在 catch 中动态调整超时与重试间隔
网络抖动往往伴随响应变慢,单纯等固定时间不够智能。应在每次重试前同步延长:
- 连接超时(connect timeout):从 1 秒 → 2 秒 → 4 秒递增;
- 读取超时(read timeout):从 3 秒 → 6 秒 → 12 秒递增;
- 重试等待间隔:按指数退避 + 抖动计算,例如第 2 次重试前休眠约 250ms,第 3 次约 700ms。
使用 OkHttp 时,可通过 Request.tag() 携带重试序号,在拦截器中重建 OkHttpClient 并设置对应超时值;用 HttpURLConnection 则需手动调用 setConnectTimeout() 和 setReadTimeout()。
记录日志并及时终止不可恢复失败
每次重试失败都应记录简明日志,包含异常类型、消息、当前重试次数和下次等待时间。例如:
[WARN] SocketException: Connection reset by peer, retry #2 in 248ms同时必须设置硬性退出条件:
- 达到最大重试次数后抛出原始异常包装的
RuntimeException; - 捕获到
InterruptedException时恢复中断状态,不吞掉线程信号; - 避免用
Timer或ScheduledExecutorService做延迟——它们会丢失上下文且难以统一异常处理。

















