分布式调度服务浅谈
在
JAVA
开发领域,目前可以通过以下几种方式进行定时任务:
- Timer:jdk中自带的一个定时调度类,可以简单的实现按某一频度进行任务执行。提供的功能比较单一,无法实现复杂的调度任务。
- ScheduledExecutorService:也是jdk自带的一个基于线程池设计的定时任务类。其每个调度任务都会分配到线程池中的一个线程执行,所以其任务是并发执行的,互不影响。
- Spring Task:
Spring
提供的一个任务调度工具,支持注解和配置文件形式,支持Cron
表达式,使用简单但功能强大。ThreadPoolTaskScheduler
- Quartz:一款功能强大的任务调度器,可以实现较为复杂的调度功能,如每月一号执行、每天凌晨执行、每周五执行等等,还支持分布式调度,就是配置稍显复杂。
在单机模式下,定时任务是没什么问题的。但当我们部署了多台服务,同时又每台服务又有定时任务时,若不进行合理的控制在同一时间,只有一个定时任务启动执行,这时,定时执行的结果就可能存在混乱和错误了。
这里简单的说说相关的解决方案吧,一家之言,希望大家能提出自己的见解,共同进步!
- 剥离所有定时任务到一个工程:此方案是最简单的,在定时任务相对较小,并发任务不多时,可以使用此方案。简单也容易维护。当定时任务牵扯的业务越来越多,越来越杂时,维护量就成本增加了,工程会越来越臃肿,此方案就不实用了。
- 利用
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
- http://www.quartz-scheduler.org/documentation/quartz-2.1.x/quick-start.html
- https://spring.io/guides/gs/scheduling-tasks/
总结
本章节主要是讲解了通过不同的方式实现定时任务。对于定时任务而言,本身是门大学问,一俩篇文章是讲不完的。像
SpringTask
和Quartz
都是很强大的调度器,两者很相似,像如何实现任务的动态修改调度周期,动态停止相关任务,调度任务的监控,这些本文章都没有涉及。还希望有相关需求的同学自行搜索相关资料了。