是否要保证单例,其实看你具体的业务需求,如果重复执行不影响你的业务逻辑,那其实业务上是可以容忍的,但是技术上分析,重复执行定时任务,必定会占用和损耗计算机的资源。如果我一定要保证单例,有哪些方案呢?
1,使用分布式锁
分布式锁的方案很常见,一般也用redis锁或者zk锁,我个人推荐zk锁,因为任务的执行时间一般过长,有时候因为服务器的自身情况(或繁忙或空闲),所以会导致redis的过期时间不好评估,所以无疑增加了有可能重复跑任务的风险。
另外,每个服务器时间的机器时间是不统一的,所以在执行定时任务比较短的情况,比如每一分钟执行,很难用锁百分百保证。
2,分布式调度框架
elastic-job ,xxxx-job等分布式任务调度框架,如果你项目中没有集成,那么引入分布式调度框架会增加系统的复杂性和运维的复杂性,但是确实一种直接有效的方案。因为你不必自己去想办法去实现,而且功能很强大。
3,修改配置文件
修改属性,让其中一台服务器生效,其他服务都不生效,也就是说固定一台服务跑任务。简单,粗暴,但是如果那台跑任务的服务挂掉了以后,你就game over了。那分布式的意义何在?
4,数据库锁
分布式服务一般都共享一个数据库,所以通过更新数据库表的一行数据的状态,这样通过数据库的锁来保证高可用。这样也不容易出错,不用去考虑一些边界和临界区。实现起来也是最简单的。虽然比分布式锁慢,但是定时任务本身就不追求性能。
5,IP
①先在代码里获取获取到当前实例的ip,通过一定算法规则转成一个Long型。
②从注册中心里根据实例名,获取ip的list,例如192.168.2.10、192.168.2.11、192.168.2.12,也分别把它们转成对应的Long型。
③拿第一步里的Long和第二步获取的Long的List做对比,如果判断到它是集合里最小的,那就在该实例里执行task,否则就retur掉。
这样就能确保永远只有一个实例执行定时任务了。