背景:
功能简介: 项目中有一个功能: 定时钟(每5分钟)增量更新产品数据到es.
大致逻辑: 单表查询大量元数据添加到redis, 然后依据redis中的元数据更新产品数据到es.
配置定时钟方式: @Scheduled(cron = “${task.product.incr.cron}”)
现象:
定时钟执行几百次之后, 会异常停止, 再也不执行, 每次都用重启服务的方式解决.
研究出的一些点:
1. 本次定时钟只要未执行完毕, 下次定时钟就不会执行.
本人解决此问题的一些记录:
前期:
方式: 各种优化程序.
结果: 定时钟有效执行次数增加了, 但是还是会异常停止.
中期:
同前期
后期:
方式:
1.从大量日志中定位问题, 发现某次定时钟的异常停止没有报错, 是执行一个千次级的循环代码时小概率性程序卡死, 导致后面的定时钟不执行, 无法在代码上定位原因
2.偶然发现java.util.concurrent.Future可以利用, Future类作用: 可以给一个方法设置超时时间, if(超时) -> throw TimeoutException
3.所以代码变更为:
ExecutorService executorService = Executors.newSingleThreadExecutor();
// callable里有定时钟要执行的方法体
Future<String> future = executorService.submit(callable);
try {
future.get(20, TimeUnit.MINUTES);
} catch (InterruptedException e) {
log.info("本次产品同步异常(InterruptedException)");
} catch (ExecutionException e) {
log.info("本次产品同步异常(ExecutionException)");
} catch (TimeoutException e) {
log.info("本次产品同步异常(TimeoutException)");
} finally {
isRuningProductSyncTask = false;
incrTaskExcuteCount ++;
log.info("本次产品同步完成, 耗时: {}秒", (System.currentTimeMillis() - startTime) / 1000);
}
其中callable记录了需要执行的方法(设置了20分钟超时):
Callable<String> callable = new Callable<String>() {
@Override
public String call() throws Exception {
try {
if(incrTaskExcuteCount % fullSyncNum == 0 && incrTaskExcuteCount != 0) {
log.info("准备执行产品\"全量\"同步, incrTaskExcuteCount = {}", incrTaskExcuteCount);
productFullSync();
}else {
log.info("准备执行产品\"增量\"同步, incrTaskExcuteCount = {}", incrTaskExcuteCount);
productIncrSync();
}
} catch (Exception e) {
e.printStackTrace();
log.error(CLASS_LOG_PREFIX + e.getMessage());
}
return "over";
}
};
4.达到的效果: 即使定时钟程序小概率卡死, 也能在20分钟后跳出本次定时钟以便继续执行下次定时钟
结果:
比较理想,没有出现过定时钟异常停止的问题
总结:
并没有从根本上解决此问题(没有治本)(/捂脸), 换了一种方式解决此问题
关键字:
spring定时钟, 定时钟停止, 定时钟异常停止, java, @Scheduled