xxl-job 源码这么容易懂,那就随手画图分析下 xxl-job 执行器的原理
声明
阅读本文前, 需要对 xxl-job 的使用有所了解。
正文
本文内容基于 xxl-job v2.2.0 源码。
一、调度中心和执行器
下图是一张 xxl-job v2.x 的架构图, 图中的两个核心模块分别是调度中心、执行器.
-
调度中心
简单来讲就是一个管理系统, 用户通过管理界面可以创建任务、编辑任务、手动触发任务以及查看任务执行日志, 另外内部会不停地把需要执行的任务从 任务表 中扫描出来, 去一个个触发.
-
执行器
当任务被触发时, 不管是定时触发还是手动触发, 调度中心都会向执行器发送 http 请求, 由执行器负责具体任务的执行.
xxl-job 源码目录
二、创建任务
从图中可以看出:
- 任务执行参数包含了诸多内容, 比如任务阻塞处理策略,执行策略等等。
- 运行模式选择 Bean,这也是本文讲解的重点。
- 任务执行前,还需要指定一个具体的 JobHandler 去执行。
- 如果是周期性的任务,它在执行的过程中,我们是可以随时调整执行参数的。比如 JobHandler(这点很重要!!!)。
三、定义任务
@Component
public class SampleXxlJob {
/**
* 1、简单任务示例(Bean模式)
*/
@XxlJob("demoJobHandler")
public ReturnT<String> demoJobHandler(String param) throws Exception {
// doSomething
return ReturnT.SUCCESS;
}
}
上面通过 Spring 注解和 Xxl-Job 注解定义了一个任务, 当执行器启动时, 会扫描到这个 SampleXxlJob中的 demoJobHandler方法, 并将该方法封装成一个JobHandler ( 具体实现为MethodJobHandler ) 对象(该 Handler 中的 execute 方法为任务的具体执行逻辑),并把该 JobHandler 注册到容器1中。容器1 的定义如下:
// key 为 XxlJob 注解中的 bean 名称
// value 为 JobHandler
ConcurrentMap<String, IJobHandler> jobHandlerRepository = new ConcurrentMap();
四、任务执行流程
当一个任务被触发(不管是手动触发还是自动触发)时:
- 调度中心 会将任务执行参数封装到 TriggerParam 通过 POST 请求传给 执行器.
- 如果该任务是第一次执行, 会 new 一个新线程并启动。 JobThread 线程启动后,它会被注册到容器2 中。容器2 定义如下,最后会把 TriggerParam 推送到 线程中维护的一个队列。
这个新线程的名字为 JobThread, 该线程和 JobHandler 是一一绑定的。我们可以通过 JobThread 获取到此 JobHandler。
// key 为任务 id, jobId
// value 为 JobThread 对象
private static ConcurrentMap<Integer, JobThread> jobThreadRepository = new ConcurrentHashMap<Integer, JobThread>();
- JobThread 线程启动后,run 方法会不断从队列中读取任务执行参数。
- 读取到执行参数后, 会把执行参数交给 JobHandler#execute方法去执行任务。
JobThread 中维护了一个队列, 任务每执行一次,就把执行参数推到该队列中,JobThread 启动后就一直从队列中读取执行参数,直到任务停止。
如果该任务第二次执行,会通过 jobId 从容器 jobThreadRepository 中获取之前缓存的 JobThread。
- 如果 JobThread 不为空,则取出 JobThread 中的 JobHandler。
- 根据前面提到的,因为任务每次执行时, JobHandler 是可以更换的。所以这里会判断此次任务执行的 JobHandler 是不是跟上一次的一样。如果不一样就要用新的替换掉旧的。并把缓存的 JobThread 注销掉。重新 new 一个新的 JobThread。
- 如果 JobHandler 没有发生改变,就复用。
五、源码入口
com.xxl.job.core.biz.impl.ExecutorBizImpl#run
com.xxl.job.core.thread.JobThread#run



