Java多线程场景下ExecutorService调用Jsoup抓取超时自动重试问题咨询
可行解决方案
以下两种方案都可以满足你的需求,可根据实际场景选择:
方案1:Jsoup内置超时+任务内重试(最简化实现)
直接利用Jsoup自带的超时配置,把单次请求的连接、读取超时都设为2秒,再给每个任务加重试逻辑即可,不需要修改原有线程池的调度逻辑。
import org.jsoup.Jsoup; import org.jsoup.nodes.Document; import java.io.IOException; import java.net.SocketTimeoutException; import java.util.concurrent.*; // 封装通用带重试的抓取方法 private Document crawlWebsite(String url, int maxRetryTimes) throws IOException, InterruptedException { int currentRetry = 0; while (currentRetry < maxRetryTimes) { try { // 直接设置Jsoup超时为2000毫秒(2秒),到达阈值直接抛出超时异常 return Jsoup.connect(url) .timeout(2000) .get(); } catch (SocketTimeoutException e) { currentRetry++; if (currentRetry >= maxRetryTimes) { throw e; // 重试次数耗尽后再抛出异常 } // 可选:重试前加短延迟,避免触发目标站反爬策略 Thread.sleep(300 + currentRetry * 200); } } return null; }
修改原有任务提交逻辑:
ExecutorService es = Executors.newCachedThreadPool(); // 提交三个抓取任务,每个最多重试3次 Future<Document> task1 = es.submit(() -> crawlWebsite("网站1地址", 3)); Future<Document> task2 = es.submit(() -> crawlWebsite("网站2地址", 3)); Future<Document> task3 = es.submit(() -> crawlWebsite("网站3地址", 3)); // 后续可通过future.get()获取抓取结果 es.awaitTermination(10, TimeUnit.SECONDS); es.shutdown();
方案2:全局超时监控(适合任务逻辑复杂的场景)
如果你的任务除了Jsoup抓取还有其他逻辑可能超时,或者不能修改Jsoup本身的配置,可以用Future的超时检测机制,单独调度监控线程判断任务是否超时,超时后取消原任务重新提交。
ExecutorService es = Executors.newCachedThreadPool(); // 新增定时线程池做超时监控 ScheduledExecutorService monitorPool = Executors.newScheduledThreadPool(1); int maxRetryTimes = 3; // 封装带重试逻辑的任务,以task1为例 Runnable crawlTask1 = new Runnable() { int retryCount = 0; @Override public void run() { if (retryCount >= maxRetryTimes) { System.out.println("任务1重试次数耗尽,执行失败"); return; } Future<?> taskFuture = es.submit(() -> { // 原有Jsoup抓取逻辑,无需修改 Document doc = Jsoup.connect("网站1地址").get(); // 处理抓取结果 }); // 2秒后检测任务是否完成 monitorPool.schedule(() -> { if (!taskFuture.isDone()) { taskFuture.cancel(true); // 终止超时任务 retryCount++; es.submit(this); // 重新提交任务重试 } }, 2, TimeUnit.SECONDS); } }; // 提交三个任务 es.submit(crawlTask1); // task2、task3按同样逻辑封装后提交 // 所有任务执行完成后关闭两个线程池即可
注意事项
- 重试时建议加递增延迟,避免短时间频繁请求被目标网站拦截
- Jsoup的IO操作默认响应中断,调用
future.cancel(true)可以正常终止阻塞中的连接 - 如果需要等待三个任务全部完成再执行后续逻辑,可以搭配
CountDownLatch做同步控制
内容的提问来源于stack exchange,提问作者ngongocthinh159
相关产品推荐
相关产品推荐

