简介
再看一遍 xxl-job 架构图:
调度中心主要提供了两个功能: 系统管理 和 任务调度。其余的都是一些辅助功能。
- 系统管理正如图中所示的那样, 包括任务管理、执行器管理、日志管理。还提供了管理界面。
- 任务调度就是负责从数据中心拉取任务,并按照执行时间将任务投递给执行器。
调度器的组成结构
两个核心线程
当调度中心启动后,会启动以下两个线程:
- schedulerThread
scheudlerThread
主要做如下两件事情:
- 从数据中心(db),也就是
xxl_job_info
表中扫描出符合 条件 1 的任务, 条件1 限制如下:- 任务执行时间 小于(当前时间 + 5 s)
- 限制扫描个数, 这个值是动态的,会根据后面的提到的 快慢线程池 中线程数量有关系。
count = treadpool-size * trigger-qps (each trigger cost 50ms, qps = 1000/50 = 20) treadpool-size = (getTriggerPoolFastMax() + getTriggerPoolSlowMax()) * 20 // 看完快慢线程池的介绍,再回过头来看这里,会更容易理解
- 扫描出来的任务被划分为以下 3 类:
- ringThread
ringThread
的作用就是不断从 容器 中读取 当前时间点需要执行 的任务, 读取出来的任务会交给一个叫 快慢线程池 的东西去将任务传递给调度器去执行。
时间轮
上述的 ringThread
和 容器 共同组成了一个时间轮。
简单来讲,时间轮实现了 延迟执行 的功能,它在 xxl-job 中的作用就是让 还未到达执行时间 的任务,按照预计的时间通过 快慢线程池 一个一个送到 执行器 中去执行。
-
时间轮的数据结构一般是 数组 + 链表, 和 jdk1.7 中的 HashMap 是一个道理,链表中的每个节点就是一个待执行的任务。
-
xxl-job 中的时间轮可以形象描述为以下这张图,像一个只有秒针的钟表一样。
-
ringThread 线程运行过程中,每秒会扫过一个刻度,假设当前刻度位置存在 job 链表,就把链表中的所有 job 取出来,最后丢给 快慢线程池。
-
当然 xxl-job 为了避免处理耗时太长,会跨过刻度,多向前校验一个刻度;也就是当指针指到 2s 时,会把 1s 和 2s 位置的任务同时读取出来。
快慢线程池
上面提到任务从数据中心扫描出来后,随之就会被丢到快慢线程池中,快慢线程池的定义如下:
fastTriggerPool = new ThreadPoolExecutor( 10, XxlJobAdminConfig.getAdminConfig().getTriggerPoolFastMax(), 60L, TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>(1000), new ThreadFactory() { @Override public Thread newThread(Runnable r) { return new Thread(r, "xxl-job, admin JobTriggerPoolHelper-fastTriggerPool-" + r.hashCode()); } }); slowTriggerPool = new ThreadPoolExecutor( 10, XxlJobAdminConfig.getAdminConfig().getTriggerPoolSlowMax(), 60L, TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>(2000), new ThreadFactory() { @Override public Thread newThread(Runnable r) { return new Thread(r, "xxl-job, admin JobTriggerPoolHelper-slowTriggerPool-" + r.hashCode()); } });
由上可知, 快慢线程池包含了两个线程池 fast 和 slow,当一个 job 提交到快慢线程池后,快慢线程池会根据一些条件, 选择其中一个线程池去执行后续的操作。
快慢线程池的作用如下:
实现线程池隔离:调度线程池进行隔离拆分,慢任务自动降级进入”Slow”线程池,避免耗尽调度线程,提高系统稳定性;
什么是慢任务?
如果一个任务在 1 分钟内,它的执行超时次数超过 10 次,就被归为 慢任务
当具体的快或者慢线程池接收到调度任务时,会通过 RPC 远程调用去触发 执行器 完成任务的执行逻辑。
源码入口
com.xxl.job.admin.core.thread.JobScheduleHelper#start com.xxl.job.admin.core.thread.JobTriggerPoolHelper#addTrigger com.xxl.job.core.biz.client.ExecutorBizClient#run
总结
本文基于 xxl-job v2.x 的源码分析了 xxl-job 调度器的组成结构 以及 调度中心是如何触发任务的。
调度器主要包含了以下模块:
- schedulerThread: 负责从数据中心扫描需要执行的任务
- ringThread: 负责精准地控制预计需要执行的任务
- 快慢线程池:通过包装两个线程池,去分别执行 快任务 和 慢任务 的调度过程。