XXL-JOB 定时任务——从 Docker 部署到分片广播,分布式任务调度落地

@Scheduled 单机跑跑还行,多实例部署了就全乱了------哪个实例执行?重复执行怎么办?数据怎么分片并行处理?XXL-JOB 就是专治这些的:调度中心统一管任务,执行器集群自动注册,分片广播把大数据量拆成 N 份给 N 台机器并行跑。这篇文章从 Docker 部署、Spring Boot 3.x 整合,到分片广播、任务参数、失败处理、父子任务链,全部用一个完整流程串下来。

安装

可以去官网下载源码,然后编译部署,这里我只介绍使用 docker 部署

docker 安装

先去 github 官网找到 xxl_job 的 SQL 文件,注意,要去找对应版本的 sql 文件,不要找 master 下的。

在 MySQL 数据库中执行,目前最新版要求 MySQL 要 8.0+ 以上,执行完后会创建一个新的数据库,名为 xxl_job

下载之前可以去 docker hub 搜索 xxl-job-admin 查看最新版

sh 复制代码
docker pull m.daocloud.io/docker.io/xuxueli/xxl-job-admin:3.4.2

docker run -d \
  --name idle-store-xxl-job-admin \
  -p 8095:8080 \
  -e TZ=Asia/Shanghai \
  -e JAVA_OPTS="-Xms256m -Xmx512m" \
  -e PARAMS="--spring.datasource.url=jdbc:mysql://host.docker.internal:3306/xxl_job?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true&useSSL=false \
  --spring.datasource.username=root \
  --spring.datasource.password=Hliu1026. \
  --xxl.job.accessToken=idle_store_xxl_job_2026" \
  -v /Users/ice/Desktop/cola/code/docker/xxl-job/logs:/data/applogs \
  xuxueli/xxl-job-admin:3.4.2

之后访问 http://locahost:8095,可以看到控制台,默认用户名 admin,账号密码 123456

Spring Boot 3.x 整合

配置之前我们先来到控制台,它这里的执行器管理里面有一个执行器默认案例,我们编辑一下这个

改成我们自己想要的名字,选择自动注册就可以。

然后在项目中,在 pom.xml 中引入依赖

xml 复制代码
<dependency>
    <groupId>com.xuxueli</groupId>
    <artifactId>xxl-job-core</artifactId>
    <version>3.4.2</version>
</dependency>

版本和下载的 XXL-JOB 保持一致

之后进行配置,在 application.yaml 配置文件中配置内容

yaml 复制代码
xxl:
  job:
    adminAddresses: http://localhost:8095 # xxl-job 调度中心部署根地址,如调度中心集群部署存在多个地址则用逗号分隔
    accessToken: idle_store_xxl_job_2026 # 和 xxl-job 上创建的 token 保持一致
    appName: xxl-job-executor-idle-store # 和 xxl-job 上创建的执行器名称保持一致
    ip: # 执行器 ip, 为空表示自动获取 ip
    port: 9999 # 执行器端口号
    logPath: ./log/xxl-job/jobhandler # 执行器日志存放路径
    logRetentiondays: 30 # 日志保留时间(单位:天)

然后创建两个配置类,一个读取配置,一个初始化执行器

java 复制代码
@ConfigurationProperties(prefix = XxlJobProperties.PREFIX)
@Component
@Data
public class XxlJobProperties {

    public static final String PREFIX = "xxl.job";

    private String adminAddresses;

    private String accessToken;

    private String appName;

    private String ip;

    private int port;

    private String logPath;

    private int logRetentionDays = 30;
}
java 复制代码
@Configuration
@Slf4j
public class XxlJobConfig {

    @Resource
    private XxlJobProperties xxlJobProperties;

    /**
     * 初始化执行器
     */
    @Bean
    public XxlJobSpringExecutor xxlJobExecutor() {
        log.info(">>>>>>>>>>> xxl-job config init.");
        XxlJobSpringExecutor xxlJobSpringExecutor = new XxlJobSpringExecutor();
        xxlJobSpringExecutor.setAdminAddresses(xxlJobProperties.getAdminAddresses());
        xxlJobSpringExecutor.setAppname(xxlJobProperties.getAppName());
        xxlJobSpringExecutor.setIp(xxlJobProperties.getIp());
        xxlJobSpringExecutor.setPort(xxlJobProperties.getPort());
        xxlJobSpringExecutor.setAccessToken(xxlJobProperties.getAccessToken());
        xxlJobSpringExecutor.setLogPath(xxlJobProperties.getLogPath());
        xxlJobSpringExecutor.setLogRetentionDays(xxlJobProperties.getLogRetentionDays());
        return xxlJobSpringExecutor;
    }

}

配置完后,启动项目

如果成功注册的话,在控制台就能查看到了 ip + 端口

写第一个任务

之后来到任务管理

这里面就可以配置我们执行器要执行的任务了,这里选中后进行编辑

配置完之后,点击保存,回到我们的服务中,创建 /job 包存放定时任务,比如我创建一个 CreateTableXxlJob 类,定义如下

java 复制代码
@Component
public class CreateTableXxlJob {

    @XxlJob("createTableJobHandler")
    public void createTableJobHandler() throws Exception {
        XxlJobHelper.log("# 初始化明日增量数据表...");
    }
}

这里的 createTableJobHandler 对应前面的 JobHandler。通过 XxlJobHelper.log 打印日志,这样在管理后台能看到日志信息

在控制台中我们可以自己运行定时任务

可以选择运行一次,或者启动(根据我们调度类型执行,比如我这里就是5秒执行一次)。

我们选择,启动,然后查询日志

如果你有梯子,可能会查询日志失败,需要改一下配置文件中的ip,改为本机IP,比如我这里

yaml 复制代码
ip: 172.25.64.42

控制台配置任务

调度类型

类型 说明 示例
不自动调度,只能手动触发 临时任务
CRON 按 Cron 表达式周期性触发 0 0 2 * * ?(每天凌晨 2 点)
固定速度 固定间隔,秒为单位 60(每 60 秒一次)

任务配置

运行模式:任务以 JobHandler 方式维护在执行器端;需要结合 "JobHandler" 属性匹配执行器中任务;

JobHandler :对应执行器中开发的任务类方法上 @XxlJob 注解自定义的 value 值;

路由策略

多实例怎么分配任务

策略 行为
FIRST 固定选第一台
ROUND 轮询
RANDOM 随机
FAILOVER 心跳检测,选第一个活着的
SHARDING_BROADCAST(分片广播) 所有机器都执行,传分片参数

调度过期策略

  • 忽略:调度过期后,忽略过期的任务,从当前时间开始重新计算下次触发时间;
  • 立即执行一次:调度过期后,立即执行一次,并从当前时间开始重新计算下次触发时间;

阻塞处理策略

任务堆积怎么办?

调度过于密集执行器来不及处理时的处理策略;

  • 单机串行(默认):调度请求进入单机执行器后,调度请求进入FIFO队列并以串行方式运行;
  • 丢弃后续调度:调度请求进入单机执行器后,发现执行器存在运行的调度任务,本次请求将会被丢弃并标记为失败;
  • 覆盖之前调度:调度请求进入单机执行器后,发现执行器存在运行的调度任务,将会终止运行中的调度任务并清空队列,然后运行本地调度任务;

任务超时时间

支持自定义任务超时时间,任务运行超时将会主动中断任务;

失败重试次数

支持自定义任务失败重试次数,当任务失败时将会按照预设的失败重试次数主动进行重试;

分配广播

分片广播是 XXL-JOB 最值钱的功能。比如凌晨 2 点需要处理全量用户数据------10 万条,部署了 3 台实例,每台各分 3.3 万条并行处理:

java 复制代码
@Component
public class DataAlignXxlJob {

    @XxlJob("dataAlignJobHandler")
    public void dataAlignJobHandler() {
        // 拿到分片参数
        int shardIndex = XxlJobHelper.getShardIndex();   // 当前分片:0, 1, 2
        int shardTotal = XxlJobHelper.getShardTotal();    // 总分片数:3

        XxlJobHelper.log("当前分片: {}/{}", shardIndex, shardTotal);

        // 按分片取数据:每台机器只取自己那一段
        // SELECT * FROM user WHERE id % #{shardTotal} = #{shardIndex}
        List<User> users = userMapper.selectByShard(shardIndex, shardTotal);

        for (User user : users) {
            // 处理逻辑...
        }
        XxlJobHelper.handleSuccess("分片 " + shardIndex + " 处理完成,共 " + users.size() + " 条");
    }
}

XxlJobHelper.getShardIndex()getShardTotal() 拿到分片信息,SQL 里用 WHERE id % shardTotal = shardIndex 做数据分片。3 台机器各处理 1/3,提速 3 倍。

控制台把这个任务的路由策略改成 SHARDING_BROADCAST 即可。

任务参数

控制台传参,动态调整逻辑,控制台 "任务参数" 里填的内容,代码里能拿到

java 复制代码
@XxlJob("dataAlignJobHandler")
public void dataAlignJobHandler() {
    String param = XxlJobHelper.getJobParam();   // "batchSize=500,startId=10000"
    // 解析参数,动态调整
}

适合控制每次处理的批量大小、起止 ID 这些需要临时调整的参数------改控制台不用改代码。

成功/失败标记

java 复制代码
@XxlJob("myJobHandler")
public void myJobHandler() {
    try {
        // 业务逻辑
        XxlJobHelper.handleSuccess("处理完成,共 1000 条");
    } catch (Exception e) {
        XxlJobHelper.handleFail("处理失败: " + e.getMessage());
    }
}

不手动调的话默认算成功。handleSuccesshandleFail 的参数会显示在控制台日志最前面,方便快速看到执行结果。

父子任务------任务链

控制台配置 "子任务ID" 后,当前任务执行成功时自动触发子任务。适合有先后依赖的场景------比如先同步数据 → 重建索引:

复制代码
任务 A:每天凌晨 2 点同步数据(CRON: 0 0 2 * * ?)
    ↓ 执行成功后自动触发
任务 B:重建 Elasticsearch 索引(调度类型:无,只被 A 触发)
相关推荐
Re_Virtual12 小时前
基于web的轻量级http测试工具——Restfox
http·docker·开源·restfox
宠友信息13 小时前
MySQL复合索引与Druid优化仿小红书源码个人主页查询链路
数据库·spring boot·websocket·mysql·uni-app
liulilittle14 小时前
论双机热备:分布式
服务器·网络·分布式·通信·竞态
极客先躯16 小时前
高级java每日一道面试题-2026年04月18日-实战篇[Docker]-如何处理金融行业的时序数据容器化?
java·运维·docker·容器·金融·时序数据·高级面试
hdsoft_huge16 小时前
SpringBoot系列18:SpringSecurity登录鉴权,JWT无状态Token认证完整流程(生产级落地)
java·spring boot·后端
liulilittle17 小时前
论无知:分布式
服务器·网络·分布式·并发·通信·竞态
杨了个杨898218 小时前
docker的网络模式
网络·docker·容器
万亿少女的梦16818 小时前
基于微信小程序、Spring Boot与Vue3的智慧养老管理系统设计与实现
spring boot·redis·微信小程序·mybatis·vue3
Zhu75819 小时前
对docker环境的postgresql数据库做快速初始化
数据库·docker·postgresql