
🚀 XXL-JOB通关实战:从部署到落地,搞定90%的常规调度场景
国内最火的分布式任务调度框架!XXL-JOB从部署到生产级优化全攻略
在分布式系统开发中,定时任务几乎是每个后端项目绕不开的需求。从每天凌晨的报表生成,到订单超时自动关闭,再到数据同步和缓存刷新,任务调度无处不在。然而,随着业务规模扩大和节点增多,单机 @Scheduled 注解的局限性愈发明显:任务重复执行、缺乏监控、无法动态管理、集群下难以协调……
这时,一个成熟的分布式任务调度框架就显得尤为重要。而在国内众多开源方案中,XXL-JOB 凭借其轻量、稳定、与 SpringBoot 无缝集成的特性,成为了绝大多数中小团队的首选。
本文将带你从零开始,深度掌握 XXL-JOB 的部署、核心功能、架构原理、生产优化,并通过一个电商实战案例,让你真正理解如何用 XXL-JOB 搞定 90% 的常规调度场景。
一、为什么 XXL-JOB 是国内首选?
在对比了 Quartz、Elastic-Job、PowerJob 等框架后,XXL-JOB 之所以能脱颖而出,主要归功于以下几点:
- 轻量级:仅依赖 MySQL,无需额外中间件,部署简单,学习成本低。
- 功能丰富:支持 Cron 任务、分片广播、任务依赖、动态线程池、失败重试、邮件告警等。
- 可视化管理:提供功能完善的后台管理界面,任务管理、日志查看、调度报表一目了然。
- SpringBoot 生态深度整合:执行器可快速集成到 SpringBoot 项目中,开发体验极佳。
- 高可用:调度中心支持集群部署,消除单点故障。
- 活跃社区:国内使用广泛,文档齐全,遇到问题容易找到解决方案。
下面这张图可以直观地展示 XXL-JOB 的整体架构:
#mermaid-svg-8cPdHdhQGo44b3b1{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-8cPdHdhQGo44b3b1 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-8cPdHdhQGo44b3b1 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-8cPdHdhQGo44b3b1 .error-icon{fill:#552222;}#mermaid-svg-8cPdHdhQGo44b3b1 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-8cPdHdhQGo44b3b1 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-8cPdHdhQGo44b3b1 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-8cPdHdhQGo44b3b1 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-8cPdHdhQGo44b3b1 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-8cPdHdhQGo44b3b1 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-8cPdHdhQGo44b3b1 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-8cPdHdhQGo44b3b1 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-8cPdHdhQGo44b3b1 .marker.cross{stroke:#333333;}#mermaid-svg-8cPdHdhQGo44b3b1 svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-8cPdHdhQGo44b3b1 p{margin:0;}#mermaid-svg-8cPdHdhQGo44b3b1 .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#333;}#mermaid-svg-8cPdHdhQGo44b3b1 .cluster-label text{fill:#333;}#mermaid-svg-8cPdHdhQGo44b3b1 .cluster-label span{color:#333;}#mermaid-svg-8cPdHdhQGo44b3b1 .cluster-label span p{background-color:transparent;}#mermaid-svg-8cPdHdhQGo44b3b1 .label text,#mermaid-svg-8cPdHdhQGo44b3b1 span{fill:#333;color:#333;}#mermaid-svg-8cPdHdhQGo44b3b1 .node rect,#mermaid-svg-8cPdHdhQGo44b3b1 .node circle,#mermaid-svg-8cPdHdhQGo44b3b1 .node ellipse,#mermaid-svg-8cPdHdhQGo44b3b1 .node polygon,#mermaid-svg-8cPdHdhQGo44b3b1 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-8cPdHdhQGo44b3b1 .rough-node .label text,#mermaid-svg-8cPdHdhQGo44b3b1 .node .label text,#mermaid-svg-8cPdHdhQGo44b3b1 .image-shape .label,#mermaid-svg-8cPdHdhQGo44b3b1 .icon-shape .label{text-anchor:middle;}#mermaid-svg-8cPdHdhQGo44b3b1 .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-8cPdHdhQGo44b3b1 .rough-node .label,#mermaid-svg-8cPdHdhQGo44b3b1 .node .label,#mermaid-svg-8cPdHdhQGo44b3b1 .image-shape .label,#mermaid-svg-8cPdHdhQGo44b3b1 .icon-shape .label{text-align:center;}#mermaid-svg-8cPdHdhQGo44b3b1 .node.clickable{cursor:pointer;}#mermaid-svg-8cPdHdhQGo44b3b1 .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-8cPdHdhQGo44b3b1 .arrowheadPath{fill:#333333;}#mermaid-svg-8cPdHdhQGo44b3b1 .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-8cPdHdhQGo44b3b1 .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-8cPdHdhQGo44b3b1 .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-8cPdHdhQGo44b3b1 .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-8cPdHdhQGo44b3b1 .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-8cPdHdhQGo44b3b1 .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-8cPdHdhQGo44b3b1 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-8cPdHdhQGo44b3b1 .cluster text{fill:#333;}#mermaid-svg-8cPdHdhQGo44b3b1 .cluster span{color:#333;}#mermaid-svg-8cPdHdhQGo44b3b1 div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-8cPdHdhQGo44b3b1 .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-8cPdHdhQGo44b3b1 rect.text{fill:none;stroke-width:0;}#mermaid-svg-8cPdHdhQGo44b3b1 .icon-shape,#mermaid-svg-8cPdHdhQGo44b3b1 .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-8cPdHdhQGo44b3b1 .icon-shape p,#mermaid-svg-8cPdHdhQGo44b3b1 .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-8cPdHdhQGo44b3b1 .icon-shape rect,#mermaid-svg-8cPdHdhQGo44b3b1 .image-shape rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-8cPdHdhQGo44b3b1 .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-8cPdHdhQGo44b3b1 .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-8cPdHdhQGo44b3b1 :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}
执行器集群
调度中心集群
注册
注册
注册
注册
注册
注册
调度请求
调度请求
调度请求
调度中心节点1
调度中心节点2
执行器节点1(SpringBoot应用)
执行器节点2
执行器节点3
MySQL数据库
二、快速部署:单机版 + 集群版
2.1 环境准备
- JDK 1.8+
- MySQL 5.7+(建议 8.0)
- Maven 3+(可选,用于源码编译)
2.2 初始化数据库
从 XXL-JOB GitHub 获取 doc/db/tables_xxl_job.sql 脚本,在 MySQL 中执行创建数据库和表。
2.3 单机版部署(推荐 Docker-Compose)
我们使用 Docker-Compose 快速启动调度中心 + MySQL,适合本地开发和测试。
创建 docker-compose.yml:
version: '3.8'
services:
mysql:
image: mysql:8.0
container_name: xxl–job–mysql
environment:
MYSQL_ROOT_PASSWORD: root123
MYSQL_DATABASE: xxl_job
ports:
– "3306:3306"
volumes:
– ./mysql–data:/var/lib/mysql
– ./tables_xxl_job.sql:/docker–entrypoint–initdb.d/init.sql
healthcheck:
test: ["CMD", "mysqladmin", "ping", "-h", "localhost"]
timeout: 10s
retries: 5
xxl-job-admin:
image: xuxueli/xxl–job–admin:2.4.0
container_name: xxl–job–admin
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=root123
––xxl.job.accessToken=default_token'
ports:
– "8080:8080"
depends_on:
mysql:
condition: service_healthy
启动:
docker-compose up -d
访问 http://localhost:8080/xxl-job-admin,默认用户名/密码:admin/123456。
2.4 集群版部署
生产环境需要调度中心高可用,通常部署两个或以上节点,共享同一个数据库。
步骤:
upstream xxl-job-admin {
server 192.168.1.10:8080;
server 192.168.1.11:8080;
}
server {
listen 80;
server_name scheduler.example.com;
location / {
proxy_pass http://xxl-job-admin;
}
}
集群架构图:
#mermaid-svg-5wrXpURR3FNuAsxv{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-5wrXpURR3FNuAsxv .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-5wrXpURR3FNuAsxv .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-5wrXpURR3FNuAsxv .error-icon{fill:#552222;}#mermaid-svg-5wrXpURR3FNuAsxv .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-5wrXpURR3FNuAsxv .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-5wrXpURR3FNuAsxv .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-5wrXpURR3FNuAsxv .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-5wrXpURR3FNuAsxv .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-5wrXpURR3FNuAsxv .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-5wrXpURR3FNuAsxv .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-5wrXpURR3FNuAsxv .marker{fill:#333333;stroke:#333333;}#mermaid-svg-5wrXpURR3FNuAsxv .marker.cross{stroke:#333333;}#mermaid-svg-5wrXpURR3FNuAsxv svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-5wrXpURR3FNuAsxv p{margin:0;}#mermaid-svg-5wrXpURR3FNuAsxv .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#333;}#mermaid-svg-5wrXpURR3FNuAsxv .cluster-label text{fill:#333;}#mermaid-svg-5wrXpURR3FNuAsxv .cluster-label span{color:#333;}#mermaid-svg-5wrXpURR3FNuAsxv .cluster-label span p{background-color:transparent;}#mermaid-svg-5wrXpURR3FNuAsxv .label text,#mermaid-svg-5wrXpURR3FNuAsxv span{fill:#333;color:#333;}#mermaid-svg-5wrXpURR3FNuAsxv .node rect,#mermaid-svg-5wrXpURR3FNuAsxv .node circle,#mermaid-svg-5wrXpURR3FNuAsxv .node ellipse,#mermaid-svg-5wrXpURR3FNuAsxv .node polygon,#mermaid-svg-5wrXpURR3FNuAsxv .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-5wrXpURR3FNuAsxv .rough-node .label text,#mermaid-svg-5wrXpURR3FNuAsxv .node .label text,#mermaid-svg-5wrXpURR3FNuAsxv .image-shape .label,#mermaid-svg-5wrXpURR3FNuAsxv .icon-shape .label{text-anchor:middle;}#mermaid-svg-5wrXpURR3FNuAsxv .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-5wrXpURR3FNuAsxv .rough-node .label,#mermaid-svg-5wrXpURR3FNuAsxv .node .label,#mermaid-svg-5wrXpURR3FNuAsxv .image-shape .label,#mermaid-svg-5wrXpURR3FNuAsxv .icon-shape .label{text-align:center;}#mermaid-svg-5wrXpURR3FNuAsxv .node.clickable{cursor:pointer;}#mermaid-svg-5wrXpURR3FNuAsxv .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-5wrXpURR3FNuAsxv .arrowheadPath{fill:#333333;}#mermaid-svg-5wrXpURR3FNuAsxv .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-5wrXpURR3FNuAsxv .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-5wrXpURR3FNuAsxv .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-5wrXpURR3FNuAsxv .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-5wrXpURR3FNuAsxv .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-5wrXpURR3FNuAsxv .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-5wrXpURR3FNuAsxv .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-5wrXpURR3FNuAsxv .cluster text{fill:#333;}#mermaid-svg-5wrXpURR3FNuAsxv .cluster span{color:#333;}#mermaid-svg-5wrXpURR3FNuAsxv div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-5wrXpURR3FNuAsxv .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-5wrXpURR3FNuAsxv rect.text{fill:none;stroke-width:0;}#mermaid-svg-5wrXpURR3FNuAsxv .icon-shape,#mermaid-svg-5wrXpURR3FNuAsxv .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-5wrXpURR3FNuAsxv .icon-shape p,#mermaid-svg-5wrXpURR3FNuAsxv .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-5wrXpURR3FNuAsxv .icon-shape rect,#mermaid-svg-5wrXpURR3FNuAsxv .image-shape rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-5wrXpURR3FNuAsxv .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-5wrXpURR3FNuAsxv .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-5wrXpURR3FNuAsxv :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}
客户端/执行器
Nginx负载均衡
调度中心节点1
调度中心节点2
MySQL
三、核心功能实操
3.1 创建第一个任务:简单任务
在调度中心,进入“任务管理”,点击“新增”,填写任务信息:
- 执行器:选择执行器(需要先注册执行器,见后文)
- 任务描述:测试任务
- 调度配置:Cron 表达式,如 0 */1 * * * ?(每分钟执行一次)
- 运行模式:Bean(使用 Spring Bean 方式)
- JobHandler:在代码中定义的 @XxlJob 注解的值,例如 demoJobHandler
在 SpringBoot 项目中引入依赖:
<dependency>
<groupId>com.xuxueli</groupId>
<artifactId>xxl-job-core</artifactId>
<version>2.4.0</version>
</dependency>
配置执行器(application.yml):
xxl:
job:
admin:
addresses: http://localhost:8080/xxl–job–admin
accessToken: default_token
executor:
appname: xxl–job–executor–sample
address:
ip:
port: 9999
logpath: /data/applogs/xxl–job/jobhandler
logretentiondays: 30
编写任务处理器:
@Component
public class SampleXxlJob {
@XxlJob("demoJobHandler")
public void demoJobHandler() throws Exception {
System.out.println("执行定时任务:" + LocalDateTime.now());
// 业务逻辑
}
}
启动项目,执行器会自动注册到调度中心。在任务管理页面启动任务,即可看到执行日志。
3.2 分片任务:并行处理海量数据
当数据量巨大时,可以将任务分成多个分片,由不同执行器节点并行处理。分片任务的核心在于获取当前分片参数。
@XxlJob("shardingJobHandler")
public void shardingJobHandler() throws Exception {
// 获取分片参数
ShardingUtil.ShardingVO shardingVO = ShardingUtil.getShardingVo();
int index = shardingVO.getIndex(); // 当前分片序号(从0开始)
int total = shardingVO.getTotal(); // 总分片数
// 根据分片处理数据,例如只处理 id % total == index 的数据
List<Long> ids = queryIdsByShard(index, total);
for (Long id : ids) {
process(id);
}
}
在任务配置中,路由策略选择“分片广播”。
执行器3
执行器2
执行器1
调度中心
执行器3
执行器2
执行器1
调度中心
#mermaid-svg-NlhtXKn54nxrhLSF{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-NlhtXKn54nxrhLSF .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-NlhtXKn54nxrhLSF .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-NlhtXKn54nxrhLSF .error-icon{fill:#552222;}#mermaid-svg-NlhtXKn54nxrhLSF .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-NlhtXKn54nxrhLSF .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-NlhtXKn54nxrhLSF .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-NlhtXKn54nxrhLSF .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-NlhtXKn54nxrhLSF .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-NlhtXKn54nxrhLSF .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-NlhtXKn54nxrhLSF .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-NlhtXKn54nxrhLSF .marker{fill:#333333;stroke:#333333;}#mermaid-svg-NlhtXKn54nxrhLSF .marker.cross{stroke:#333333;}#mermaid-svg-NlhtXKn54nxrhLSF svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-NlhtXKn54nxrhLSF p{margin:0;}#mermaid-svg-NlhtXKn54nxrhLSF .actor{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-NlhtXKn54nxrhLSF text.actor>tspan{fill:black;stroke:none;}#mermaid-svg-NlhtXKn54nxrhLSF .actor-line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-NlhtXKn54nxrhLSF .innerArc{stroke-width:1.5;stroke-dasharray:none;}#mermaid-svg-NlhtXKn54nxrhLSF .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333;}#mermaid-svg-NlhtXKn54nxrhLSF .messageLine1{stroke-width:1.5;stroke-dasharray:2,2;stroke:#333;}#mermaid-svg-NlhtXKn54nxrhLSF #arrowhead path{fill:#333;stroke:#333;}#mermaid-svg-NlhtXKn54nxrhLSF .sequenceNumber{fill:white;}#mermaid-svg-NlhtXKn54nxrhLSF #sequencenumber{fill:#333;}#mermaid-svg-NlhtXKn54nxrhLSF #crosshead path{fill:#333;stroke:#333;}#mermaid-svg-NlhtXKn54nxrhLSF .messageText{fill:#333;stroke:none;}#mermaid-svg-NlhtXKn54nxrhLSF .labelBox{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-NlhtXKn54nxrhLSF .labelText,#mermaid-svg-NlhtXKn54nxrhLSF .labelText>tspan{fill:black;stroke:none;}#mermaid-svg-NlhtXKn54nxrhLSF .loopText,#mermaid-svg-NlhtXKn54nxrhLSF .loopText>tspan{fill:black;stroke:none;}#mermaid-svg-NlhtXKn54nxrhLSF .loopLine{stroke-width:2px;stroke-dasharray:2,2;stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-NlhtXKn54nxrhLSF .note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-NlhtXKn54nxrhLSF .noteText,#mermaid-svg-NlhtXKn54nxrhLSF .noteText>tspan{fill:black;stroke:none;}#mermaid-svg-NlhtXKn54nxrhLSF .activation0{fill:#f4f4f4;stroke:#666;}#mermaid-svg-NlhtXKn54nxrhLSF .activation1{fill:#f4f4f4;stroke:#666;}#mermaid-svg-NlhtXKn54nxrhLSF .activation2{fill:#f4f4f4;stroke:#666;}#mermaid-svg-NlhtXKn54nxrhLSF .actorPopupMenu{position:absolute;}#mermaid-svg-NlhtXKn54nxrhLSF .actorPopupMenuPanel{position:absolute;fill:#ECECFF;box-shadow:0px 8px 16px 0px rgba(0,0,0,0.2);filter:drop-shadow(3px 5px 2px rgb(0 0 0 / 0.4));}#mermaid-svg-NlhtXKn54nxrhLSF .actor-man line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-NlhtXKn54nxrhLSF .actor-man circle,#mermaid-svg-NlhtXKn54nxrhLSF line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;stroke-width:2px;}#mermaid-svg-NlhtXKn54nxrhLSF :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}
触发任务,分片参数(0/3)
触发任务,分片参数(1/3)
触发任务,分片参数(2/3)
处理数据分片0
处理数据分片1
处理数据分片2
3.3 任务依赖(子任务)
XXL-JOB 支持简单的任务依赖:一个任务执行完成后,可以触发另一个任务。在任务配置中设置“子任务 ID”(多个用逗号分隔)。
#mermaid-svg-EiI8LaS4uOYSynpZ{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-EiI8LaS4uOYSynpZ .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-EiI8LaS4uOYSynpZ .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-EiI8LaS4uOYSynpZ .error-icon{fill:#552222;}#mermaid-svg-EiI8LaS4uOYSynpZ .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-EiI8LaS4uOYSynpZ .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-EiI8LaS4uOYSynpZ .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-EiI8LaS4uOYSynpZ .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-EiI8LaS4uOYSynpZ .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-EiI8LaS4uOYSynpZ .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-EiI8LaS4uOYSynpZ .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-EiI8LaS4uOYSynpZ .marker{fill:#333333;stroke:#333333;}#mermaid-svg-EiI8LaS4uOYSynpZ .marker.cross{stroke:#333333;}#mermaid-svg-EiI8LaS4uOYSynpZ svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-EiI8LaS4uOYSynpZ p{margin:0;}#mermaid-svg-EiI8LaS4uOYSynpZ .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#333;}#mermaid-svg-EiI8LaS4uOYSynpZ .cluster-label text{fill:#333;}#mermaid-svg-EiI8LaS4uOYSynpZ .cluster-label span{color:#333;}#mermaid-svg-EiI8LaS4uOYSynpZ .cluster-label span p{background-color:transparent;}#mermaid-svg-EiI8LaS4uOYSynpZ .label text,#mermaid-svg-EiI8LaS4uOYSynpZ span{fill:#333;color:#333;}#mermaid-svg-EiI8LaS4uOYSynpZ .node rect,#mermaid-svg-EiI8LaS4uOYSynpZ .node circle,#mermaid-svg-EiI8LaS4uOYSynpZ .node ellipse,#mermaid-svg-EiI8LaS4uOYSynpZ .node polygon,#mermaid-svg-EiI8LaS4uOYSynpZ .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-EiI8LaS4uOYSynpZ .rough-node .label text,#mermaid-svg-EiI8LaS4uOYSynpZ .node .label text,#mermaid-svg-EiI8LaS4uOYSynpZ .image-shape .label,#mermaid-svg-EiI8LaS4uOYSynpZ .icon-shape .label{text-anchor:middle;}#mermaid-svg-EiI8LaS4uOYSynpZ .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-EiI8LaS4uOYSynpZ .rough-node .label,#mermaid-svg-EiI8LaS4uOYSynpZ .node .label,#mermaid-svg-EiI8LaS4uOYSynpZ .image-shape .label,#mermaid-svg-EiI8LaS4uOYSynpZ .icon-shape .label{text-align:center;}#mermaid-svg-EiI8LaS4uOYSynpZ .node.clickable{cursor:pointer;}#mermaid-svg-EiI8LaS4uOYSynpZ .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-EiI8LaS4uOYSynpZ .arrowheadPath{fill:#333333;}#mermaid-svg-EiI8LaS4uOYSynpZ .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-EiI8LaS4uOYSynpZ .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-EiI8LaS4uOYSynpZ .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-EiI8LaS4uOYSynpZ .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-EiI8LaS4uOYSynpZ .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-EiI8LaS4uOYSynpZ .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-EiI8LaS4uOYSynpZ .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-EiI8LaS4uOYSynpZ .cluster text{fill:#333;}#mermaid-svg-EiI8LaS4uOYSynpZ .cluster span{color:#333;}#mermaid-svg-EiI8LaS4uOYSynpZ div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-EiI8LaS4uOYSynpZ .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-EiI8LaS4uOYSynpZ rect.text{fill:none;stroke-width:0;}#mermaid-svg-EiI8LaS4uOYSynpZ .icon-shape,#mermaid-svg-EiI8LaS4uOYSynpZ .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-EiI8LaS4uOYSynpZ .icon-shape p,#mermaid-svg-EiI8LaS4uOYSynpZ .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-EiI8LaS4uOYSynpZ .icon-shape rect,#mermaid-svg-EiI8LaS4uOYSynpZ .image-shape rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-EiI8LaS4uOYSynpZ .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-EiI8LaS4uOYSynpZ .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-EiI8LaS4uOYSynpZ :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}
任务A
任务B
任务C
3.4 执行器注册与动态线程池
执行器启动时会自动向调度中心注册。可以在调度中心查看“执行器管理”中的在线机器列表。
XXL-JOB 内部使用线程池执行任务,可通过配置调整:
xxl:
job:
executor:
threadpool:
core-size: 20
max-size: 200
queue-size: 1000
3.5 日志追踪
每个任务执行都会生成一条日志,可以在调度中心“调度日志”中查看详细执行记录、运行结果、异常堆栈。
3.6 告警配置(邮件/钉钉)
XXL-JOB 支持任务失败时发送邮件或钉钉机器人告警。
邮件配置(调度中心配置文件):
spring.mail.host=smtp.qq.com
spring.mail.port=587
spring.mail.username=your-email@qq.com
spring.mail.password=your-password
spring.mail.properties.mail.smtp.auth=true
xxl.job.mail.from=your-email@qq.com
钉钉配置:在任务编辑页面填写“告警机器人”的 Webhook 地址。
3.7 失败重试策略
在任务配置中设置“重试次数”,任务执行失败后会自动重试。重试间隔固定,默认 30 秒。
四、深度解析:架构与核心特性
4.1 架构设计
XXL-JOB 采用经典的“调度中心”+“执行器”架构:
- 调度中心:负责任务管理、调度、监控,不执行业务逻辑。
- 执行器:嵌入业务应用,接收调度中心的调度请求并执行任务。
通信方式:调度中心通过 HTTP 接口调用执行器,执行器提供回调接口上报执行结果。
4.2 核心特性
- 分片广播:任务一次性广播给集群中所有执行器,各执行器根据分片参数处理不同数据。
- 动态线程池:执行器内部维护线程池,可配置核心线程数、最大线程数,避免任务阻塞。
- 任务依赖:支持简单的串行依赖,但不支持复杂 DAG。
- 阻塞处理策略:调度过于密集时,可配置“串行”、“丢弃后续调度”、“覆盖之前调度”。
- 任务超时控制:可设置任务超时时间,超时自动中断。
- 失败重试与告警:支持邮件、钉钉、企业微信等多种告警渠道。
- 权限管理:多用户、角色权限控制。
五、生产级优化:高可用、性能调优、监控
5.1 高可用部署
- 调度中心集群:至少 2 个节点,共享数据库,前端负载均衡。
- 执行器集群:业务应用多节点部署,任务自动路由或分片。
- 数据库高可用:MySQL 主从复制 + 读写分离,或使用云数据库 RDS。
- 令牌校验:配置 accessToken 防止未授权调用。
5.2 性能调优
- 调度中心调优:调整 JVM 参数,增大堆内存。-Xms2g -Xmx2g
- 数据库连接池:调度中心使用 HikariCP,根据并发量调整最大连接数。
- 执行器线程池:根据任务类型(IO密集型/CPU密集型)合理配置核心线程数。
- 任务分片:避免单个任务耗时过长,拆分为分片任务。
- 日志自动清理:设置 logretentiondays 定期清理执行日志。
5.3 运维监控
- 监控调度中心:使用 Prometheus + Grafana 监控 JVM 指标、请求量。
- 报警:配置任务失败告警,及时响应。
- 日志聚合:将执行器日志接入 ELK 或 Loki,方便集中查看。
六、优缺点分析
| 轻量,仅依赖 MySQL | 调度中心有单点风险(需集群部署) |
| 功能丰富,覆盖大部分场景 | 不支持复杂工作流(DAG) |
| 可视化 UI,管理方便 | 依赖数据库,数据库压力较大时可能成为瓶颈 |
| 与 SpringBoot 无缝集成 | 任务依赖只能串行,不支持条件分支 |
| 社区活跃,文档齐全 | 分片任务需要应用层处理分片逻辑 |
七、实战案例:电商定时关单
7.1 需求
用户下单后 30 分钟内未支付,系统自动取消订单并释放库存。
7.2 设计方案
7.3 实现步骤
1. 任务表设计(示例)
CREATE TABLE `order` (
`id` BIGINT NOT NULL,
`status` TINYINT COMMENT '0-待支付 1-已支付 2-已取消',
`expire_time` DATETIME COMMENT '超时时间',
PRIMARY KEY (`id`),
KEY `idx_expire_status` (`expire_time`,`status`)
);
2. 分片任务处理器
@Component
public class OrderJob {
@XxlJob("closeExpiredOrderJob")
public void closeExpiredOrderJob() {
// 获取分片参数
ShardingUtil.ShardingVO shardingVO = ShardingUtil.getShardingVo();
int index = shardingVO.getIndex();
int total = shardingVO.getTotal();
// 查询待处理订单:status=0 且 expire_time < NOW()
// 使用分片条件:id % total == index
List<Long> orderIds = orderMapper.selectExpiredOrderIds(index, total);
for (Long orderId : orderIds) {
try {
orderService.closeOrder(orderId);
// 记录日志
} catch (Exception e) {
// 单个失败不影响其他订单
log.error("close order error", e);
}
}
}
}
3. 任务配置
- 路由策略:分片广播
- Cron:0 */1 * * * ?(每分钟执行)
- JobHandler:closeExpiredOrderJob
4. 效果
假设有 4 台执行器,每台处理 1/4 的订单,一分钟内即可完成所有超时订单的关闭,且不会互相干扰。
八、适用场景总结
XXL-JOB 适合:
- 中小型团队,希望快速接入分布式任务调度。
- 常规定时任务,如日报、对账、数据清理。
- 需要分片处理的批任务。
- SpringBoot 技术栈项目。
不适合:
- 需要复杂工作流编排(推荐 DolphinScheduler)。
- 超大规模任务调度(每日数亿级别,可考虑 PowerJob 或 MQ 方案)。
九、结语
XXL-JOB 以其轻量、稳定、易用的特点,成为国内使用最广泛的分布式任务调度框架。通过本文的实战,你应该已经掌握了它的部署、核心功能、架构原理和生产优化技巧。在下一个项目中,不妨尝试用它替换掉零散的 @Scheduled,体验一下分布式调度的魅力。
如果你在实践过程中遇到问题,欢迎在评论区留言交流!下篇文章我们将深入 PowerJob,探索更强大的无锁化调度和工作流能力,敬请期待。

