Xxl-Job 快速入门

Xxl-Job 快速入门

  • 一、Xxl-Job 简介
    • 1.1 概念
    • 1.2 历史背景
    • 1.3 特性
    • 1.4 Xxl-Job 的架构图
    • 1.5 Xxl-Job 和 Quartz 的区别
  • 二、快速入门
    • 2.1 启动 xxl-job-admin
    • 2.2 创建一个新的定时任务
    • 2.3 在调度中心新增定时任务
  • 三、操作指南
    • 3.1 新建执行器
    • 3.2 创建任务
    • 3.3 其他概念
      • 3.3.1 路由策略
      • 3.3.2 任务运行模式(BEAN、GLUE)
      • 3.3.3 阻塞处理策略
      • 3.3.4 子任务
      • 3.3.5 任务超时时间
    • 3.4 高级任务用法
      • 3.4.1 分片任务
      • 3.4.2 命令任务
      • 3.4.3 周期性任务
  • 四、总结

一、Xxl-Job 简介

Xxl-Job 快速入门_第1张图片

1.1 概念


Xxl-Job 是一个轻量级分布式任务调度平台

作用Xxl-Job 是一个任务调度框架,通过引入 Xxl-Job 相关的依赖,按照相关格式撰写代码后,可在其可视化界面进行任务的启动、执行、中止以及包含了日志记录与查询和任务状态监控

理解: 如果将 Xxl-Job 形容为一个人的话,每一个引入 Xxl-Job 的微服务就相当于一个独立的人(执行器),而按照相关约定格式撰写的 Handler 为餐桌上的食物,可视化界面则可以决定哪个执行器(人),吃东西或者不吃某个东西(定时任务),在什么时间吃(Corn 表达式控制或者执行或终止);

1.2 历史背景


Xxl-Job 是 2015 年点评员工 许雪里 业余开源制作,2016 点评内部接入 Xxl-Job,据统计,自 2016.01.21 接入至 2017.12.01期间,该系统已调度约 100 万次,表现优异。

经过数十个版本的更新,系统的任务模型、UI 交互模型以及底层调度通讯模型都有了较大的优化和提升,核心功能更加稳定高效。

目前接入登记使用的企业已经有几百家,算上没有登记的企业,几千家至少是有的。接入场景:如电商行业、O2O 业务和大数据作业等。

版本变更:在早期的 Xxl-Job,调度依赖是 Quartz,直到 2019 年 7月发布的 7.27版本才移除 Quartz 依赖。重构代码虽然移除了 Quartz,但是仍然少不了调度器、触发器、任务的设计。

1.3 特性


  1. 简单、动态、轻量级、支持弹性扩容缩容、事件全异步执行、跨语言。
  2. 调度中心和执行任务解耦
  3. 调度任务支持多种不同场景的路由策略、容错策略、触发策略
  4. 运维更加便捷

1.4 Xxl-Job 的架构图


Xxl-Job 快速入门_第2张图片

1.5 Xxl-Job 和 Quartz 的区别


Quartz 的不足: Quartz 作为开源任务调度中的佼佼者,是任务调度的首选。但是在集群环境中,Quartz 采用 API 的方式对任务进行管理,这样存在一下问题:

  • 通过调用 API 的方式操作任务,不人性化。
  • 需要持久化业务的 QuartzJobBean 到底层数据表中,系统侵入性相当严重。
  • 调度逻辑和 QuartzJobBean 耦合在同一个项目中,这将导致一个问题,在调度。
  • 任务数量数量逐渐增多,同时调度任务逻辑逐渐加重的情况下,此时调度系统的性能将大大受限于业务。
Quartz Xxl-Job
依赖 mysql mysql、jdk1.7+、maven3.0+
集群、弹性扩容 多节点部署,通过竞争数据库锁来保证只有一个节点执行任务 使用 Quartz 基于数据库的分布式功能,服务器超出一定数量会给数据库造成一定的压力
任务分片 不支持 支持
管理界面
高级功能 弹性扩容,分片广播,故障转移,Rolling 实时日志,Glue(支持在线编辑代码,免发布),任务进度监控,任务依赖,数据加密,邮件报警,运行报表,国际化
缺点 没有管理界面,以及不支持任务分片等。不适用于分布式场景 调度中心通过获取 DB 锁来保证集群中执行任务的唯一性,如果短任务很多,随着调度中心集群数量增加,那么数据库的锁竞争会比较厉害,性能不好。
任务不能重复执行 使用 Quartz 基于数据库的分布式功能
并行调度 调度系统多线程(默认10个线程)触发调度运行,确保调度精确执行,不被堵塞。
失败处理策略 调度失败时的处理策略,策略包括:失败警告(默认)、失败重试(界面可配置)
动态分片策略 分片广播任务以执行器为维度进行分片,支持动态扩容执行器集群从而动态增加分片数量,协同进行业务处理;在进行大数据量业务操作时可显著提升任务处理能力和速度。执行器集群部署时,任务路由策略选择“分片广播”情况下,一次任务调度将会广播触发对应集群中所有执行器执行一次任务,通过传递分片参数;可根据分片参数开发分片任务;

二、快速入门

从 github 上将 Xxl-Job 项目 clone 下来

GitHub 地址:https://github.com/xuxueli/xxl-job
Gitee 地址:https://gitee.com/xuxueli0323/xxl-job

2.1 启动 xxl-job-admin


  1. 打开项目
    Xxl-Job 快速入门_第3张图片
    项目结构:

    doc: 文档资料,包括“调度数据库”建表脚本
    xxl-job-core: 公共 jar 依赖
    xxl-job-admin:调度中心,项目源码,Spring Boot 项目,可以直接启动
    xxl-job-executor-samples:执行器,sample 示例项目,其中的 Spring Boot 工程,可以直接启动。可以在该项目上进行开发,也可以将现有项目改造生成执行器项目。

  2. 配置好数据源,执行 sql
    Xxl-Job 快速入门_第4张图片
    Xxl-Job 快速入门_第5张图片
  3. 打开 xxl-job-admin 模块,在 application.properties 中进行后台的配置
    Xxl-Job 快速入门_第6张图片
  4. 启动 XxlJobAdminApplication 类,访问 http://localhost:8080/xxl-job-admin 默认账户admin,密码123456
    Xxl-Job 快速入门_第7张图片

2.2 创建一个新的定时任务


定位到 JobHandler 下,新增定时任务

Xxl-Job 快速入门_第8张图片

package com.xxl.job.executor.service.jobhandler;

import com.xxl.job.core.biz.model.ReturnT;
import com.xxl.job.core.handler.annotation.XxlJob;
import org.springframework.stereotype.Component;

import static com.xxl.job.core.biz.model.ReturnT.SUCCESS;

/**
 * @author zoom
 * @desription 编写自己的定时任务handler
 * @date 2023/10/27 11:50
 */
@Component
public class MyJobHandler {

    /**
     * @param param 参数
     * @return 成功信息
     * @desription 简单任务示例【bean模式】
     */
    @XxlJob(value = "myJobHandler", init = "", destroy = "")
    public ReturnT<String> demoJobHandler(String param) throws InterruptedException {
        // 模拟业务执行
        System.out.println("we should fight...");
        // 返回执行结果
        return SUCCESS;
    }
}

2.3 在调度中心新增定时任务


  1. 选中右侧 任务管理 -- 新增
    Xxl-Job 快速入门_第9张图片

  2. 启动 XxlJobExecutorApplication 执行器
    Xxl-Job 快速入门_第10张图片

  3. 在调度中心,启动定时任务
    Xxl-Job 快速入门_第11张图片

  4. 查看结果
    Xxl-Job 快速入门_第12张图片

三、操作指南


自己的项目想要引入 xxl-job,导入以下依赖即可

 <dependency>
     <groupId>com.xuxueligroupId>
     <artifactId>xxl-job-coreartifactId>
     <version>2.0.1version>
 dependency>

3.1 新建执行器


执行器相当于是小组组长,负责任务的具体执行,由它分配线程(组员)执行任务。执行器需要注册到调度中心,这样调度中心才知道怎样选择执行器,或者说做路由。执行器的执行结果,也需要通过回调的方式告诉调度中心

这里选择 Spring Boot 项目用来举例子,从源码中单独拷一个项目出来,如果你是在业务项目里集成的话,也是参考这个 sample,在项目里面加上 xxl-job-core 的依赖,添加配置就可以创建执行器了。

  1. xxl-job-executor-samples 下新建自己的执行器模块,例如:xxl-job-executor-myExec【直接复制 xxl-job-executor-sample-springboot】,修改一下文件名和模块名
    Xxl-Job 快速入门_第13张图片
    注意: 同时修改执行器的名称,否则后面会报命名冲突,修改@XxlJobvalue值即可

  2. 修改 sample-myExec 模块的配置,将其修改为集群模式

    在D盘中,创建 xxl-log 文件夹

    # no web
    #spring.main.web-environment=false
    
    # log config
    logging.config=classpath:logback.xml
    
    
    ### xxl-job admin address list, such as "http://address" or "http://address01,http://address02"
    xxl.job.admin.addresses=http://127.0.0.1:8080/xxl-job-admin
    
    ### xxl-job, access token
    xxl.job.accessToken=default_token
    
    ### xxl-job executor appname
    ### xxl-job executor server-info
    xxl.job.executor.ip=
    ### xxl-job executor log-retention-days
    xxl.job.executor.logretentiondays=30
    
    
    
    
    ### xxl-job executor log-path 日志存放路径
    xxl.job.executor.logpath=D:\\xxl-log
    
    ### 因为我们要模拟执行器集群部署,打包后单击运行多次,为服务设置随机端口
    server.port=${random.int[10000,19999]}
    
    ### 执行器的端口
    xxl.job.executor.port=${random.int[9000,10000]}
    ### 集群部署,这两项配置要一致
    xxl.job.executor.appname=xxl-job-executor-myExec
    xxl.job.executor.address=
    
  3. 修改idea配置

    启动项目,在本地启动多个执行器集群的时候这里要打上勾(或者直接 alt + U ),因为我们配置了随机端口,所以是不会报错的。

    Xxl-Job 快速入门_第14张图片

  4. 启动执行器

    启动两个(XxlJobExecutorApplication)

    Xxl-Job 快速入门_第15张图片

  5. 新增执行器
    Xxl-Job 快速入门_第16张图片

  6. 刷新,查看效果
    Xxl-Job 快速入门_第17张图片
    注册成功

3.2 创建任务


注意点:
新建任务有几个注意点:

  1. 在 Spring Bean 实例中(@Component 注解),开发 Job 方法。方法格式要求为public ReturnT demoJobHandler(String param),返回值和参数格式是固定的,这个是不能动的,唯一能动的是方法名。
  2. 在方法名上打上 @XxlJob 注解,这里面有几个属性,第一个 value 值对应的是调度中心新建任务的 JobHandler 属性的值。另外的 init 对应 JobHandler 初始化方法,destory 对应 JobHandler 销毁方法。这两个方法要在任务类里面创建。
  3. 执行日志:需要通过XxlJobLogger.log打印执行日志,会写到指定的日志文件中。

3.3 其他概念

3.3.1 路由策略


路由策略是指一个任务可以由多个执行器完成,那具体由哪一个完成呢,这就要看我们指定的路由策略了,这个参数当执行器做集群部署的时候才有意义

Quartz 中只能随机负载。那么这里的第一个,最后一个是按什么顺序来的呢,就是点击查看-注册节点的1,2,3,4,第一个指的就是1,最后一个指的就是4。
Xxl-Job 快速入门_第18张图片

3.3.2 任务运行模式(BEAN、GLUE)


运行模式分为两种,一种是 BEAN、一种是 GLUE

  • BEAN 模式:这个是在项目中写 Java 类,然后在 JobHandler 里填上 @XxlJob 里面的名字,是在执行器端编写的
  • GLUE 模式:支持 Java、Shell、Python、PHP、Nodejs、PowerShell,这个时候代码是直接维护在调度中心这边的
  1. 新增一个任务,模式选择为 GLUE
    Xxl-Job 快速入门_第19张图片

  2. 编写任务
    Xxl-Job 快速入门_第20张图片
    Xxl-Job 快速入门_第21张图片

    但是这样会存在一个安全隐患的问题,没有做鉴权。解决方法也很简单,只需要在调度中心和执行器的 application.properties 里加上相同的 token 即可。

    xxx.job.accessToken=
    

3.3.3 阻塞处理策略


阻塞处理策略指的是任务的一次运行还没有结束,下一次调度的时间又到了,比如一个任务执行的时间是三分钟,但是设置的频率是每分钟执行一次,这时候第一次还没执行完,第二次怎么办?

一共有三个选项

策略 参数值 详细含义
单机串行,默认 SERIAL_EXECUTION 调度请求进入单机执行器后,调度请求进入 FIFO 队列并以串行方式运行
丢弃后续调度 DISCARD_LATER 调度请求进入单机执行器后,发现执行器存在运行的调度任务,本次请求将会被丢弃并标记为失败
覆盖之前调度 COVER_EARLY 调度请求进入单机执行器后,发现执行器存在运行的调度任务,将会终止运行中的调度任务并清空队列,然后运行本地调度任务

3.3.4 子任务


当我们要写一个 Job 的时候,任务是相互依赖的。比如下面我要赶这么多事情,A干完了干B,B干完了干C,C干完了干D。
在这里插入图片描述

解决这种问题的时候思路有两种:

  • 第一种是把这么多逻辑写成一个大 job,串行化
  • 第二种就是用子任务,在一个任务末尾触发另一个任务

如果我们需要在本任务执行结束并且执行成功的时候触发另外一个任务,那么就可以把另外的任务作为本任务的子任务执行,因为每个 Job 都有自己的唯一 id,所以只需要在子任务一栏中填上任务 id 即可。

3.3.5 任务超时时间

超时:如果在指定时间内没有返回结果,就不再等待结果

3.4 高级任务用法

3.4.1 分片任务


假设我们有个任务需要处理 20 万条数据,每条数据的业务逻辑处理要 0.1 秒,对于普通任务单线程来讲,需要处理 5.5 个小时才能处理完成。这时候你想要提高速度,你的第一反应就是开多线程跑嘛,我开三个,差不多就是一个多小时就搞定了,你的思路完全正确!

这时候将会是下面这种情况,在执行器 0 上有三个线程在拼命的工作,但是这样大家觉得好不好?不好吧,我执行器 0 在这累的要死,你执行器 1 和执行器 2 在那休息,旱的旱死,涝的涝死,首先这是一个分配不均匀的问题。其次当执行器 0 三个线程都在工作的时候会浪费它的资源,使之这台服务器的性能也会下降,所以这是一种不好的方式。

Xxl-Job 快速入门_第22张图片
这时候就要用到我们的分片任务了,真正好的方案如下,这是一个既科学又合理的方案。三台执行器各自起一个线程来共同把这个任务完成!

Xxl-Job 快速入门_第23张图片

这时候有个问题,三台机器大家都执行同一段代码,那岂不是乱套了,这个数据你也执行一遍,我也执行一遍,它也执行一遍。解决的思路很简单,一台执行器处理总数的1/3,大家把需要干的活平均分了嘛,我干1/3,你干1/3,它干1/3,这样也不会产生冲突。
分片任务在运行的时候,调度器会给每个执行器发送一个不同的分片序号,分片的最大序号跟执行器的总数量是一样的,确保每个执行器都会执行到这个任务,比如上图中第一个执行器拿到分片序号0,第二台执行器拿到分片序号1,第三台执行器拿到分片序号2。那现在就好办了,我们只需要把处理得数据进行模3取余,余数为 0 的数据就由执行器 0 干,余数为 1 的数据就由执行器 1 干,余数为 2 的数据就由执行器 2 干。

我们获取数据的 sql 可以这样写:

// count 分片总数, index 当前分片数
select id,name,pssword from student where status = 0 and mod(id,#{count}) = #{index} order by id desc limit 1000;
@Component
public class ShardingJobHandler {
    private static Logger logger = LoggerFactory.getLogger(ShardingJobHandler.class);

    /**
     * 分片广播任务
     */
    @XxlJob("shardingJobHandler")
    public ReturnT<String> shardingJobHandler(String param) throws Exception {
        // 分片参数,包括总分片数(执行器个数),当前分片序号
        ShardingUtil.ShardingVO shardingVO = ShardingUtil.getShardingVo();
        XxlJobLogger.log("分片参数:当前分片序号 = {}, 总分片数 = {}", shardingVO.getIndex(), shardingVo.getTotal());
        // sql
        String sql = "select id,name,password from student where status = 0 and mod(id,"
                + shardingVO.getTotal() + ")=" + shardingVO.getIndex() + " order by id desc limit 1000;";
        // List list = getStudent(sql);
        // 获取到这台机器需要执行的数据,执行业务逻辑
        return ReturnT.SUCCESS;
    }
}

新建任务的时候选择分片广播,填上对应的 JobHandler 即可
Xxl-Job 快速入门_第24张图片
最后需要说明一下,分片的数据量不一定是完全均等的数据量,上面的取模只是一个举例,一个思路。我们也可以把0、1、2替换成其他条件去从所有数据中获取部分数据,比如分片序号0 = 机器我查2018年的数据,分片序号1 的机器我查2019年的数据,分片序号2的机器我查2020年的数据。具体怎么分全靠我们的业务来选择。

如果增加或者减少了节点,总分片数和最大分片序号会实时发生变化。

3.4.2 命令任务


命令任务比较有用,比如我们需要定时重启数据库(service restart),定时备份数据文件(cp tar rm),定时清理日志(rm)。

命令行的使用也很简单,只需要把执行的命令作为参数传递进来即可

@Component
public class CommandJobHandler {

    @XxlJob("commandJobHandler")
    public ReturnT<String> commandJobHandlerTest() throws IOException{
        // 用于获取动态传进来的参数
        String jobParam = XxlJobHelper.getJobParam();
        Process exec = Runtime.getRuntime().exec(jobParam);
        System.out.println("command run success...");
        return ReturnT.SUCCESS;
    }
}

更新任务
Xxl-Job 快速入门_第25张图片
启动一下任务,每过3秒,计算器就自动弹出来了

Xxl-Job 快速入门_第26张图片

拓展: 执行 bat 文件

@Component
public class CommandJobHandler2 {

    /**
     * 命令行任务
     *
     * @param
     * @return
     */
    @XxlJob(value = "commandJobHandler2")
    public ReturnT<String> commandJobHandlerTest() throws IOException {
        //用于获取动态传入进来的参数
        String jobParam = XxlJobHelper.getJobParam();
        File file = new File(jobParam);

        System.out.println(String.valueOf(file));
        System.out.println(file.getAbsolutePath());
        System.out.println(file.getCanonicalFile());
		// Process exec = Runtime.getRuntime().exec("cmd /k start " + "www.baidu.com");
        Process exec2 = Runtime.getRuntime().exec("cmd /k start " + file.getCanonicalFile());
        System.out.println("command run success...");
        return ReturnT.SUCCESS;
    }
}

Xxl-Job 快速入门_第27张图片

  • 效果
    Xxl-Job 快速入门_第28张图片

命令

cmd命令执行窗口开闭指令

cmd /c dir 是执行完dir命令后关闭命令窗口。

cmd /k dir 是执行完dir命令后不关闭命令窗口。

cmd /c start dir 会打开一个新窗口后执行dir指令,原窗口会关闭。

cmd /k start dir 会打开一个新窗口后执行dir指令,原窗口不会关闭。

3.4.3 周期性任务


周期性任务就是在任务的开始和销毁的时候执行自定义的方法,做一些自己想做的事

新增JobHandler
Xxl-Job 快速入门_第29张图片

注意: 集群情况下,我们执行任务的时候需要填写执行器地址

Xxl-Job 快速入门_第30张图片

执行器地址在执行器管理中查看

Xxl-Job 快速入门_第31张图片

四、总结



Xxl-Job 是一个分布式任务调度平台。有 2 个角色,xxl-job-admin 调度中心和 xxl-job-executor 执行器。调度中心统一管理调度任务,不承担业务逻辑,负责发起调度请求。执行器是我们的业务系统,负责接收调度请求并执行对应的 JobHandler 中的业务逻辑。
特点:

  1. 简单:有界面维护定时任务和触发规则,方便管理;
  2. 动态:支持动态修改任务状态、启动/停止任务,以及终止运行中任务,即使生效;
  3. 调度中心(中心式):调度采用中心式设计,“调度中心”自研调度组件并支持集群部署,可保证调度中心;
  4. 执行器(分布式):任务分布式执行,任务“执行器”支持集群部署,可保证任务执行;
  5. 邮件报警:任务失败时支持邮件报警,支持配置多邮件地址群发报警邮件;
  6. Rolling 实时日志:支持在线查看调度结果,并且支持以 Rolling 方式实时查看执行器输出的完整的执行日志;

你可能感兴趣的:(Java,xxl-job,java,定时任务)