定时任务模块
本模块基于 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 -ddocker-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访问调度中心
- 访问地址:http://localhost:8080/xxl-job-admin
- 默认账号:admin
- 默认密码:123456
项目集成
添加依赖
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 模式(推荐)
开发步骤:
- 创建任务处理器类,添加
@Component注解 - 定义任务方法,添加
@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);
}告警配置
在任务配置中设置告警邮箱:
- 任务执行失败时自动发送告警邮件
- 支持配置多个告警邮箱(逗号分隔)
常见问题
执行器未注册
现象:调度中心看不到执行器注册信息
排查:
- 检查
xxl.job.enabled是否为true - 检查网络连通性:执行器能否访问调度中心
- 检查执行器端口
9999是否被占用 - 检查
appname是否与调度中心配置一致
任务不执行
现象:任务状态为运行中,但不触发执行
排查:
- 检查 Cron 表达式是否正确
- 检查执行器是否有可用实例
- 检查任务是否被暂停
- 查看调度日志是否有错误信息
任务执行失败
现象:任务触发后返回失败状态
排查:
- 查看任务执行日志,定位具体异常
- 检查
JobHandler名称是否与代码一致 - 检查任务方法是否有异常抛出
- 检查任务执行超时设置
时区问题
现象:任务执行时间与预期不符
解决方案:
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.yml | Docker Compose 配置 |
application-*.yml | 环境配置文件 |
