开源仓库:youlai-boot

前言

本系列文章基于 youlai-boot 开源项目实践,一个开箱即用的 Spring Boot 后台管理系统。

定时任务这东西,小项目用 Spring 的 @Scheduled 注解确实够用。但项目一上规模,问题就来了:

  • 多实例重复执行:部署了3个实例,任务跑了3遍
  • 无法动态调整:改个 Cron 表达式要重新发版
  • 失败无重试:任务执行失败只能手动触发
  • 缺乏监控:任务执行情况两眼一抹黑

XXL-JOB 这些问题都解决了,而且学习成本很低。youlai-boot 直接集成好了,开箱即用。

XXL-JOB 是什么

轻量级分布式任务调度平台,核心特性:

特性说明
简单一个 @XxlJob 注解定义任务
动态管理Web 控制台管理任务,无需重启
分布式多执行器集群,自动注册发现
故障转移执行器挂了自动切换其他节点
分片广播大数据量分片并行处理
失败重试配置重试次数,自动重试

架构上分两部分:调度中心(xxl-job-admin)负责管理和触发,执行器(你的应用)负责实际执行。

环境搭建

数据库初始化

XXL-JOB 需要几张表存任务配置和日志,官方脚本:

https://gitee.com/xuxueli0323/xxl-job/blob/2.4.0/doc/db/tables_xxl_job.sql

核心表:

  • xxl_job_info:任务配置
  • xxl_job_log:执行日志
  • xxl_job_registry:执行器注册

Docker 部署调度中心

youlai-boot 的 docker-compose 已经配好:

cd youlai-boot/docker
docker-compose -p youlai-boot up -d

或者单独部署:

docker run -d --name xxl-job-admin \
  -e PARAMS="--spring.datasource.url=jdbc:mysql://127.0.0.1:3306/xxl_job?..." \
  -p 8080:8080 \
  xuxueli/xxl-job-admin:2.4.0

访问 http://localhost:8080/xxl-job-admin,默认 admin/123456。

项目集成

添加依赖

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

配置文件

xxl:
  job:
    enabled: false  # 开发环境关闭,生产开启
    admin:
      addresses: http://127.0.0.1:8080/xxl-job-admin
    accessToken: default_token
    executor:
      appname: xxl-job-executor-youlai-boot
      port: 9999
      logpath: /data/applogs/xxl-job
      logretentiondays: 30

enabled 配置很实用,开发环境不用启动 XXL-JOB,避免依赖问题。

youlai-boot 已经封装好 XxlJobConfig,用 @ConditionalOnProperty 条件装配,只有配置了 enabled=true 才初始化执行器。

任务开发:一个注解的事

最简示例

@Component
public class SampleJob {

    @XxlJob("demoJobHandler")
    public void demoJobHandler() {
        log.info("任务执行了");
    }
}

然后在调度中心添加任务,JobHandler 填 demoJobHandler,配置 Cron 表达式,启动。

任务参数

@XxlJob("paramJob")
public void paramJob() {
    String param = XxlJobHelper.getJobParam();
    log.info("任务参数: {}", param);
}

参数在调度中心配置,支持 JSON 格式。

分片任务:大数据量并行处理

@XxlJob("shardingJob")
public void shardingJob() {
    int index = XxlJobHelper.getShardIndex();  // 当前分片序号
    int total = XxlJobHelper.getShardTotal();  // 总分片数
    
    // 每个执行器处理 1/total 的数据
    List<Order> orders = orderMapper.selectList(
        new LambdaQueryWrapper<Order>()
            .apply("MOD(id, {0}) = {1}", total, index)
    );
    // 处理订单...
}

调度中心配置路由策略为「分片广播」,一次调度会触发所有执行器,每个拿到不同的分片参数。

调度中心配置要点

添加执行器

执行器管理 → 新增

AppName 要和配置文件的 xxl.job.executor.appname 一致,注册方式选「自动注册」。

添加任务

任务管理 → 新增

关键配置:

配置项说明
JobHandler代码里 @XxlJob 的值
Cron调度表达式
路由策略单机选 FIRST,分片选 SHARDING_BROADCAST
阻塞策略单机串行/并行
失败重试重试次数

路由策略说明

策略适用场景
FIRST单机任务
ROUND负载均衡
FAILOVER高可用,故障自动切换
SHARDING_BROADCAST分片并行处理

常见问题排查

执行器未注册

检查清单:

  1. xxl.job.enabled 是否为 true
  2. 执行器能否访问调度中心(网络问题)
  3. 端口 9999 是否被占用
  4. AppName 是否与调度中心配置一致

任务不执行

检查:

  1. Cron 表达式是否正确
  2. 任务是否启动
  3. 执行器是否有在线实例
  4. 查看调度日志

执行失败

查看任务执行日志,通常是业务代码抛异常。JobHandler 名称要和代码一致。

时区问题

设置 JVM 时区:

spring:
  jackson:
    time-zone: Asia/Shanghai

从 @Scheduled 迁移的步骤

  1. 梳理现有任务:列出所有 @Scheduled 方法
  2. 添加依赖和配置:引入 xxl-job-core
  3. 改造任务方法:把 @Scheduled 改成 @XxlJob
  4. 调度中心配置:添加执行器和任务
  5. 测试验证:先在测试环境跑通
  6. 灰度上线:先上一台实例观察

迁移工作量不大,主要是配置和测试。

最佳实践

幂等性设计

任务可能重复执行(调度中心重启等),业务逻辑要保证幂等。用乐观锁或状态判断。

@XxlJob("timeoutJob")
public void timeoutJob() {
    // 只查待处理状态的数据,已处理的不会重复
    List<Order> orders = orderMapper.selectList(
        new LambdaQueryWrapper<Order>()
            .eq(Order::getStatus, "PENDING")
            .lt(Order::getExpireTime, new Date())
    );
}

异常处理

@XxlJob("syncJob")
public void syncJob() {
    try {
        doSync();
        XxlJobHelper.handleSuccess("同步成功");
    } catch (Exception e) {
        log.error("同步失败", e);
        XxlJobHelper.handleFail("同步失败: " + e.getMessage());
    }
}

性能优化

  • 大数据量用分片
  • 避免循环单条处理数据库
  • 耗时操作异步化

结语

从 @Scheduled 迁移到 XXL-JOB,前期投入一点时间,后期维护轻松很多。任务管理可视化、失败重试、分片执行、故障转移,这些能力生产环境真的需要。

youlai-boot 已经集成好了,配置一下就能用。如果你的项目还在用 @Scheduled,建议早点换。

Logo

汇聚全球AI编程工具,助力开发者即刻编程。

更多推荐