概要
借助xxl-job现有的能力,在此基础上,实现分布式延迟任务调度的能力。
整体架构流程
技术细节
1、为了不影响xxl-job原有的功能,重新定义了注解@XxlDelay:
客户端项目启动的时候,将Bean中标记了@XxlDelay注解的方法收集起来,封装成IJobHandler,并且以value定义的名称作为key缓存起来,方便服务端调度的时候查找。看一下代码:
2、服务端这边新增一个延迟任务信息表,用来存储延迟任务:
3、 服务端提供三个操作延迟任务的api,给客户端调用:
分别是:保存延迟任务,取消延迟任务和延迟任务调度成功之后,结果回写的api。
4、客户端这边提供一个专门操作延迟任务的工具类XxlJobDelayHelper:
5、服务端这边启一个线程定时拉取5分钟以内需要执行的任务,放到时间轮中,所有的逻辑都在JobDelayHelper中,最主要的逻辑如下:
查询到任务之后,判断一下调度时间,如果马上要进行调度,或者已经过了调度时间,就马上执行一次。出现这种情况,一般是服务器重启了。其他正常情况,就放入时间轮中等待调度。时间轮是用的netty中的。
具体的调度逻辑在doTrigger方法中:
随机找到一个客户端节点,封装好调度参数,请求这个节点执行。xxl-job原本的路由策略是很丰富的,但考虑到延迟任务是一次性执行的,所以,就用了一个随机的策略。
6、客户端这边需要提供一个调度的api给服务端调用,客户端提供的api都在EmbedServer中:
直接丢到一个队列中,通过一个线程池慢慢去消费。消费完之后,将执行结果反馈给服务端。
7、结果补偿处理。这里主要是考虑到,如果客户端下线,到了调度时间的任务,服务端找不到一个执行节点来执行,那么任务的状态就会一直是调度中。所以,这里做个规定,超过10分钟,状态还是“调度中”的任务,需要重新复位一下,让它立马再调度一次,保证这个任务至少执行一次。主要处理逻辑是在JobCompleteHelper中:
8、测试:
小结
1、可以满足任意延迟时间的任务调度,调度时间差在1秒以内;
2、魔改源码地址:xxl-job-plus: 通过改造xxl-job,实现分布式延迟任务的功能。调度时间精确,误差不超过1秒。
3、强烈推荐一下xxl-job这个项目,里面的编程思维很受启发。