Xxl-job 一文读懂

Posted 在奋斗的大道

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Xxl-job 一文读懂相关的知识,希望对你有一定的参考价值。

目录

1、前言

1.1 运用场景   

1.2 什么是定时任务

1.2.1  Java 实现定时任务三大方式

1.2.2  常见开源分布式任务框架

1.2.3 传通定时任务的不足

·2、Xxl-job 分布式定时任务

2.1 Xxl-job 核心组件

 2.2 Xxl-job 优点

3、Xxl-job 实战

3.1、Xxl-job 安装

3.1.1 下载源码

 3.1.2 数据库初始化

3.1.3 XXL-JOB 数据库说明

3.1.4 XXL-JOB 项目介绍

 3.1.5 XXL-JOB 资源配置文件和程序启动入口

3.2、 XXL-JOB 分布式集群部署结构图

 3.3 创建执行器项目

 3.4 配置执行器任务

 3.5 xxl-job 高级配置之路由策略

 3.6 xxl-job 高级配置之父子任务

 3.7 xxl-job 高级配置之动态参数

  3.8 xxl-job 高级配置之分片任务

3.9xxl-job 高级配置之 日志回调

3.10 xxl-job 高级配置之任务生命周期

 3.11 xxl-job 任务配置之运行模式GLUE【Java】 

 3.12 xxl-job 任务配置之运行模式GLUE【Python】 

  3.13 xxl-job 任务配置之运行模式GLUE【Shell】 

 3.14 xxl-job 任务配置之运行模式GLUE【PHP】 

 3.15 xxl-job 任务配置之运行模式GLUE【Node.js】 

 3.16 xxl-job 任务配置之运行模式GLUE【PowerShell】 


1、前言

1.1 运用场景   

  • 数据备份
  • 订单未支付则自动取消
  • 定时爬取数据
  • 定时推送信息
  • 定时发布文章
  • 定时生成报表

1.2 什么是定时任务

        定时任务是指基于给定的时间点,间隔或者给定执行次数自动的执行程序。任务调度室系统的重要组成部分,而对于实时的系统,任务调度直接影响着系统的实时性。任务调度涉及多线程并发、运行时间规则定制与解析、线程池的维护等多项工作。

1.2.1  Java 实现定时任务三大方式

  • JDK自带 :JDK自带的Timer以及JDK1.5+ 新增的ScheduledExecutorService;
  • Quartz :简单却强大的JAVA作业调度框架
  • Spring Task :可以将它看成一个轻量级的Quartz,而且使用起来比Quartz简单许多;

JDK 自带的定时器实现

  • Timer类
    这个类允许你调度一个java.util.TimerTask任务。主要有以下几个方法:
  1. schedule(TimerTask task, long delay) 延迟 delay 毫秒 执行。
public static void main(String[] args) 
        for (int i = 0; i < 10; ++i) 
            new Timer("timer - " + i).schedule(new TimerTask() 
                @Override
                public void run() 
                    println(Thread.currentThread().getName() + " run ");
                
            , 1000);
        
    
out :
timer - 2 run 
timer - 1 run 
timer - 0 run 
timer - 3 run 
timer - 9 run 
timer - 4 run 
timer - 8 run 
timer - 5 run 
timer - 6 run 
timer - 7 run 
  1. schedule(TimerTask task, Date time) 特定时间执行。
public static void main(String[] args) 
        for (int i = 0; i < 10; ++i) 
            new Timer("timer - " + i).schedule(new TimerTask() 
                @Override
                public void run() 
                    println(Thread.currentThread().getName() + " run ");
                
            , new Date(System.currentTimeMillis() + 2000));
        
    
out:
timer - 0 run 
timer - 7 run 
timer - 6 run 
timer - 8 run 
timer - 3 run 
timer - 5 run 
timer - 2 run 
timer - 1 run 
timer - 4 run 
timer - 9 run 
  1. schedule(TimerTask task, long delay, long period) 延迟 delay 执行并每隔period 执行一次。
public static void main(String[] args) 
        for (int i = 0; i < 10; ++i) 
            new Timer("timer - " + i).schedule(new TimerTask() 
                @Override
                public void run() 
                    println(Thread.currentThread().getName() + " run ");
                
            , 2000 , 3000);
        
    
out:
timer - 0 run 
timer - 5 run 
timer - 4 run 
timer - 8 run 
timer - 3 run 
timer - 2 run 
timer - 1 run 
timer - 7 run 
timer - 9 run 
timer - 6 run 
timer - 3 run 
timer - 7 run 
timer - 5 run 
timer - 4 run 
timer - 8 run 

JDK ScheduledExecutorService 接口实现类

        ScheduledExecutorService 是JAVA 1.5 后新增的定时任务接口,主要有以下几个方法。

- ScheduledFuture<?> schedule(Runnable command,long delay, TimeUnit unit);
- <V> ScheduledFuture<V> schedule(Callable<V> callable,long delay, TimeUnit unit);
- ScheduledFuture<?> scheduleAtFixedRate(Runnable command,long initialDelay,long period,TimeUnitunit);
- ScheduledFuture<?> scheduleWithFixedDelay(Runnable command,long initialDelay,long delay,TimeUnitunit);

        默认实现为ScheduledThreadPoolExecutor 继承了ThreadPoolExecutor 的线程池特性,配合future特性,比Timer更强大。

示例功能代码:

public static void main(String[] args) throws SchedulerException 
        ScheduledThreadPoolExecutor executor = (ScheduledThreadPoolExecutor)Executors.newScheduledThreadPool(10);
        for (int i = 0; i < 10; ++i) 
            executor.schedule(new Runnable() 
                @Override
                public void run() 
                    System.out.println(Thread.currentThread().getName() + " run ");
                
             , 2 , TimeUnit.SECONDS);
        
        executor.shutdown();
    

out:
pool-1-thread-2 run 
pool-1-thread-5 run 
pool-1-thread-4 run 
pool-1-thread-3 run 
pool-1-thread-8 run 
pool-1-thread-5 run 
pool-1-thread-7 run 
pool-1-thread-2 run 
pool-1-thread-1 run 
pool-1-thread-6 run 

温馨提示:Spring Task内部实现也是基于ScheduledExecutorService 接口。

Quartz 定时器 

Quartz是一个完全由Java编写的开源作业调度框架,为在Java应用程序中进行作业调度提供了简单却强大的机制。Quartz允许开发人员根据时间间隔来调度作业。它实现了作业和触发器的多对多的关系,还能把多个作业与不同的触发器关联。可以动态的添加删除定时任务,另外很好的支撑集群调度

示例功能代码:

Maven 依赖Quartz 定时器Jar 包依赖。

        <!-- https://mvnrepository.com/artifact/org.quartz-scheduler/quartz -->
        <dependency>
            <groupId>org.quartz-scheduler</groupId>
            <artifactId>quartz</artifactId>
            <version>2.3.0</version>
        </dependency>

创建Quartz 定时器Job类

public class TestJob implements Job
    @Override
    public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException 
        println(Thread.currentThread().getName() + " test job begin " + DateUtil.getCurrentTimeStr());
    

代码测试

public static void main(String[] args) throws InterruptedException, SchedulerException 

        Scheduler scheduler = new StdSchedulerFactory().getScheduler();
        // 开始
        scheduler.start();
        // job 唯一标识 test.test-1
        JobKey jobKey = new JobKey("test" , "test-1");
        JobDetail jobDetail = JobBuilder.newJob(TestJob.class).withIdentity(jobKey).build();
        Trigger trigger = TriggerBuilder.newTrigger()
                .withIdentity("test" , "test")
                // 延迟一秒执行
                .startAt(new Date(System.currentTimeMillis() + 1000))
                // 每隔一秒执行 并一直重复
        .withSchedule(SimpleScheduleBuilder.simpleSchedule().withIntervalInSeconds(1).repeatForever())
                .build();
        scheduler.scheduleJob(jobDetail , trigger);
        
        Thread.sleep(5000);
        // 删除job
        scheduler.deleteJob(jobKey);
    

out :
DefaultQuartzScheduler_Worker-1test job begin 2017-06-03 14:30:33
DefaultQuartzScheduler_Worker-2test job begin 2017-06-03 14:30:34
DefaultQuartzScheduler_Worker-3test job begin 2017-06-03 14:30:35
DefaultQuartzScheduler_Worker-4test job begin 2017-06-03 14:30:36
DefaultQuartzScheduler_Worker-5test job begin 2017-06-03 14:30:37

Quartz 核心部分

Job:是一个接口,只有一个方法void execute(JobExecutionContext context),开发者实现该接口定义运行任务,JobExecutionContext类提供了调度上下文的各种信息。Job运行时的信息保存在JobDataMap实例中;

JobDetail:Quartz在每次执行Job时,都重新创建一个Job实例,所以它不直接接受一个Job的实例,
相反它接收一个Job实现类,以便运行时通过newInstance()的反射机制实例化Job。因此需要通过一个类来描述Job的实现类及其它相关的静态信息,如Job名字、描述、关联监听器等信息,JobDetail承担了这一角色。

Trigger:是一个类,描述触发Job执行的时间触发规则。主要有SimpleTrigger和CronTrigger这两个子类。
当仅需触发一次或者以固定时间间隔周期执行,SimpleTrigger是最适合的选择;而CronTrigger则可以通过Cron表达式定义出各种复杂时间规则的调度方案:如每早晨9:00执行,周一、周三、周五下午5:00执行等;

Calendar:org.quartz.Calendar和java.util.Calendar不同,它是一些日历特定时间点的集合(可以简单地将org.quartz.Calendar看作java.util.Calendar的集合——java.util.Calendar代表一个日历时间点,无特殊说明后面的Calendar即指org.quartz.Calendar)。
一个Trigger可以和多个Calendar关联,以便排除或包含某些时间点。假设,我们安排每周星期一早上10:00执行任务,但是如果碰到法定的节日,任务则不执行,这时就需要在Trigger触发机制的基础上使用Calendar进行定点排除。

Scheduler:代表一个Quartz的独立运行容器,Trigger和JobDetail可以注册到Scheduler中,两者在Scheduler中拥有各自的组及名称,组及名称是Scheduler查找定位容器中某一对象的依据,
Trigger的组及名称必须唯一,JobDetail的组和名称也必须唯一(但可以和Trigger的组和名称相同,因为它们是不同类型的)。Scheduler定义了多个接口方法,允许外部通过组及名称访问和控制容器中Trigger和JobDetail。Scheduler可以将Trigger绑定到某一JobDetail中,这样当Trigger触发时,对应的Job就被执行。一个Job可以对应多个Trigger,但一个Trigger只能对应一个Job。可以通过SchedulerFactory创建一个Scheduler实例。Scheduler拥有一个SchedulerContext,它类似于ServletContext,保存着Scheduler上下文信息,Job和Trigger都可以访问SchedulerContext内的信息。SchedulerContext内部通过一个Map,以键值对的方式维护这些上下文数据,SchedulerContext为保存和获取数据提供了多个put()和getXxx()的方法。可以通过Scheduler#getContext()获取对应的SchedulerContext实例;

ThreadPool:Scheduler使用一个线程池作为任务运行的基础设施,任务通过共享线程池中的线程提高运行效率。

Spring Task

Spring Task 是 Spring 提供的轻量级定时任务工具,也就意味着不需要再添加第三方依赖了,相比其他第三方类库更加方便易用。

第一步:新建配置类 SpringTaskConfig,并添加 @EnableScheduling注解开启 Spring Task。

@Configuration
@EnableScheduling
public class SpringTaskConfig 

可以不新建这个配置类,直接在主类上添加 @EnableScheduling 注解。

@SpringBootApplication
@EnableScheduling
public class CodingmoreSpringtaskApplication 

	public static void main(String[] args) 
		SpringApplication.run(CodingmoreSpringtaskApplication.class, args);
	

第二步,新建定时任务类 CronTask,使用 @Scheduled 注解注册 Cron 表达式执行定时任务。

@Slf4j
@Component
public class CronTask 
    @Scheduled(cron = "0/1 * * ? * ?")
    public void cron() 
        log.info("定时执行,时间", DateUtil.now());
    

启动服务器端,发现每隔一秒钟会打印一次日志,证明 Spring Task 的 cron 表达式形式已经起效了。

默认情况下,@Scheduled 创建的线程池大小为 1,如果想增加线程池大小的话,可以让 SpringTaskConfig 类实现 SchedulingConfigurer 接口,通过 setPoolSize 增加线程池大小。

@Configuration
@EnableScheduling
public class SpringTaskConfig implements SchedulingConfigurer 
    @Override
    public void configureTasks(ScheduledTaskRegistrar taskRegistrar) 
        ThreadPoolTaskScheduler threadPoolTaskScheduler = new ThreadPoolTaskScheduler();

        threadPoolTaskScheduler.setPoolSize(10);
        threadPoolTaskScheduler.setThreadNamePrefix("my-scheduled-task-pool-");
        threadPoolTaskScheduler.initialize();

        taskRegistrar.setTaskScheduler(threadPoolTaskScheduler);
    

 知识拓展:Spring Task 除了支持 Cron 表达式,还有 fixedRate(固定速率执行)、fixedDelay(固定延迟执行)、initialDelay(初始延迟)三种用法。

/**
 * fixedRate:固定速率执行。每5秒执行一次。
 */
@Scheduled(fixedRate = 5000)
public void reportCurrentTimeWithFixedRate() 
    log.info("Current Thread : ", Thread.currentThread().getName());
    log.info("Fixed Rate Task : The time is now ", DateUtil.now());


/**
 * fixedDelay:固定延迟执行。距离上一次调用成功后2秒才执。
 */
@Scheduled(fixedDelay = 2000)
public void reportCurrentTimeWithFixedDelay() 
    try 
        TimeUnit.SECONDS.sleep(3);
        log.info("Fixed Delay Task : The time is now ",DateUtil.now());
     catch (InterruptedException e) 
        e.printStackTrace();
    


/**
 * initialDelay:初始延迟。任务的第一次执行将延迟5秒,然后将以5秒的固定间隔执行。
 */
@Scheduled(initialDelay = 5000, fixedRate = 5000)
public void reportCurrentTimeWithInitialDelay() 
    log.info("Fixed Rate Task with Initial Delay : The time is now ", DateUtil.now());

1.2.2  常见开源分布式任务框架

  • xxl-job 轻量级分布式任务调度平台
  • Elastic-job 当当开源的分布式调度解决方案
  • Saturn 唯品会开源的分布式任务调度平台
  • light-task-scheduler 阿里员工开源的个人项目
  • Quartz java定时任务的标配

1.2.3 传通定时任务的不足

  • 不支持集群:集群情况下容易造成任务重复问题
  • 不支持失败重试:失败即结束,不支持重试
  • 不支持动态调整:修改任务参数时需要修改代码,并且要重启服务
  • 无报警机制:任务失败后没有提醒功能
  • 无统一管理:没有办法手动关闭或开启任务

·2、Xxl-job 分布式定时任务

Xxl-job是一个开源的,具有丰富的任务管理功能以及高性能,高可用等特点的轻量级分布式任务调度平台,其核心设计目标是开发迅速、学习简单、轻量级、易扩展、开箱即用!!!

 1、将调度行为抽象形成“调度中心”公共平台,而平台自身并不承担业务逻辑,
“调度中心”负责发起调度请求。
2、将任务抽象成分散的JobHandler,交由执行器统一管理,执行器负责接受调度请求
并执行对应的JobHandler中的业务逻辑。
目的: “调度”和“执行任务”可以互相解耦,提供系统整体的稳定性和拓展性。

2.1 Xxl-job 核心组件

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

 2.2 Xxl-job 优点

1、简单:支持通过Web页面对任务进行CRUD操作,操作简单,一分钟上手;

2、动态:支持动态修改任务状态、启动/停止任务,以及终止运行中任务,即时生效;

3、路由策略:执行器集群部署时提供丰富的路由策略,包括:第一个、最后一个、轮询、随机、一致性HASH、最不经常使用、最近最久未使用、故障转移、忙碌转移等;

4、故障转移:任务路由策略选择”故障转移”情况下,如果执行器集群中某一台机器故障,将会自动Failover切换到一台正常的执行器发送调度请求。

5、任务超时控制:支持自定义任务超时时间,任务运行超时将会主动中断任务

6、一致性:“调度中心”通过DB锁保证集群分布式调度的一致性, 一次任务调度只会触发一次执行

7、邮件报警:任务失败时支持邮件报警,支持配置多邮件地址群发报警邮件;

8、任务进度监控:支持实时监控任务进度.
 

3、Xxl-job 实战

3.1、Xxl-job 安装

3.1.1 下载源码

下载项目源码并解压,获取“数据库初始化SQL脚本”并执行即可。
Git地址:https://github.com/xuxueli/xxl-job
码云地址:https://github.com/xuxueli/xxl-job

官方文档地址:https://www.xuxueli.com/xxl-job/

 3.1.2 数据库初始化

将数据库脚本导入数据库。

3.1.3 XXL-JOB 数据库说明

  1. xxl_job_group :执行器信息表,用于维护任务执行器的信息。
  2. xxl_job_info:调度扩展信息表,主要用于保存调度任务的拓展信息,如:任务分组、任务名称、机器地址等。
  3. xxl_job_lock:任务调度锁.
  4. xxl_job_log:日志表,主要用于保存xxl-job 任务调度的历史信息,如:调度结果,
  5. 执行结果,调度参数等。
  6. xxl_job_log_report:日志报表,存储xxl-job 任务调度的日志报表,会在调度中心报表展示。
  7. xxl_job_logglue:任务GLUE 日志,用于保存GLUE日志的更新变化,支持GLUE版本的回溯。
  8. xxl_job_registry:执行器注册表,用于维护在线的执行器与调度的地址信息。
  9. xxl_job_user:系统的用户表。

3.1.4 XXL-JOB 项目介绍

 3.1.5 XXL-JOB 资源配置文件和程序启动入口

application.properties 配置文件详解

### web 服务端口及其项目名称
server.port=8080
server.servlet.context-path=/xxl-job-admin

### actuator 健康检查
management.server.servlet.context-path=/actuator
management.health.mail.enabled=false

### resources 静态资源配置
spring.mvc.servlet.load-on-startup=0
spring.mvc.static-path-pattern=/static/**
spring.resources.static-locations=classpath:/static/

### freemarker 模板配置
spring.freemarker.templateLoaderPath=classpath:/templates/
spring.freemarker.suffix=.ftl
spring.freemarker.charset=UTF-8
spring.freemarker.request-context-attribute=request
spring.freemarker.settings.number_format=0.##########

### mybatis ORM框架配置
mybatis.mapper-locations=classpath:/mybatis-mapper/*Mapper.xml
#mybatis.type-aliases-package=com.xxl.job.admin.core.model

### xxl-job, datasource 数据库连接账户信息
spring.datasource.url=jdbc:mysql://127.0.0.1:3306/xxl_job?useUnicode=true&characterEncoding=UTF-8&autoReconnect=true&serverTimezone=Asia/Shanghai
spring.datasource.username=root
spring.datasource.password=root_pwd
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver

### datasource-pool  数据库连接池配置
spring.datasource.type=com.zaxxer.hikari.HikariDataSource
spring.datasource.hikari.minimum-idle=10
spring.datasource.hikari.maximum-pool-size=30
spring.datasource.hikari.auto-commit=true
spring.datasource.hikari.idle-timeout=30000
spring.datasource.hikari.pool-name=HikariCP
spring.datasource.hikari.max-lifetime=900000
spring.datasource.hikari.connection-timeout=10000
spring.datasource.hikari.connection-test-query=SELECT 1
spring.datasource.hikari.validation-timeout=1000

### xxl-job, email  报警邮件服务器配置
spring.mail.host=smtp.qq.com
spring.mail.port=25
spring.mail.username=xxx@qq.com
spring.mail.from=xxx@qq.com
spring.mail.password=xxx
spring.mail.properties.mail.smtp.auth=true
spring.mail.properties.mail.smtp.starttls.enable=true
spring.mail.properties.mail.smtp.starttls.required=true
spring.mail.properties.mail.smtp.socketFactory.class=javax.net.ssl.SSLSocketFactory

### xxl-job, access token  默认连接Token 信息
xxl.job.accessToken=default_token

### xxl-job, i18n (default is zh_CN, and you can choose "zh_CN", "zh_TC" and "en")  
xxl.job.i18n=zh_CN

## xxl-job, triggerpool max size 触发器最大值设置
xxl.job.triggerpool.fast.max=200
xxl.job.triggerpool.slow.max=100

### xxl-job, log retention days 日志保存天数
xxl.job.logretentiondays=30

 程序启动入口:

com.xxl.job.admin.XxlJobAdminApplication

修改完数据库连接,启动项目程序入口。

项目访问地址:http://localhost:8080/xxl-job-admin/

默认账户/密码:admin/123456 

xxl-job 调度中心后台相关页面截图

3.2、 XXL-JOB 分布式集群部署结构图

 3.3 创建执行器项目

新建一个SpringBoot项目,项目的名称为:xxl-job-actuator-boot 。

第一:pom.xml 添加相关Jar依赖。

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.6.7</version>
    </parent>

    <groupId>org.example</groupId>
    <artifactId>xxl-job-actuator-boot</artifactId>
    <version>1.0-SNAPSHOT</version>

    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
    </properties>

    <dependencies>
        <!-- spring-boot-starter-web (spring-webmvc + tomcat) -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>

        <!-- xxl-job-core -->
        <dependency>
            <groupId>com.xuxueli</groupId>
            <artifactId>xxl-job-core</artifactId>
            <version>2.3.1</version>
        </dependency>

    </dependencies>


</project>

 第二步:resource 文件夹添加application.properties 配置文件和logback.xml 日志文件。

application.properties

# web port 执行器项目端口
server.port=8081
# 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.appname=xxl-job-actuator-sample
### xxl-job executor registry-address: default use address to registry , otherwise use ip:port if address is null 默认使用动态获取
xxl.job.executor.address=
### xxl-job executor server-info 默认使用动态获取
xxl.job.executor.ip=
### 调度中心与执行器内部通信端口
xxl.job.executor.port=9999
### xxl-job executor log-path
xxl.job.executor.logpath=/data/applogs/xxl-job/jobhandler
### xxl-job executor log-retention-days
xxl.job.executor.logretentiondays=30
<?xml version="1.0" encoding="UTF-8"?>
<configuration debug="false" scan="true" scanPeriod="1 seconds">

    <contextName>logback</contextName>
    <property name="log.path" value="/data/applogs/xxl-job/xxl-job-actuator-boot.log"/>

    <appender name="console" class="ch.qos.logback.core.ConsoleAppender">
        <encoder>
            <pattern>%dHH:mm:ss.SSS %contextName [%thread] %-5level %logger36 - %msg%n</pattern>
        </encoder>
    </appender>

    <appender name="file" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <file>$log.path</file>
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <fileNamePattern>$log.path.%dyyyy-MM-dd.zip</fileNamePattern>
        </rollingPolicy>
        <encoder>
            <pattern>%date %level [%thread] %logger36 [%file : %line] %msg%n
            </pattern>
        </encoder>
    </appender>

    <root level="info">
        <appender-ref ref="console"/>
        <appender-ref ref="file"/>
    </root>

</configuration>

第三步:新增Xxl-job 执行器配置文件和Xxl-job 执行器项目程序入口。

package com.xxl.job.actuator.core.config;

import com.xxl.job.core.executor.impl.XxlJobSpringExecutor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class XxlJobConfig 
    private Logger logger = LoggerFactory.getLogger(XxlJobConfig.class);

    @Value("$xxl.job.admin.addresses")
    private String adminAddresses;

    @Value("$xxl.job.accessToken")
    private String accessToken;

    @Value("$xxl.job.executor.appname")
    private String appname;

    @Value("$xxl.job.executor.address")
    private String address;

    @Value("$xxl.job.executor.ip")
    private String ip;

    @Value("$xxl.job.executor.port")
    private int port;

    @Value("$xxl.job.executor.logpath")
    private String logPath;

    @Value("$xxl.job.executor.logretentiondays")
    private int logRetentionDays;


    @Bean
    public XxlJobSpringExecutor xxlJobExecutor() 
        logger.info(">>>>>>>>>>> xxl-job config init.");
        XxlJobSpringExecutor xxlJobSpringExecutor = new XxlJobSpringExecutor();
        xxlJobSpringExecutor.setAdminAddresses(adminAddresses);
        xxlJobSpringExecutor.setAppname(appname);
        xxlJobSpringExecutor.setAddress(address);
        xxlJobSpringExecutor.setIp(ip);
        xxlJobSpringExecutor.setPort(port);
        xxlJobSpringExecutor.setAccessToken(accessToken);
        xxlJobSpringExecutor.setLogPath(logPath);
        xxlJobSpringExecutor.setLogRetentionDays(logRetentionDays);

        return xxlJobSpringExecutor;
    

    /**
     * 针对多网卡、容器内部署等情况,可借助 "spring-cloud-commons" 提供的 "InetUtils" 组件灵活定制注册IP;
     *
     *      1、引入依赖:
     *          <dependency>
     *             <groupId>org.springframework.cloud</groupId>
     *             <artifactId>spring-cloud-commons</artifactId>
     *             <version>$version</version>
     *         </dependency>
     *
     *      2、配置文件,或者容器启动变量
     *          spring.cloud.inetutils.preferred-networks: 'xxx.xxx.xxx.'
     *
     *      3、获取IP
     *          String ip_ = inetUtils.findFirstNonLoopbackHostInfo().getIpAddress();
     */

package com.xxl.job.actuator;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class XxlJobActuatorApplication 
    public static void main(String[] args) 
        SpringApplication.run(XxlJobActuatorApplication.class, args);
    

第四步:启动执行器项目,并向调度中心注册此执行器,执行器名称为:xxl-job-actuator-sample

打开Xxl-job 调度中心管理端。选择执行器管理/新增

 3.4 配置执行器任务

 在执行器项目:xxl-job-actuator-boot, 添加定时任务DemoJob

package com.xxl.job.actuator.job;

import com.xxl.job.core.context.XxlJobHelper;
import com.xxl.job.core.handler.annotation.XxlJob;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

import java.util.concurrent.TimeUnit;

@Component
public class DemoJob 
    private static Logger logger = LoggerFactory.getLogger(DemoJob.class);

    /**
     * 1、简单任务示例
     */
    @XxlJob("demoJobHandler")
    public void demoJobHandler() throws Exception 
        XxlJobHelper.log("XXL-JOB, 你好.");

        for (int i = 0; i < 5; i++) 
            XxlJobHelper.log("beat at:" + i);
            TimeUnit.SECONDS.sleep(2);
        
        // default success
    


 打开Xxl-job 调度中心管理端。选择任务管理/新增

 

 

点击操作按钮,选择启动,等待一段时间后,查看定时任务执行产生的相关日志信息。

 3.5 xxl-job 高级配置之路由策略

新建任务,在高级配置中路由策略包含如下截图模式:

 向DemoJob 定时任务,添加如下验证路由策略功能代码片段。

  /**
     * 路由模式之第一个
     */
    @XxlJob("firstTask")
    public ReturnT firstTask()
        XxlJobHelper.log("路由策略模式之:第一个");
        return ReturnT.SUCCESS;
    

    /**
     * 路由模式之最后一个
     */
    @XxlJob("lastTask")
    public ReturnT lastTask()
        XxlJobHelper.log("路由策略模式之:最后一个");
        return ReturnT.SUCCESS;
    

    /**
     * 路由模式之轮训
     */
    @XxlJob("pollTask")
    public ReturnT pollTask()
        XxlJobHelper.log("路由策略模式之:轮训");
        return ReturnT.SUCCESS;
    

    /**
     * 路由模式之Hash
     */
    @XxlJob("hashTask")
    public ReturnT hashTask()
        XxlJobHelper.log("路由策略模式之:Hash");
        return ReturnT.SUCCESS;
    

    /**
     * 路由模式之随机
     */
    @XxlJob("randomTask")
    public ReturnT randomTask()
        XxlJobHelper.log("路由策略模式之:随机");
        return ReturnT.SUCCESS;
    

 为实现路由策略模式的代码测试,我们需要把xxl-job-actuator-boot执行器项目启动多个实例。多个实例分配如下:

项目名称项目端口内部通信端口
XxlJobActuatorApplication80819999
XxlJobActuatorApplication-808280829998
XxlJobActuatorApplication-808280839997

多个项目实例操作截图:

 

 

验证:同一个执行器是否包含多个服务实例。

高级配置中路由策略功能验证:

 3.6 xxl-job 高级配置之父子任务

父子任务,是指父任务执行后自动调用子任务,完成一种类似于链式的调用。在使用是仅仅需要配置子任务的ID即可。

 新建任务,在高级配置中子任务ID包含如下截图模式:

  向DemoJob 定时任务,添加如下验证父子任务功能代码片段。

/**
     * 父子任务之父任务
     */
    @XxlJob("fatherTask")
    public ReturnT fatherTask()
        logger.info("父任务执行");
        return ReturnT.SUCCESS;
    

    /**
     * 父子任务之子任务
     */
    @XxlJob("childTask")
    public ReturnT childTask()
        logger.info("子任务执行");
        return ReturnT.SUCCESS;
    

  打开Xxl-job 调度中心管理端。选择任务管理/新增父子任务。

 

温馨提示:子任务Id 为xxl-job 为每个任务生成的唯一Id

 3.7 xxl-job 高级配置之动态参数

        xxl-job 支持动态的接受参数进行任务调度,调度器可以传指定参数给具体的任务,
具体任务接受调度参数后,进行相应的业务逻辑处理。

向DemoJob 定时任务,添加如下验证动态参数任务功能代码片段。

 /**
     * 动态参数任务
     */
    @XxlJob("parameterTask")
    public ReturnT parameterTask()
        logger.info("动态参数任务执行");
        // 通过XxlJobHelper.getJobParam() 获取参数
        String param = XxlJobHelper.getJobParam();
        logger.info("参数值为:" + param);
        return ReturnT.SUCCESS;
    

  打开Xxl-job 调度中心管理端。选择任务管理/新增动态参数任务。

执行操作,选择仅执行一次,在任务参数中传入当前日期"2022-10-23"

执行器控制台打印相关结果参数:

 JobThread-10-1666540416032,10,main]
23:55:59.254 logback [xxl-job, EmbedServer bizThreadPool-1562433863] INFO  c.x.job.core.executor.XxlJobExecutor - >>>>>>>>>>> xxl-job regist JobThread success, jobId:10, handler:com.xxl.job.core.handler.impl.MethodJobHandler@118102ee[class com.xxl.job.actuator.job.DemoJob#parameterTask]
23:55:59.255 logback [xxl-job, JobThread-10-1666540559254] INFO  com.xxl.job.actuator.job.DemoJob - 动态参数任务执行
23:55:59.256 logback [xxl-job, JobThread-10-1666540559254] INFO  com.xxl.job.actuator.job.DemoJob - 参数值为:2022-10-23

  3.8 xxl-job 高级配置之分片任务

        分片任务是指会对所有的执行器广播这个任务,所有的执行器都会接受调用请求,
每个执行器可以根据总分片数及当前执行器的缩影进行相关业务处理。

向DemoJob 定时任务,添加如下验证分片任务功能代码片段。

  /**
     * 分片任务
     */
    @XxlJob("shardTask")
    public ReturnT shardTask()
        logger.info("分片任务执行");
        // 获取分片信息

        //总分片数量
        int shardTotal =XxlJobHelper.getShardTotal();
        // 当前分片的索引
        int shardIndex = XxlJobHelper.getShardIndex();

        // 总数据量
        int total = 10 *10000;
        // 分片平均数据量
        int size = total / shardTotal

        // 每个 分片的起始值
        int startIndex = shardIndex * size +1;
        int endIndex = (shardIndex  + 1) *size;

        // 处理最后一个分片
        if(shardIndex == (shardTotal -1))
            endIndex = total;
        

        logger.info("总分片数:, 当前分片索引为:, 处理数据范围为:~", shardTotal, shardIndex, startIndex, endIndex);
        return ReturnT.SUCCESS;
    

 打开Xxl-job 调度中心管理端。选择任务管理/新增分片任务。

选择新增分片任务,高级配置/路由策略,选择分片广播。

 执行器控制台打印相关结果参数:

00:20:56.301 logback [xxl-job, EmbedServer bizThreadPool-1472487530] INFO  c.x.job.core.executor.XxlJobExecutor - >>>>>>>>>>> xxl-job regist JobThread success, jobId:11, handler:com.xxl.job.core.handler.impl.MethodJobHandler@56681eaf[class com.xxl.job.actuator.job.DemoJob#shardTask]
00:20:56.311 logback [xxl-job, JobThread-11-1666542056301] INFO  com.xxl.job.actuator.job.DemoJob - 分片任务执行
00:20:56.311 logback [xxl-job, JobThread-11-1666542056301] INFO  com.xxl.job.actuator.job.DemoJob - 总分片数:1, 当前分片索引为:0, 处理数据范围为:1~100000

3.9xxl-job 高级配置之 日志回调

        日志回调是指执行器在执行任务时可以将执行日志传递给调度中心,即使任务没有执行完成,调度中心也可以看到回调的调度日志内容,便于开发者能够更细化的分析任务的执行情况。

向DemoJob 定时任务,添加如下验证任务日志回调功能代码片段。

/**
     * 任务日志回调
     */
    @XxlJob(value = "logCallBackTask")
    public ReturnT logCallBackTask() throws InterruptedException 
        logger.info("任务日志回调执行");
        XxlJobHelper.log("当前日志执行至行", 143);
        Thread.sleep(3000);
        XxlJobHelper.log("当前日志执行至行", 145);
        Thread.sleep(3000);
        XxlJobHelper.log("当前日志执行至行", 147);
        return ReturnT.SUCCESS;
    

  打开Xxl-job 调度中心管理端。选择任务管理/新增任务日志回调任务。

根据任务ID,将页面切换至调度日志,点击查询执行日志信息。

 

3.10 xxl-job 高级配置之任务生命周期

        xxl-job 支持在任务调用时,第一次调用时先执行指定方法,然后在执行具体的任务,
当执行器停止时会执行指定方法,这就是xxl-job 任务的生命周期。

向DemoJob 定时任务,添加如下验证任务生命周期功能代码片段。

 /**
     * 任务生命周期方法
     */
    @XxlJob(value = "lifeCycleTask", init = "init", destroy = "destroy")
    public ReturnT lifeCycleTask()
        logger.info("任务生命周期执行");
        return ReturnT.SUCCESS;
    

    public void init()
        logger.info("任务初始化方法");
    

    public void destroy() 
        logger.info("任务销毁方法");
    

 打开Xxl-job 调度中心管理端。选择任务管理/新增任务生命周期任务。

 执行器控制台打印相关结果参数:

 

 00:30:30.055 logback [xxl-job, JobThread-12-1666542630055] INFO  com.xxl.job.actuator.job.DemoJob - 任务初始化方法
00:30:30.063 logback [xxl-job, JobThread-12-1666542630055] INFO  com.xxl.job.actuator.job.DemoJob - 任务生命周期执行
00:32:03.274 logback [xxl-job, JobThread-12-1666542630055] INFO  com.xxl.job.actuator.job.DemoJob - 任务销毁方法

 3.11 xxl-job 任务配置之运行模式GLUE【Java】 

        任务以源码方式维护在调度中心,支持通过Web IDE在线更新,实时编译和生效,因此不需要指定JobHandler。

打开Xxl-job 调度中心管理端。选择任务管理/新增GLUE【Java】任务。

开发任务代码:

选中指定任务,点击该任务右侧“GLUE”按钮,将会前往GLUE任务的Web IDE界面,在该界面支持对任务代码进行开发(也可以在IDE中开发完成后,复制粘贴到编辑中)。

版本回溯功能(支持30个版本的版本回溯):在GLUE任务的Web IDE界面,选择右上角下拉框“版本回溯”,会列出该GLUE的更新历史,选择相应版本即可显示该版本代码,保存后GLUE代码即回退到对应的历史版本;

查看任务调度中心,查看GLUE【Java】日志记录

 3.12 xxl-job 任务配置之运行模式GLUE【Python】 

温馨提示:请先确认调度中心服务器是否安装Python环境,我本地调度中心服务器安装的是Python3.

新建的任务进行参数配置,运行模式选中 “GLUE模式(Python)”; 

 

开发任务Python代码:

选中指定任务,点击该任务右侧“GLUE”按钮,将会前往GLUE任务的Web IDE界面,在该界面支持对任务代码进行开发(也可以在IDE中开发完成后,复制粘贴到编辑中)

#!/usr/bin/python3
import time
import io
import sys

sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding='utf-8') 

print ("xxl-job: hello python")

print ("脚本位置:", sys.argv[0])
print ("任务参数:", sys.argv[1])

print ("Good bye!")

​

查看任务调度中心,查看GLUE【Python】日志记录 

  3.13 xxl-job 任务配置之运行模式GLUE【Shell】 

 新建的任务进行参数配置,运行模式选中 “GLUE模式(Shell)”;

 

开发任务Shell代码:

选中指定任务,点击该任务右侧“GLUE”按钮,将会前往GLUE任务的Web IDE界面,在该界面支持对任务代码进行开发(也可以在IDE中开发完成后,复制粘贴到编辑中)

#!/bin/bash
echo "xxl-job: hello shell"

echo "脚本位置:$0"
echo "任务参数:$1"
echo "分片序号 = $2"
echo "分片总数 = $3"

echo "Good bye!"
exit 0

 查看任务调度中心,查看GLUE【Shell】日志记录 

产生上述错误的原因:调度中心服务器是Windows 系统,Bash 命令是Linux 系统,解决此问题的办法:第一种方法:将调度中心的系统切换为Linux 系统, 第二种方法:Windows系统中安装Cygwin.

 3.14 xxl-job 任务配置之运行模式GLUE【php】 

由于本机缺失环境,此章节不做讲解

 3.15 xxl-job 任务配置之运行模式GLUE【Node.js】 

由于本机缺失环境,此章节不做讲解

 3.16 xxl-job 任务配置之运行模式GLUE【PowerShell】 

由于本机缺失环境,此章节不做讲解

以上是关于Xxl-job 一文读懂的主要内容,如果未能解决你的问题,请参考以下文章

一文读懂Linux任务间调度原理和整个执行过程

任务调度框架 Quartz 一文读懂

任务调度框架 Quartz 一文读懂

Java 并发编程一文读懂线程协程守护线程

一文读懂:开源大数据调度系统Taier1.2版本新增的「工作流」到底是什么?

一文带你读懂Python中的进程