服务器采用了负载均衡,有两台服务器,部署的代码一样,所以里面的定时任务在某一时间会被同时执行,这就导致了很多其他意外的发生,想要解决的问题基本就三个:单点执行,故障转移,服务状态。这里对比一下网上找的几种方案,:
(1)只在一台服务器上部署该定时任务代码。
优点:解决方法容易理解 缺点:部署麻烦,需要多套代码,且当这台服务器出问题时就没定时任务了。
(2)在定时任务代码上加上某个特定的ip限制,仅某个ip的服务器能运行该定时任务。
优点:解决方法容易理解,部署简单,不需要多套代码。 缺点:同上,只能规定一台服务器运行,发送故障时就没办法了。
(3)利用数据库的共享锁事务管理机制来运行定时任务。
参考博客:https://blog.csdn.net/u012881584/article/details/70194237
原理:在数据库中新建一张表定时任务表,存储了上次执行定时任务的ip地址(ip),任务名称(task_name),是否正在执行(execute)。原博客用代码的方式解释了自己的思路,这里我用文字来总结一下:
集群中的所有服务器都是走以下流程
第一步:查找数据库的定时任务表。
第二步:检查是否有机器在运行定时任务。检查方法:update定时任务表的excute字段为1(1为执行中,0为未执行)、ip为自己的ip,如果update失败,则证明有机器在执行该定时任务,该机器的定时任务就不执行了,成功则进行第三步。
第三步:执行定时任务的具体内容。
第四步:还原excute字段为0。
以上是该方案的流程,利用了mysql的共享锁机制判断,通过是否更新成功来判断是否有机器正在执行定时任务,这种方案可以保证任务只执行一次,且只要集群中有一台服务器是好的,就会执行任务。方案挺好,暂时想不到有啥缺点,可能增加了数据库的负担算一个吧....
(4)利用redis数据库。
参考:https://www.jianshu.com/p/48c5b11b80cd
原理:和第三种差不多,只是通过redis的key-value来存储任务名--执行ip。执行定时任务前先查询redis是否有改任务的值,没有就自己 执行,并插入新的key-vale。有的话就查看ip是否是自己,是的话就执行,不是的话就 证明有其他机器在执行,自己就不执行啦。过期时间可以自己设置,方便有机器出故障时候可以转移机器执行任务。
优点:利用了redis的自动过期机制实现了转移故障机器的问题,比较简单,而且redis的访问速度也很快。
缺点:这里没有事务管理机制,访问redis的时候,一定会出现高并发的情况,所以得自己实现redis的共享锁机制。
(5)利用quartz集群分布式(并发)部署解决方案。
参考:https://www.tuicool.com/articles/B3qeUrB
quartz有很成熟的分布式并发部署定时任务的解决方案了,但是配置比较复杂,且需要新建恨的数据库表,这里就不详细写了(好吧,我也没认真看....)
综上所述,我觉得第三种方案适合小型的项目去做,大的项目最好用quartz去做。
在实现的过程中又发现,同一台服务器上的不同版本之间也会发生cron重复执行的问题,所以不仅考虑不同服务器的问题,还得考虑不同版本之间的问题。
网友评论