之前写SpringBoot时,有简单介绍过分布式定时器的一些思路(SpringBoot | 第二十二章:定时任务的使用)。原来的项目本身使用dubbo实现了一个简单的实现,目前项目迁移至SpringCloud后,原来的就不适用了,但基本原理都是差不多的,都是集中管理需要调用的api及调度等相关信息。故本篇会简单介绍下一些常见的分布式定时器的实现方案,还会编写一个基于http调用的 统一调度
项目,实现简单的调用 SpringCloud
项目 RESTful
接口。
一些说明
基于ShedLock实现轻量级分布式定时锁
集成示例
@SchedulerLock注解说明
两种集成模式
基于统一调度中心实现任务调用
quartz工厂类(QuartzJobFactory)
初始化任务(InitJob)
任务类(TaskJob)
技术选型
数据库脚本
相关类说明
服务效果
参考资料
总结
老生常谈
本身Spring提供了 SpringTask
进行定时配置,基于注解和xml配置方式可实现简单的定时器配置,再一些场景下,若在非单机模式下,部署了多个应用时,若不加以控制,很容易造成数据的错误问题。在之前编写的文章中也有简单的提及一些分布式解决方案,比如 Quartz
等,感谢的同学可点击: SpringBoot | 第二十二章:定时任务的使用 ,进行查看,这里就不再重复阐述了。
ShedLock
是一个在分布式环境中使用的定时任务框架,用于解决在分布式环境中的多个实例的相同定时任务在同一时间点重复执行的问题,解决思路是通过对公用的数据库中的某个表进行记录和加锁,使得同一时间点只有第一个执行定时任务并成功在数据库表中写入相应记录的节点能够成功执行而其他节点直接跳过该任务。简单来说, ShedLock
本身只做一件事情: 保证一个任务最多同时执行一次 。所以如官网所说的, ShedLock
不是一个分布式调度器,只是一个锁!
注意: ShedLock
支持 Mongo
, Redis
, Hazelcast
, ZooKeeper
以及任何带有JDBC驱动程序的东西。本例子为了方便,直接使用了 redis
进行示例,若本身基于jdbc等,可直接参考官网给出的提示:https://github.com/lukas-krecan/ShedLock#jdbctemplate. 创建对应的表结构。
创建工程名: java-shedlock-demo
0.maven依赖(这里使用当前最新版本及使用redis进行实现),基于 SpringBoot2.0.3.RELEASE
版本。
1.配置 LockProvider
,同时开启@EnableSchedulerLock注解。
ShedLockRedisConfig.java
2.编写一个简单定时任务。
3.编写启动类开启定时任务功能,及配置文件。
application.properties
4.利用多环境启动多个服务(8001,8002),查看是否正常运行。 8001服务
8002服务
通过日志输出,可以看出每次任务执行时,只有一个实例在运行。具体哪个服务,看谁获取到锁了。
@SchedulerLock注解一共支持五个参数,分别是
name:用来标注一个定时服务的名字,被用于写入数据库作为区分不同服务的标识,如果有多个同名定时任务则同一时间点只有一个执行成功
lockAtMostFor:成功执行任务的节点所能拥有独占锁的最长时间,单位是毫秒ms
lockAtMostForString:成功执行任务的节点所能拥有的独占锁的最长时间的字符串表达,例如“PT14M”表示为14分钟
lockAtLeastFor:成功执行任务的节点所能拥有独占所的最短时间,单位是毫秒ms
lockAtLeastForString:成功执行任务的节点所能拥有的独占锁的最短时间的字符串表达,例如“PT14M”表示为14分钟
按官网介绍,其有两种模式: TaskScheduler
及 Method
代理,具体的可以查看官网介绍,这里就不过多阐述了。简单来说,都是使用 AOP
代理机制,一个是代理了 taskScheduler
,一个是代理了被注解了 SchedulerLock
具体的方法。可以具体场景进行设置,比如记录定时任务日志等。这里需要注意,使用 Method
代理时,其不依赖于Spring环境,但普通调用此方法时也会进行锁定的,需要注意,而且目前只支持 void
的方法。
统一调度中心
:一个管理定时任务配置及发起任务执行的一个服务。简单来说,就是通过维护需要执行任务的服务列表,如 api地址
、 dubbo
服务信息等,通过配置的定时配置进行服务调用。从而避免了定时任务重复问题,同时也能利用注册中心实现负载均衡动态调用对应任务。
核心框架: SpringBoot2.0.3.RELEASE
、 SpringcloudFinchley.SR1
任务调度: Quartz
持久层框架: MyBatis
+ MyBatis-Plus
数据库: mysql
题外话:原本想延续原先 SpringBoot1.5
版本进行开发,后面考虑此服务相对简单,所以直接尝试使用 webflux
进行服务开发,顺便也学习学习 WebFlux
相关操作。
为了使得自定义的job能主动注入spring的相关bean,需要额外实现此工厂类,方便调用。当然也可以直接动态获取bean实例了。
同时,配置 SchedulerFactoryBean
,设置其工厂类。 QuartzConfig.java
在服务启动时,启动开启配置的任务,同时设置其定时器。
实现具体任务的执行和调用。利用 WebClient
实现http服务的调用。暂时未实现dubbo的调用,后期再补充。
配置普通 WebClient
和具有负载均衡的 webClient
,主要是考虑到存在访问 SpringCloud
服务和 普通http
的需求,原先使用负载均衡的 restTemplate
时,访问普通的http请求是无法访问的,不知道 webClient
是否也是一样,这里直接简单粗暴的直接设置了两个 webClient
。
具体执行任务类,根据不同的类型,进行不同的调用。
为了测试,简单改造了 java-shedlock-demo
为 SpringCloud
项目,具体就不贴代码了,可直接下载相应工程进行查看。
数据库配置:
服务启动,控制台输出:
大家可自行测试下,这里只是简单的进行控制台输出。
https://github.com/lukas-krecan/ShedLock
本文主要简单介绍了一些分布式定时任务的解决方案。对于 ShedLock
大部分的分布式场景应该是够用了,特别场景下可能需要注意,实际情况实际解决了。而对于后一种, 统一调度
服务而言,本身只是个简单的示例,后续会考虑加入 dubbo
的支持,及一些其他的特性,如调用反馈,失败次数等等,目前只是简单的为了满足业务需要,后需要会进行优化的,目前就且看吧,一些异常之类的都还没有进行处理⊙﹏⊙‖∣。
个人QQ: 499452441
微信公众号: lqdevOps
个人博客:http://blog.lqdev.cn
完整示例: 统一调度中心:okong-scheduler schedLock-demo:https://github.com/xie19900123/java-learning/tree/master/java-shedlock-demo
如果你觉得文章不错,欢迎点赞分享到朋友圈