微服务中定时任务的重复执行问题
在微服务架构中,多个节点都单独部署了应用,那么对于应用中的定时任务应该如何避免重复执行呢?
从抽象层面而言,这似乎是一个并发问题,但实际上这是一个选举问题:
1.任务必须执行且仅执行一次(不能重复)
2.当某个微服务实例故障,其他实例仍然可以执行定时任务(故障转移)
3.当某个实例在执行任务过程中失败(补偿机制)此细节本文不做讨论
对于此类“分布式并发”场景,☝️🤓 诶!可以使用分布式锁Redisson方案。其实不然,正如我上面所述这是选举问题,而非并发问题。
Redisson解决的是并发问题(特别是高并发),此场景下的并发量取决于实例的数量,而且还受到实例之间时间偏差的影响。
因此,利用Redis的SETNX命令就已经足够应付此场景,无须引入更复杂的Redisson方案。
代码示例
@Component
public class ScheduleTask {
@Autowired
private StringRedisTemplate stringRedisTemplate;
/**
* 每分钟执行
*/
@Scheduled(cron = "0 * * * * ?")
public void executeTask() {
Boolean bool = stringRedisTemplate.opsForValue().setIfAbsent(GlobalConst.SCHEDULE_TASK, "lol", 30, TimeUnit.SECONDS);
LocalDateTime now = LocalDateTime.now();
if (Boolean.TRUE.equals(bool)) {
System.out.println("定时任务执行了!当前时间:" + now);
} else {
// 锁获取失败,打印日志或记录
System.out.println("定时任务未执行,因为未能获取锁。当前时间:" + now);
}
}
}
需要注意的是:
定时任务需要执行的时间一定要足够小于Redis对应key的持续时间(不能任务还未执行完,就提前释放锁)
Redis对应key的持续时间一定要接近定时任务循环周期时间(锁的持续时间尽量接近一个循环周期)
定时任务执行过程中发生异常时,如何触发补偿机制?或者是否需要监控定时任务一定执行成功了?MQ?