在单机模式下,定时任务是没什么问题的。但当我们部署了多台服务,同时又每台服务又有定时任务时,若不进行合理的控制在同一时间,只有一个定时任务启动执行,这时,定时执行的结果就可能存在混乱和错误了。
这里简单的说说相关的解决方案吧,一家之言,希望大家能提出自己的见解,共同进步!
剥离所有定时任务到一个工程:此方案是最简单的,在定时任务相对较小,并发任务不多时,可以使用此方案。简单也容易维护。当定时任务牵扯的业务越来越多,越来越杂时,维护量就成本增加了,工程会越来越臃肿,此方案就不实用了。
利用Quartz集群方案:本身Quartz是支持通过数据库实现集群的,以下是其集群架构图:
其实现原理也相对简单:通过数据库实现任务的持久化,保存定时任务的相关配置信息,以保证下次系统启动时,定时任务能自动启动。同时,通过数据库行锁(for update)机制,控制一个任务只能被一个实例运行,只有获取锁的实例才能运行任务,其他的只能等待,直到锁被释放。这种方式有些弊端,就是依赖了数据库,同时也需要保证各服务器之间的时间需要同步,不然也是会混乱的。
现在Quartz也有基于Redis的集群方案,有兴趣的可以搜索下。
分布式锁:可通过使用Redis或者ZooKeeper实现一个分布式锁的机制,使得只有获取到锁的实例方能运行定时任务,避免任务重复执行。可查看下开源的基于Redis实现的分布式锁项目:redisson。github地址:https://github.com/redisson/redisson有兴趣的同学可以了解下。
统一调度中心:
可构建一个纯粹的定时服务,只有定时器相关配置,比如定时时间,定时调度的api接口或者http服务,甚至是统一注册中心下的服务类,如dubbo服务等。而具体的任务执行操作都在各自业务方系统中,调度中心只负责接口的调用,具体实现还是在业务方。这种方案相对来说比较通用,实现起来也简单。就是需要业务方进行约定编程,或者对外提供一个api接口。
当然,为了实现定时任务的自动发现和注册功能,还是需要规范一套规则来实现自动注册功能。简单来说,以Dubbo服务为例,可以定义一个定时任务接口类,调度中心只需要获取所有实现此接口的服务,同时通过服务的相关配置(调度时间、失败策略等)进行相关定时操作。或者编写一个服务注册与发现的客户端,通过Spring获取到实现此接口的所有实现类,上送到调度中心。
而且,统一调度中心,还可以对所有的定时任务的调度情况进行有效监控,日志记录等,也可以约定接口,让定时任务回传定时结果,做到全局把控的目的。
以上就是对分布式调度的一点理解,有错误的地方还望指正,有更好的方案也希望能分享下。
参考资料https://www.cnblogs.com/yank/p/3955322.html
https://blog.csdn.net/tsyj810883979/article/details/8481621
https://www.cnblogs.com/javahr/p/8318728.html
https://spring.io/guides/gs/scheduling-tasks/
总结本章节主要是讲解了通过不同的方式实现定时任务。对于定时任务而言,本身是门大学问,一俩篇文章是讲不完的。像SpringTask和Quartz都是很强大的调度器,两者很相似,像如何实现任务的动态修改调度周期,动态停止相关任务,调度任务的监控,这些本文章都没有涉及。还希望有相关需求的同学自行搜索相关资料了。
最后目前互联网上很多大佬都有SpringBoot系列教程,如有雷同,请多多包涵了。本文是作者在电脑前一字一句敲的,每一步都是自己实践的。若文中有所错误之处,还望提出,谢谢。
老生常谈个人QQ:499452441
微信公众号:lqdevOps
个人博客:
完整示例:chapter-22