Skip to content

定时任务模块

本模块基于 XXL-JOB 实现分布式定时任务调度,支持任务的动态管理、故障转移、分片执行等特性。

XXL-JOB 简介

什么是 XXL-JOB

XXL-JOB 是一款轻量级分布式任务调度平台,核心设计目标是开发迅速、学习简单、轻量级、易扩展

核心特性

特性说明
简单易用通过 @XxlJob 注解即可定义任务
动态管理支持动态创建、启停、修改任务
弹性扩容执行器集群化部署,自动注册发现
故障转移执行器故障时自动切换到其他节点
任务分片支持大数据量分片并行处理
失败重试支持配置失败重试次数
任务日志完整的任务执行日志追踪

架构设计

环境搭建

数据库初始化

执行官方 SQL 脚本创建数据库表结构:

bash
# 下载 SQL 脚本
https://gitee.com/xuxueli0323/xxl-job/blob/2.4.0/doc/db/tables_xxl_job.sql

核心表说明

表名说明
xxl_job_info任务配置信息
xxl_job_log任务执行日志
xxl_job_log_report日志统计报表
xxl_job_registry执行器注册信息
xxl_job_group执行器分组
xxl_job_user系统用户
xxl_job_lock分布式锁

Docker 部署调度中心

方式一:Docker Compose(推荐)

项目已内置 Docker Compose 配置:

bash
cd youlai-boot/docker

# 启动所有中间件(MySQL、Redis、MinIO、XXL-JOB)
docker-compose -f ./docker-compose.yml -p youlai-boot up -d

docker-compose.yml 配置

yaml
xxl-job-admin:
  image: xuxueli/xxl-job-admin:2.4.0
  container_name: xxl-job-admin
  restart: unless-stopped
  environment:
    PARAMS: '--spring.datasource.url=jdbc:mysql://mysql:3306/xxl_job?useUnicode=true&characterEncoding=UTF-8&autoReconnect=true&serverTimezone=Asia/Shanghai --spring.datasource.username=root --spring.datasource.password=123456'
  volumes:
    - ./xxljob/logs:/data/applogs
  ports:
    - 8080:8080
  networks:
    - youlai-boot

方式二:Docker 单独部署

bash
docker run -d --name xxl-job-admin \
  -e PARAMS="--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=123456" \
  -p 8080:8080 \
  -v /logs/xxl-job:/data/applogs \
  xuxueli/xxl-job-admin:2.4.0

访问调度中心

项目集成

添加依赖

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

配置文件

yaml
# application.yml
xxl:
  job:
    enabled: false              # 是否启用定时任务(生产环境开启)
    admin:
      addresses: http://127.0.0.1:8080/xxl-job-admin  # 调度中心地址
    accessToken: default_token  # 访问令牌
    executor:
      appname: xxl-job-executor-${spring.application.name}  # 执行器名称
      address:                  # 执行器注册地址(为空自动获取)
      ip:                       # 执行器IP(为空自动获取)
      port: 9999                # 执行器端口
      logpath: /data/applogs/xxl-job/jobhandler  # 日志路径
      logretentiondays: 30      # 日志保留天数

配置类

java
@Configuration
@ConditionalOnProperty(name = "xxl.job.enabled")  // 条件装配
@Slf4j
public class XxlJobConfig {

    @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.port}")
    private int port;

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

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

    @Bean
    public XxlJobSpringExecutor xxlJobExecutor() {
        log.info(">>>>>>>>>>> xxl-job config init.");
        XxlJobSpringExecutor executor = new XxlJobSpringExecutor();
        executor.setAdminAddresses(adminAddresses);
        executor.setAppname(appname);
        executor.setPort(port);
        executor.setAccessToken(accessToken);
        executor.setLogPath(logPath);
        executor.setLogRetentionDays(logRetentionDays);
        return executor;
    }
}

任务开发

Bean 模式(推荐)

开发步骤

  1. 创建任务处理器类,添加 @Component 注解
  2. 定义任务方法,添加 @XxlJob("任务名称") 注解
java
@Component
@Slf4j
public class XxlJobSampleHandler {

    /**
     * 简单任务示例
     */
    @XxlJob("demoJobHandler")
    public void demoJobHandler() {
        log.info("XXL-JOB, Hello World.");
    }

    /**
     * 带返回值的任务
     */
    @XxlJob("returnJobHandler")
    public ReturnT<String> returnJobHandler() {
        // 业务逻辑处理
        doSomething();
        
        return ReturnT.SUCCESS;
    }

    /**
     * 任务参数示例
     */
    @XxlJob("paramJobHandler")
    public void paramJobHandler() {
        // 获取任务参数
        String param = XxlJobHelper.getJobParam();
        log.info("任务参数: {}", param);
        
        // 获取分片参数
        int shardIndex = XxlJobHelper.getShardIndex();  // 当前分片序号
        int shardTotal = XxlJobHelper.getShardTotal();  // 总分片数
    }
}

分片任务

适用于大数据量并行处理场景:

java
@Component
@Slf4j
public class ShardingJobHandler {

    @XxlJob("shardingJobHandler")
    public void shardingJobHandler() {
        // 获取分片参数
        int shardIndex = XxlJobHelper.getShardIndex();  // 当前分片序号(从0开始)
        int shardTotal = XxlJobHelper.getShardTotal();  // 总分片数

        log.info("分片参数: 当前序号={}, 总分片数={}", shardIndex, shardTotal);

        // 业务逻辑:根据分片处理不同数据
        // 例如:处理订单数据,每个分片处理 1/shardTotal 的数据
        List<Order> orders = orderService.getOrdersBySharding(shardIndex, shardTotal);
        
        for (Order order : orders) {
            processOrder(order);
        }
    }
}

SQL 分片查询示例

sql
-- 根据分片参数查询数据
SELECT * FROM orders 
WHERE MOD(id, #{shardTotal}) = #{shardIndex}

子任务

支持任务链式触发:

java
@Component
@Slf4j
public class ChildJobHandler {

    @XxlJob("parentJobHandler")
    public ReturnT<String> parentJobHandler() {
        log.info("父任务执行完成");
        
        // 触发子任务(需要在调度中心配置子任务)
        return new ReturnT<>(200, "子任务ID1,子任务ID2");
    }

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

任务配置

添加执行器

登录调度中心 → 执行器管理新增

配置项说明示例
AppName执行器名称(与配置一致)xxl-job-executor-youlai-boot
名称执行器描述有来后台执行器
注册方式自动注册/手动录入自动注册

添加任务

任务管理新增

配置项说明示例
执行器选择已注册的执行器有来后台执行器
任务描述任务名称订单状态同步
路由策略执行器选择策略FIRST/轮询/分片
Cron调度表达式0 0 2 * * ?
JobHandler任务方法名demoJobHandler
阻塞处理策略并发控制单机串行/并行
任务参数传递给任务的参数{"type": "daily"}

路由策略说明

策略说明适用场景
FIRST第一个执行器单机任务
LAST最后一个执行器单机任务
ROUND轮询负载均衡
RANDOM随机负载均衡
CONSISTENT_HASH一致性哈希有状态任务
LEAST_FREQUENTLY_USED最不经常使用负载均衡
LEAST_RECENTLY_USED最近最久未使用负载均衡
FAILOVER故障转移高可用场景
BUSYOVER忙碌转移高可用场景
SHARDING_BROADCAST分片广播大数据量处理

Cron 表达式示例

表达式说明
0 0 2 * * ?每天凌晨 2 点执行
0 0/5 * * * ?每 5 分钟执行一次
0 0 0 1 * ?每月 1 号 0 点执行
0 0 9-17 * * ?每天 9-17 点整点执行
0 0 12 * * MON-FRI周一到周五中午 12 点执行

运维管理

任务操作

  • 启动:任务状态切换为运行中,按 Cron 表达式自动调度
  • 停止:暂停任务调度
  • 执行一次:手动触发任务执行
  • 查看日志:查看任务执行历史和详细日志

执行日志

java
// 任务中记录日志
@XxlJob("orderSyncJob")
public void orderSyncJob() {
    XxlJobHelper.log("开始同步订单数据");
    
    int count = orderService.syncOrders();
    
    XxlJobHelper.log("同步完成,共同步 {} 条订单", count);
}

告警配置

在任务配置中设置告警邮箱:

  • 任务执行失败时自动发送告警邮件
  • 支持配置多个告警邮箱(逗号分隔)

常见问题

执行器未注册

现象:调度中心看不到执行器注册信息

排查

  1. 检查 xxl.job.enabled 是否为 true
  2. 检查网络连通性:执行器能否访问调度中心
  3. 检查执行器端口 9999 是否被占用
  4. 检查 appname 是否与调度中心配置一致

任务不执行

现象:任务状态为运行中,但不触发执行

排查

  1. 检查 Cron 表达式是否正确
  2. 检查执行器是否有可用实例
  3. 检查任务是否被暂停
  4. 查看调度日志是否有错误信息

任务执行失败

现象:任务触发后返回失败状态

排查

  1. 查看任务执行日志,定位具体异常
  2. 检查 JobHandler 名称是否与代码一致
  3. 检查任务方法是否有异常抛出
  4. 检查任务执行超时设置

时区问题

现象:任务执行时间与预期不符

解决方案

yaml
# 设置 JVM 时区
spring:
  jackson:
    time-zone: Asia/Shanghai

最佳实践

任务幂等性

确保任务可重复执行而不产生副作用:

java
@XxlJob("orderTimeoutJob")
public void orderTimeoutJob() {
    // 查询超时订单(已支付或已取消的不会重复处理)
    List<Order> timeoutOrders = orderService.getTimeoutOrders();
    
    for (Order order : timeoutOrders) {
        // 使用乐观锁确保幂等
        boolean success = orderService.cancelTimeoutOrder(order.getId());
        if (success) {
            log.info("订单 {} 超时取消成功", order.getId());
        }
    }
}

异常处理

java
@XxlJob("dataSyncJob")
public void dataSyncJob() {
    try {
        // 业务逻辑
        doSync();
        XxlJobHelper.handleSuccess("同步成功");
    } catch (Exception e) {
        log.error("同步失败", e);
        XxlJobHelper.handleFail("同步失败: " + e.getMessage());
    }
}

性能优化

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

生产环境配置

yaml
xxl:
  job:
    enabled: true
    admin:
      addresses: ${XXL_JOB_ADMIN:http://xxl-job-admin:8080/xxl-job-admin}
    accessToken: ${XXL_JOB_TOKEN:default_token}
    executor:
      logretentiondays: 7  # 生产环境保留 7 天

相关文件

文件说明
XxlJobConfig.java执行器配置类
XxlJobSampleHandler.java任务处理器示例
docker-compose.ymlDocker Compose 配置
application-*.yml环境配置文件

参考资料

基于 MIT 许可发布