Java爬虫应仅对SocketTimeoutException、ConnectException等可恢复网络异常重试,采用指数退避+随机抖动控制节奏(如1s、2–3s、4–8s),限制3~5次,并在HTTP客户端层统一实现,GET幂等可安全重试,POST等需谨慎。

Java 爬虫中捕获网络异常并自动重试,关键不是“一.catch了之”,而是精准识别可恢复异常、控制重试节奏、避免副作用,并把逻辑落到合适层级。
只对可恢复的网络异常重试
不是所有报错都该重试。比如 404、403、500 这类 HTTP 状态码,是服务端明确拒绝或出错,再试也没用;而真正适合重试的是底层连接不稳导致的异常:
- SocketTimeoutException:读取超时,大概率是目标响应慢,网络抖动
- ConnectException:连接被拒,可能是瞬时拒绝或代理不稳定
- UnknownHostException:DNS 解析失败,常为临时故障
- SSLHandshakeException(部分场景):如证书链临时验证失败,可尝试重试
- IOException(需判断):若堆栈含 “Connection reset” 或 “Broken pipe”,通常可重试
像 NullPointerException、ParseException 或明确的 4xx/5xx 响应,应直接记录后跳过,不纳入重试流程。
用指数退避+随机抖动控制重试节奏
连续立刻重试容易触发对方限流,也加重自身压力。推荐用指数增长等待时间,并加随机扰动防“重试风暴”:
立即学习“Java免费学习笔记(深入)”;
在 Java 中初始化和管理阿里云 SDK客户端。包括单例模式、线程安全、endpoint 与 region 配置、VPC 终端节点、同步与异步等。
- 第 1 次失败后等约 1 秒
- 第 2 次等约 2–3 秒
- 第 3 次等约 4–8 秒
代码示意(单位毫秒):
long delay = (long) (1000 * Math.pow(2, retryCount) * (0.8 + 0.4 * Math.random()));Thread.sleep(delay);
同时硬性限制最大重试次数,一般设为 3~5 次,超过即抛出原始异常或封装为自定义 CrawlRetryExhaustedException。
把重试逻辑下沉到 HTTP 客户端层
在每个 fetchUrl() 方法里写 try-catch + while 循环,既重复又难维护。更合理的方式是交由客户端统一处理:
-
HttpClient:用
HttpRequestRetryHandler自定义重试判定,配合RequestConfig设置 connectTimeout 和 socketTimeout -
OkHttp:通过
Interceptor拦截失败请求,对指定异常类型或 5xx 响应码触发重试,并管理间隔 -
WebMagic / Crawl4j:框架已内置重试策略,只需配置
setRetryTimes(3)和setSleepTime(2000)即可生效
这样既能复用连接池、超时设置,又能保证所有请求行为一致,还便于后续接入断路器(如 resilience4j)做熔断保护。
注意 GET 与 POST 的重试安全边界
GET 请求天然幂等,重试无副作用;但 POST、PUT、DELETE 可能造成重复提交(如下单、删数据),默认不应自动重试。
- 若必须重试 POST,需确认目标接口支持幂等(如带唯一 request-id 或 token)
- 建议爬虫主流程只对 GET 请求启用自动重试,其他方法由业务逻辑显式控制
- 每次重试前记日志,包含 URL、method、retry count、异常类名,方便定位是否误重试了非幂等请求

















