XXL-Job 是国内最流行的分布式任务调度平台——替代 cron / ScheduledExecutorService 的核心组件。本篇把 Docker 化部署、MySQL 初始化、调度中心 / 执行器接入、告警通知整理清楚。
阅读对象:Java 后端 / 运维工程师
覆盖范围:XXL-Job 调度中心部署 + MySQL 初始化表结构 + 执行器注册 + 任务路由策略(轮询 / 故障转移 / 分片广播)+ 告警邮件 + docker-compose 完整集成
一、为什么需要分布式调度
传统定时任务的痛点:
- 单机 cron:服务器挂了任务就没了
- ScheduledExecutorService:分布式下会重复执行(3 台机器都跑)
- 手动控制:凌晨 3 点被叫起来手动跑批
XXL-Job 的解决方案:
| 能力 | 说明 |
|---|
| 可视化管理 | Web 端配置任务、查看日志、触发重跑 |
| 分布式执行 | 任务在多台执行器中只跑一次(通过路由策略) |
| 任务分片 | 1 个任务广播到 10 台机器并行处理(10x 提速) |
| 失败重试 | 自动重试 N 次,指数退避 |
| 告警通知 | 任务失败邮件 / 钉钉 / 飞书通知 |
| GLUE 模式 | Web 端直接写 Java / Shell / Python 脚本 |
| 任务依赖 | 父任务完成触发子任务(DAG) |
When to use XXL-Job:
- 替代 crontab(跨服务器去重 + 可视化)
- 跑批任务(日终结算、数据归档)
- 第三方接口同步(每 5 分钟拉一次数据)
- 定时清理(30 天前的日志)
- 订单超时关闭(每分钟扫描一次)
二、架构原理
1
2
3
4
5
6
7
8
9
10
11
12
13
| ┌──────────────────┐ ┌──────────────────┐
│ 调度中心 (Admin) │◄──────────────►│ 执行器 (Executor)│
│ - 任务配置 │ 注册 │ - 业务服务 │
│ - 触发调度 │ │ - 接收任务 │
│ - 日志收集 │ │ - 执行回调 │
│ - 告警通知 │ │ │
└────────┬─────────┘ └──────────────────┘
│
▼
┌──────────┐
│ MySQL │
│ (元数据) │
└──────────┘
|
- 调度中心(Admin):独立部署,Web 管理界面
- 执行器(Executor):嵌入到业务服务里,通过 jar 包依赖
- MySQL:存任务配置 / 执行日志 / 调度锁
核心协议:执行器启动时主动注册到调度中心;调度中心触发后通过 RPC 调执行器。
三、MySQL 初始化
3.1 下载 SQL 脚本
1
2
| # XXL-Job 2.4.0 表结构
wget https://github.com/xuxueli/xxl-job/blob/2.4.0/doc/db/tables_xxl_job.sql
|
完整表结构:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
| CREATE DATABASE IF NOT EXISTS `xxl_job` DEFAULT CHARSET utf8mb4;
USE `xxl_job`;
SET NAMES utf8mb4;
CREATE TABLE `xxl_job_info` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`job_group` int(11) NOT NULL COMMENT '执行器主键ID',
`job_desc` varchar(255) NOT NULL,
`add_time` datetime DEFAULT NULL,
`update_time` datetime DEFAULT NULL,
`author` varchar(64) DEFAULT NULL,
`alarm_email` varchar(255) DEFAULT NULL,
`schedule_type` varchar(50) NOT NULL DEFAULT 'NONE' COMMENT '调度类型',
`schedule_conf` varchar(128) DEFAULT NULL COMMENT '调度配置',
`misfire_strategy` varchar(50) NOT NULL DEFAULT 'DO_NOTHING',
`executor_route_strategy` varchar(50) DEFAULT NULL,
`executor_handler` varchar(255) DEFAULT NULL,
`executor_param` varchar(512) DEFAULT NULL,
`executor_block_strategy` varchar(50) DEFAULT NULL,
`executor_timeout` int(11) NOT NULL DEFAULT '0',
`executor_fail_retry_count` int(11) NOT NULL DEFAULT '0',
`glue_type` varchar(50) NOT NULL COMMENT 'GLUE类型',
`glue_source` mediumtext COMMENT 'GLUE源代码',
`glue_remark` varchar(128) DEFAULT NULL,
`glue_updatetime` datetime DEFAULT NULL,
`child_jobid` varchar(255) DEFAULT NULL,
`trigger_status` tinyint(4) NOT NULL DEFAULT '0',
`trigger_last_time` bigint(13) NOT NULL DEFAULT '0',
`trigger_next_time` bigint(13) NOT NULL DEFAULT '0',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
-- 还有:xxl_job_log, xxl_job_log_report, xxl_job_logglue, xxl_job_registry,
-- xxl_job_group, xxl_job_user, xxl_job_lock
|
8 张表:
| 表名 | 作用 |
|---|
xxl_job_info | 任务配置 |
xxl_job_log | 任务执行日志 |
xxl_job_log_report | 日志报表 |
xxl_job_logglue | GLUE 源码历史 |
xxl_job_registry | 执行器注册表 |
xxl_job_group | 执行器分组 |
xxl_job_user | 调度中心用户 |
xxl_job_lock | 分布式锁 |
3.2 初始化用户
执行完 SQL 后,默认账号:admin / 123456(生产必改!)
四、调度中心部署
4.1 拉取镜像
1
| docker pull xuxueli/xxl-job-admin:2.4.0
|
4.2 启动容器
1
2
3
4
5
6
7
8
9
10
11
12
13
| docker run -d --restart=always --name xxl-job-admin \
--net=host \
-v /home/docker/xxl-job/data:/data \
-e PARAMS=" \
--spring.datasource.url=jdbc:mysql://10.0.1.1:3306/xxl_job?useUnicode=true&characterEncoding=UTF-8&autoReconnect=true&serverTimezone=Asia/Shanghai \
--spring.datasource.username=root \
--spring.datasource.password={{DB_PASS}}" \
-e JAVA_OPTS="-Xms2g -Xmx2g -Xmn1g -Xss1m \
-XX:SurvivorRatio=8 \
-XX:MetaspaceSize=256m \
-XX:MaxMetaspaceSize=256m \
-XX:NativeMemoryTracking=detail" \
xuxueli/xxl-job-admin:2.4.0
|
关键参数:
--net=host:管理端口 8080 直接暴露PARAMS:MySQL 连接信息JAVA_OPTS:JVM 调优- 数据卷
/data 持久化日志
4.3 访问调度中心
1
2
| http://<IP>:8080/xxl-job-admin
默认账号:admin / 123456
|
4.4 docker-compose 完整版
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
| version: "3"
services:
mysql:
image: mysql:8.0
container_name: xxl-job-mysql
restart: always
environment:
MYSQL_ROOT_PASSWORD: {{DB_ROOT_PASS}}
volumes:
- /home/xxl-job/mysql:/var/lib/mysql
command: --default-authentication-plugin=mysql_native_password
xxl-job-admin:
image: xuxueli/xxl-job-admin:2.4.0
container_name: xxl-job-admin
restart: always
depends_on:
- mysql
ports:
- "8080:8080"
volumes:
- /home/xxl-job/logs:/data/applogs
environment:
PARAMS: >
--spring.datasource.url=jdbc:mysql://mysql:3306/xxl_job?useUnicode=true&characterEncoding=UTF-8
--spring.datasource.username=root
--spring.datasource.password={{DB_ROOT_PASS}}
JAVA_OPTS: "-Xms1g -Xmx1g"
|
首次启动:
- 启动 MySQL 容器
- 手动导入
tables_xxl_job.sql - 启动 xxl-job-admin
五、执行器接入(业务服务)
5.1 添加依赖
1
2
3
4
5
| <dependency>
<groupId>com.xuxueli</groupId>
<artifactId>xxl-job-core</artifactId>
<version>2.4.0</version>
</dependency>
|
5.2 配置 application.yml
1
2
3
4
5
6
7
8
9
10
11
12
| xxl:
job:
admin:
addresses: http://xxl-job-admin.internal.example.com:8080/xxl-job-admin
executor:
appname: my-service
address:
ip:
port: 9999
logpath: /app/logs/xxl-job/jobhandler
logretentiondays: 30
accessToken: {{XXL_JOB_TOKEN}}
|
5.3 写任务 Handler
1
2
3
4
5
6
7
8
9
10
11
| @Component
@JobHandler(value = "demoJobHandler")
public class DemoJobHandler extends IJobHandler {
@Override
public ReturnT<String> execute(String param) throws Exception {
// 业务逻辑
log.info("XXL-JOB Hello, param: {}", param);
return ReturnT.SUCCESS;
}
}
|
或者注解方式:
1
2
3
4
5
| @XxlJob("myJobHandler")
public ReturnT<String> myJobHandler() throws Exception {
// 业务逻辑
return ReturnT.SUCCESS;
}
|
5.4 启动服务
业务服务启动时自动注册到调度中心,30 秒内在 Admin 后台 → 执行器管理 看到。
六、任务配置
6.1 创建执行器
Admin → 执行器管理 → 新增:
1
2
3
4
| AppName:my-service
名称:我的服务
注册方式:自动注册
机器地址:(自动填入)
|
6.2 创建任务
Admin → 任务管理 → 新增:
1
2
3
4
5
6
7
8
9
10
| 执行器:my-service
任务描述:每日订单结算
调度类型:CRON
Cron:0 0 2 * * ? # 每天凌晨 2 点
运行模式:BEAN
JobHandler:demoJobHandler
路由策略:第一个
阻塞处理策略:单机串行
失败重试:3 次
告警邮件:ops@example.com
|
6.3 路由策略详解
| 策略 | 行为 | 适用 |
|---|
| 第一个 | 固定选第一台 | 任务不能并行 |
| 轮询 | 1→2→3→1→2→3 | 负载均衡 |
| 随机 | 随机选一台 | 简单场景 |
| 一致性 HASH | 同参数同机器 | 分片场景 |
| 最不经常使用 | 选使用最少的 | 性能均衡 |
| 故障转移 | 失败后切下一台 | 高可用 |
| 忙碌转移 | 跳过忙碌选空闲 | 实时任务 |
| 分片广播 | 所有机器都跑 | 并行处理 |
6.4 分片广播实战
场景:10 万条订单待结算,1 台机器跑要 1 小时。
配置:
代码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
| @XxlJob("settleOrdersJobHandler")
public ReturnT<String> settleOrdersJobHandler() throws Exception {
// 分片参数
int shardIndex = XxlJobHelper.getShardIndex(); // 当前分片(0 ~ shardTotal-1)
int shardTotal = XxlJobHelper.getShardTotal(); // 总分片数
// 假设有 10 万条待结算订单
int total = 100000;
int pageSize = total / shardTotal + 1;
int from = shardIndex * pageSize;
int to = Math.min(from + pageSize, total);
// 处理 [from, to) 范围的订单
List<Order> orders = orderDao.findPending(from, to);
for (Order order : orders) {
settle(order);
}
return ReturnT.SUCCESS;
}
|
效果:
- 5 台执行器 → 每台处理 2 万条 → 5x 提速
七、GLUE 模式
Web 端直接写代码——不用改业务服务:
7.1 GLUE Java
任务管理 → 选任务 → GLUE IDE → 写 Java:
1
2
3
4
5
6
7
8
| public class GlueJobHandler extends IJobHandler {
@Override
public ReturnT<String> execute(String param) throws Exception {
// 直接写业务逻辑
// 注意:只能用 JDK 内置类,第三方类用不了
return ReturnT.SUCCESS;
}
}
|
适用:临时脚本(数据修复、临时跑批)。
7.2 GLUE Shell
1
2
3
| #!/bin/bash
echo "xxl-job: hello shell"
echo "参数:$1"
|
7.3 GLUE Python
1
2
3
4
5
6
| #!/usr/bin/env python
# -*- coding: UTF-8 -*-
import os
import sys
print("xxl-job: hello python")
print("参数:", sys.argv[1])
|
7.4 GLUE PHP / Node.js
类似,不展开。
八、告警通知
8.1 邮件告警
任务配置:
Admin application.yml:
1
2
3
4
5
6
7
8
9
10
11
| spring:
mail:
host: smtp.example.com
port: 465
username: noreply@example.com
password: {{SMTP_PASS}}
xxl:
job:
triggerpool:
fast:
max: 200
|
8.2 钉钉告警
自定义告警实现:
1
2
3
4
5
6
7
8
9
10
11
12
| @Component
public class DingTalkJobAlarm implements JobAlarm {
@Override
public boolean doAlarm(XxlJobInfo info, XxlJobLog log) {
// 调钉钉 Webhook
String text = String.format("任务 [%s] 执行失败\n日志:%s",
info.getJobDesc(),
log.getHandleMsg());
DingTalk.send("{{DINGTALK_TOKEN}}", text);
return true;
}
}
|
注册:
1
2
3
4
5
6
7
8
9
10
11
| @Configuration
public class XxlJobConfig {
@Bean
public XxlJobSpringExecutor xxlJobExecutor() {
XxlJobSpringExecutor executor = new XxlJobSpringExecutor();
executor.setAdminAddresses("http://xxl-job-admin:8080/xxl-job-admin");
executor.setAppname("my-service");
executor.setJobAlarm(new DingTalkJobAlarm()); // 自定义告警
return executor;
}
}
|
九、监控与日志
9.1 任务执行日志
任务管理 → 选任务 → 操作 → 日志 → 看每次执行:
- 触发时间
- 执行结果(成功 / 失败 / 调度失败)
- 耗时
- 失败原因(堆栈)
- 重试次数
日志保留:xxl.job.executor.logretentiondays=30(默认 30 天)。
9.2 调度报表
Admin → 调度报表:
1
2
| 成功 / 失败 / 调度失败 / 总数
按日 / 周 / 月 聚合
|
实战用法:每周一看上周的报表,任务成功率 < 99% 就要查问题。
9.3 告警指标
接入 Prometheus(自定义 exporter):
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
| @Aspect
@Component
public class XxlJobMetricsAspect {
@Around("@annotation(com.xxl.job.core.handler.annotation.XxlJob)")
public void track(ProceedingJoinPoint pjp) throws Throwable {
long start = System.currentTimeMillis();
try {
pjp.proceed();
Metrics.counter("xxl_job_success", "name", pjp.getSignature().getName()).increment();
} catch (Exception e) {
Metrics.counter("xxl_job_failure", "name", pjp.getSignature().getName()).increment();
} finally {
Metrics.timer("xxl_job_duration", "name", pjp.getSignature().getName())
.record(System.currentTimeMillis() - start, TimeUnit.MILLISECONDS);
}
}
}
|
十、典型坑位
10.1 任务执行但 Admin 看不到日志
症状:执行器跑完了任务,Admin 端日志查询不到。
原因:执行器 xxl.job.executor.logpath 路径配置错误。
修复:
1
2
3
4
| xxl:
job:
executor:
logpath: /data/applogs/xxl-job/jobhandler # 必须是绝对路径
|
容器内注意:路径要在挂载卷内(否则容器重启日志丢)。
10.2 任务调度延迟
症状:任务应该 2 点跑,实际 2:05 才跑。
原因:
- 调度中心集群只 1 台,单点压力
- 任务太多(> 1000 个),调度中心处理不过来
修复:
- 调度中心集群部署
- 减少短间隔任务(每分钟 1 次的太多)
- 加大
xxl.job.triggerpool.fast.max
10.3 任务重复执行
症状:1 个任务在 2 台机器都跑了。
原因:路由策略选错(**不能用"分片广播"**用于只能跑 1 次的任务)。
修复:选 故障转移 或 第一个(默认)。
10.4 GLUE 模式不能用第三方类
修复:
1
2
| GLUE Java 只能用 JDK 内置类
要用 Spring Bean,注解 @XxlJob
|
十一、生产部署清单
11.1 调度中心高可用
两台调度中心集群:
1
2
3
4
5
6
7
8
9
10
11
| # 调度中心 1
xxl-job-admin:
ports: 8080
environment:
PARAMS: "--xxl.job.accessToken={{TOKEN}}"
# 调度中心 2
xxl-job-admin:
ports: 8080
environment:
PARAMS: "--xxl.job.accessToken={{TOKEN}}"
|
执行器配置 2 个地址:
1
2
3
4
| xxl:
job:
admin:
addresses: http://admin1:8080/xxl-job-admin,http://admin2:8080/xxl-job-admin
|
任务触发时会轮询选一个调度中心。
11.2 数据库高可用
1
2
3
4
5
6
| # MySQL 主从 + 读写分离
spring:
datasource:
url: jdbc:mysql://mysql-primary:3306/xxl_job
# 读流量大的话可加:
# url: jdbc:mysql:loadbalance://mysql-primary:3306,mysql-replica:3306/xxl_job
|
11.3 监控
- Prometheus 抓 Admin metrics
- 告警规则:
- 任务失败率 > 5% → 告警
- 调度延迟 > 5 分钟 → 告警
- 调度中心内存 > 80% → 告警
十二、完整 docker-compose
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
| version: "3.8"
services:
mysql:
image: mysql:8.0
container_name: xxl-job-mysql
restart: always
environment:
MYSQL_ROOT_PASSWORD: {{DB_ROOT_PASS}}
volumes:
- /home/xxl-job/mysql:/var/lib/mysql
networks:
- xxl-net
xxl-job-admin:
image: xuxueli/xxl-job-admin:2.4.0
container_name: xxl-job-admin
restart: always
depends_on:
- mysql
ports:
- "8080:8080"
volumes:
- /home/xxl-job/logs:/data/applogs
environment:
PARAMS: >
--spring.datasource.url=jdbc:mysql://mysql:3306/xxl_job?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai
--spring.datasource.username=root
--spring.datasource.password={{DB_ROOT_PASS}}
--xxl.job.accessToken={{XXL_JOB_TOKEN}}
JAVA_OPTS: "-Xms1g -Xmx1g -XX:+UseContainerSupport -XX:MaxRAMPercentage=75.0"
networks:
- xxl-net
networks:
xxl-net:
driver: bridge
|
十三、最佳实践清单
- MySQL 8.0 + utf8mb4——中文支持好
- 调度中心至少 2 台集群——避免单点
- 路由策略根据场景选:
- 跑批任务 → 故障转移(高可用)
- 同步任务 → 分片广播(并行)
- 普通定时 → 第一个(避免重复)
- 告警邮件必开——失败 5 分钟内收到
- 任务日志保留 30 天——
logretentiondays=30 - 失败重试 3 次——临时网络抖动不报错
- 执行器
ip+port 唯一——xxl.job.executor.ip 显式指定 - GLUE 用于临时脚本——生产任务用
@XxlJob 注解 - JVM 调优:
UseContainerSupport + MaxRAMPercentage - 任务配置备份:MySQL 定期 dump
2024+ 视角补充
本文写于 2023-11,2024-2026 期间 XXL-Job / 分布式调度生态关键演进:
- XXL-Job 2.4+ → 2.5+(2024-2025):Spring Boot 3.x 兼容;JDK 17 强制;K8s 原生调度(不再依赖固定 IP)
- XXL-Job 2.5+:内置 OpenTelemetry 集成;Web IDE 增强(GLUE Java 支持 Spring Bean)
- PowerJob 5.x(2024-2025):K8s 原生 Worker(动态注册);DAG 工作流 2.0;MapReduce 性能 10x——vs XXL-JOB 更现代
- DolphinScheduler 3.2+(2024-2026):Apache 顶级项目;大数据集成(Spark / Flink / Hive);K8s 调度原生;多租户 + 工作流 3.0
- Airflow 2.10+(2024-2026):Python 系首选;TaskFlow API(Pythonic DAG);Kubernetes Executor 2.0
- Temporal 1.25+(2024-2026):Workflow-as-Code 之王——状态机 + 长期运行任务首选
- KubeVela 1.10+:云原生应用编排——微服务编排 + 灰度发布
- Serverless Cron:AWS EventBridge Scheduler / 阿里云定时任务 / GCP Cloud Scheduler 2024+ 仍是云端首选
实战建议(2025-2026 视角):
- Java 业务定时任务 → XXL-Job 2.5+(轻量、上手快)或 PowerJob 5.x(更现代)
- 大数据 / 离线数仓 → DolphinScheduler 3.2+(Apache 顶级项目)
- Python 系 → Airflow 2.10+
- 复杂工作流 / 长期任务 → Temporal 1.25+(2024-2026 趋势)
- Serverless / 简单定时 → 云厂商 EventBridge / 定时任务
下一步