基于spring+redis实现动态分布式调度任务的管理方案

前言:简单阐述下这套方案的背景
业务场景:任务中心系统下发任务需要支持周期性发布,这里的任务是动态创建的,那么意味着任务注册的调度job也必须支持动态创建。

设计之初这里有三套方案做参考
1、xxl-job
2、Elastic-Job
3、基于spring的scheduling自行实现调度方案

先说下结果,我们选择了第三种,因为考虑到多节点的调度任务注册,这里通过redis做了一个注册中心的功能,实现了job的发布订阅功能,如图:
基于spring+redis实现动态分布式调度任务的管理方案_第1张图片为什么不考虑方案1、方案2
XXL-JOB
首先xxl-job大体有三种方式实现调度
①通过引用jar包方式使用@XxlJob注解注册调度任务【此方式不支持动态创建,pass】
②搭配xxl-job的JobHandler,自定义handler,通过url包装成任务参数注册调度任务
基于spring+redis实现动态分布式调度任务的管理方案_第2张图片③通过glue脚本注册调度任务

在这里其实可以通过②去实现我们想要的效果,这就要搭一套xxl-job,然后通过系统暴露job的增删改查接口供任务系统去调用
就在这时,我们的架构师提了一个疑问,就是我们的生产环境是微盟体系下的wos自动化部署的,会存在节点扩容,到时候如果调度中心被扩容,能否支持?
然后就xxl-job的节点扩容我晚上回家研究了一下:
1、xxljob的admin和executor支持集群方式,注册ip可以通过域名的方式解决多节点的ip问题
2、但是我在模拟多节点的时候遇到一个问题:
一台调度中心,俩台执行器
admin-A,executor-B1,executor-B2
刚开始启动ok,停调执行器B1,任务不受影响,admin继续与B2建立连接
这个时候把B1重启,然后断掉B2,这个时候admin就报错了,报连接错误,网上找了很多方案都只能通过重启admin才能解决,大体问题出在了XxlJobRemotingUtil建立连接的方式上,具体原因等后续有空再深挖
所以这个问题没解决也没敢去用这套方案

Elastic-Job
es-job其他方式跟xxl-job差不多
有一个不同的是可以通过实现simpleJob接口自定义调度,这就跟注解方式有区别了
但是在后续使用过程中发现,这个也只能基于jvm本身去处理的,
JobClient.getInstance().regJob(“jobname”, jobConfig, new TaskElasticJob(“外部参数”));
如果项目重启的话任务会丢失,需要重写注册上去,这就要通过业务系统自定义功能去支持,而且es-job需要zookeeper的支持,运维成本也增大,故这套方案也pass了

所以最终为了轻巧、方便、可控,我们采用了自行实现分布式调度方案
项目的架构是springboot+dubbo+redis+es
es是为了处理后续任务明细的海量数据用的

重点的实现逻辑是:
1、系统在启动的时候,会通过applicationRunner进行系统预热,将DB里面的任务表信息给注册到TaskScheduler上,然后记录启动的调度线程ScheduledFuture,定义一个map容器:Map>,通过任务id作为key去本地存一个线程容器,后续可以通过任务id获取到调度线程,进行停用启用操作。
【这里只大概讲一下思路,具体的实现逻辑可以去网上搜一下单机模式下spring的schedule动态实现调度的方案,这里我主要是讲如何在分布式环境下如何去优化这套方案】

2、重点:通过redis做一个订阅发布功能
A节点注册job1,然后通过redis的convertAndSend向redis发送一条消息,其他节点通过messageListener订阅到job1的信息数据,也同步完成注册。job的注销也同理

3、任务的执行通过redis加分布式锁,然后通过延迟释放时间控制任务的重复调度,这里更多的是业务细化的实现,增强功能的健壮性。
这套方案可能会暴露的问题也在下图说明了,总之这套方案刚刚落地,后续肯定会有很多需要改进的地方这里就暂时做一个记录
基于spring+redis实现动态分布式调度任务的管理方案_第3张图片
总体下来这套方案还是很轻量的,理论上已经可以支持当前的业务,而且支持镜像复制多节点扩容,不需要再更改任何配置。

你可能感兴趣的:(自我摸索,java,分布式,spring,boot)