声明
阅读本文前, 需要对 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