xxl-job集群原理

参考官网:https://gitee.com/xuxueli0323/xxl-job/blob/master/doc/XXL-JOB%E5%AE%98%E6%96%B9%E6%96%87%E6%A1%A3.md#%E6%AD%A5%E9%AA%A4%E4%BA%8C%E9%83%A8%E7%BD%B2%E9%A1%B9%E7%9B%AE

一、xxl-job集群需要满足的条件

调度中心支持集群部署,提升调度系统容灾和可用性;
调度中心集群部署时,几点要求和建议:
1)DB配置保持一致;
2)集群机器时钟保持一致(单机集群忽视);
官方建议:推荐通过nginx为调度中心集群做负载均衡,分配域名。调度中心访问、执行器回调配置、调用API服务等操作均通过该域名进行。
当然也可以在客户端地址配置多个调度中心地址也可以

admin:
  addresses: http://10.2.61.27:8080/xxl-job-admin/,http://10.2.61.28:8080/xxl-job-admin/,http://10.2.61.29:8080/xxl-job-admin/

二、系统组件

1、调度模块(调度中心)

负责管理调度信息,按照调度配置发出调度请求,自身不承担业务代码。调度系统与任务解耦,提高了系统可用性和稳定性,同时调度系统性能不再受限于任务模块;
支持可视化、简单且动态的管理调度信息,包括任务新建,更新,删除,GLUE开发和任务报警等,所有上述操作都会实时生效,同时支持监控调度结果以及执行日志,支持执行器Failover。

2、执行模块(执行器)

负责接收调度请求并执行任务逻辑。任务模块专注于任务的执行等操作,开发和维护更加简单和高效;
接收“调度中心”的执行请求、终止请求和日志请求等。

三、设计思想

将调度行为抽象形成“调度中心”公共平台,而平台自身并不承担业务逻辑,“调度中心”负责发起调度请求。
将任务抽象成分散的JobHandler,交由“执行器”统一管理,“执行器”负责接收调度请求并执行对应的JobHandler中业务逻辑。
因此,“调度”和“任务”两部分可以相互解耦,提高系统整体稳定性和扩展性;
xxl-job集群原理_第1张图片

四、工作原理

xxl-job集群原理_第2张图片
1、任务执行器根据配置的调度中心的地址,自动注册到调度中心
2、达到任务触发条件,调度中心下发任务
3、执行器基于线程池执行任务,并把执行结果放入内存队列中、把执行日志写入日志文件中
4、执行器的回调线程消费内存队列中的执行结果,主动上报给调度中心
5、当用户在调度中心查看任务日志,调度中心请求任务执行器,任务执行器读取任务日志文件并返回日志详情

五、流程

启动任务调度流程

首先需要在任务调度页面创建调度任务并启动调度任务
1、类XxlJobAdminConfig实现Spring的InitializingBean接口,创始化当前bean的时候执行afterPropertiesSet方法,
2、实例化XxlJobScheduler执行init方法
3、调用JobScheduleHelper中的start方法创建线程并启动
4、JobScheduleHelper线程每执行一次休眠5秒再执行

任务执行流程

1、JobScheduleHelper线程首先尝试获取mysql表xxl_job_lock的悲观锁
2、获取到悲观锁后从xxl_job_info表中查询启动状态并且下一次执行时间小于当前时间+5的任务列表
3、遍历需要执行的任务,刷新下次执行时间,并将这次的执行放入到时间论中,
4、时间论进行遍历并调用触发器执行
后来流程太复杂不写了、自己看源码去,为什么写这一段下面会讲到

六、什么是时间论

2.1.0版本前核心调度模块都是基于quartz框架,2.1.0版本开始自研调度组件,移除quartz依赖 ,使用时间轮调度;
时间轮数据结构:Map>> key是秒数(1-60) ,value是任务id列表
定时任务ringThread:时间轮实现到点触发任务
xxl-job集群原理_第3张图片
时间轮作用:
从时间轮内移出当前秒数前2个秒数(避免处理耗时太长,跨过刻度,向前校验一个刻度)的任务列表id,触发任务;

七、调用集群情况下那个节点调用执行器

在多个调用中心的情况下,使用mysql的悲观锁来避免重复调用;
setAutoCommit(false)关闭隐式自动提交事务,启动事务
select lock for update(显式排他锁,其他事务无法进入&无法实现for update)
读db任务信息 -> 拉任务到内存时间轮 -> 更新db任务信息
commit提交事务,同时会释放for update的排他锁(悲观锁)

    public class JobScheduleHelper {
    	public void start(){

        // schedule thread
        scheduleThread = new Thread(new Runnable() {
        ……
         while (!scheduleThreadToStop) {

                    // Scan Job
                    long start = System.currentTimeMillis();

                    Connection conn = null;
                    Boolean connAutoCommit = null;
                    PreparedStatement preparedStatement = null;

                    boolean preReadSuc = true;
                    try {

                        conn = XxlJobAdminConfig.getAdminConfig().getDataSource().getConnection();
                        connAutoCommit = conn.getAutoCommit();
                        conn.setAutoCommit(false);

                        preparedStatement = conn.prepareStatement(  "select * from xxl_job_lock where lock_name = 'schedule_lock' for update" );
                        preparedStatement.execute();

八、如何实现任务分片、并行执行

index:当前分片序号(从0开始),执行器集群列表中当前执行器的序号;
total:总分片数,执行器集群的总机器数量;

拉出任务的执行机器列表,逐个设置index / total,把index / total分发到任务执行器
任务执行器可根据index / total参数开发分片任务

九、任务注册, 任务自动发现

AppName: 每个执行器机器集群的唯一标示, 任务注册以 “执行器” 为最小粒度进行注册; 每个任务通过其绑定的执行器可感知对应的执行器机器列表;
注册表: 见"xxl_job_registry"表, “执行器” 在进行任务注册时将会周期性维护一条注册记录,即机器地址和AppName的绑定关系; “调度中心” 从而可以动态感知每个AppName在线的机器列表;
执行器注册: 任务注册Beat周期默认30s; 执行器以一倍Beat进行执行器注册, 调度中心以一倍Beat进行动态任务发现; 注册信息的失效时间为三倍Beat;
执行器注册摘除:执行器销毁时,将会主动上报调度中心并摘除对应的执行器机器信息,提高心跳注册的实时性;

为保证系统”轻量级”并且降低学习部署成本,没有采用Zookeeper作为注册中心,采用DB方式进行任务注册发现;

你可能感兴趣的:(任务调度,xxl-job)