1 介绍一下xxl-job
XXL-JOB 是一个分布式任务调度平台,旨在为分布式应用系统提供开箱即用的调度解决方案。它非常易于使用,并具有很高的可扩展性。以下是 XXL-JOB 的详细介绍,包括其核心功能、架构设计、主要组件及其应用场景。
核心功能
-
简单易用:
- 提供友好的 Web 控制台,支持任务的增删查改操作。
- 支持手动触发、停止和重试任务。
-
调度策略丰富:
- 支持 CRON 表达式调度,精确到秒。
- 支持手动触发、周期调度、固定频率调度、补偿调度等多种调度策略。
-
任务执行方式:
- 支持 BEAN、GLUE(即在控制台编辑的脚本,比如JavaScript、Groovy)、脚本(Shell、Python 等)的任务执行方式。
-
分布式支持:
- 天然支持集群,采用无中心设计,可以轻松进行水平扩展。
- 支持广播模式和分片广播模式的任务执行方式。
-
执行日志:
- 详细记录每一次任务执行情况,包括任务开始时间、结束时间、执行结果和日志内容。
-
失败告警:
- 支持邮件、钉钉等多种方式进行任务执行失败告警。
架构设计
XXL-JOB 采用调度中心和执行器的分离设计,调度中心负责任务的管理和调度,执行器负责任务的具体执行。其主要架构组件包括:
-
调度中心(Admin)
- 提供 Web 管理界面,进行任务的管理和调度。
- 负责计算调度任务并将任务下发给执行器。
-
执行器(Executor)
- 实现执行任务的具体逻辑。
- 从调度中心接收任务并执行,执行完毕后回传执行结果。
-
任务调度:
- 基于 CRON 表达式或自定义调度方式进行任务调度。
-
执行日志:
- 记录任务的执行日志,方便查看任务的执行情况。
主要组件
-
调度中心(Admin):
- Web 控制台:提供任务的管理和监控。
- 调度管理:执行调度任务的分发。
-
执行器(Executor):
- 任务处理器:处理具体执行的任务逻辑。
- 日志记录:记录任务执行日志。
典型使用场景
-
数据处理:
- 定时从数据库、文件系统或者其他数据源提取数据,进行数据清洗和处理,再存储到数据仓库或其他存储系统。
-
营销活动:
- 定时推送营销消息或进行营销数据统计分析。
-
定时备份:
- 定时备份数据库、文件系统,以防止数据丢失。
-
定时检测:
- 定时进行服务器健康检测、系统性能监控等。
示例项目
以下是一个简单的示例项目,展示如何快速集成 XXL-JOB 到 Spring Boot 项目中。
1. 引入依赖
在您的 pom.xml
文件中添加 XXL-JOB 核心依赖:
<dependency>
<groupId>com.xuxueli</groupId>
<artifactId>xxl-job-core</artifactId>
<version>2.3.0</version> <!-- 版本号根据实际情况选择 -->
</dependency>
2. 配置 XXL-JOB 执行器
在 application.properties
或 application.yml
文件中配置执行器参数:
xxl.job.admin.addresses=http://127.0.0.1:8080/xxl-job-admin
xxl.job.executor.appname=xxl-job-executor-example
xxl.job.executor.ip=
xxl.job.executor.port=9999
xxl.job.accessToken=
xxl.job.executor.logpath=/data/applogs/xxl-job/jobhandler
xxl.job.executor.logretentiondays=30
3. 启动类添加 XXL-JOB 配置
在 Spring Boot 主入口类中添加 XXL-JOB 执行器配置:
import com.xxl.job.core.executor.impl.XxlJobSpringExecutor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class XxlJobConfig {
@Bean
public XxlJobSpringExecutor xxlJobExecutor() {
XxlJobSpringExecutor xxlJobSpringExecutor = new XxlJobSpringExecutor();
xxlJobSpringExecutor.setAdminAddresses("http://127.0.0.1:8080/xxl-job-admin");
xxlJobSpringExecutor.setAppname("xxl-job-executor-example");
xxlJobSpringExecutor.setIp(null);
xxlJobSpringExecutor.setPort(9999);
xxlJobSpringExecutor.setAccessToken(null);
xxlJobSpringExecutor.setLogPath("/data/applogs/xxl-job/jobhandler");
xxlJobSpringExecutor.setLogRetentionDays(30);
return xxlJobSpringExecutor;
}
}
4. 编写任务处理器
在项目中编写一个任务处理器,并使用 @XxlJob
注解声明任务名称:
import com.xxl.job.core.handler.annotation.XxlJob;
import com.xxl.job.core.log.XxlJobLogger;
import com.xxl.job.core.biz.model.ReturnT;
import org.springframework.stereotype.Component;
@Component
public class SampleJob {
@XxlJob("sampleJobHandler")
public ReturnT<String> execute(String param) throws Exception {
XxlJobLogger.log("XXL-JOB, Hello World.");
System.out.println("Executing job with param: " + param);
return ReturnT.SUCCESS;
}
}
5. 在控制台配置任务
登录 XXL-JOB 管理控制台,创建并配置调度任务。
通过这些步骤,你可以轻松在项目中集成和使用 XXL-JOB 进行任务调度。XXL-JOB 提供了强大的功能和灵活的配置,适用于各种场景下的任务调度需求。
2 弄懂xxl-job的bean调度策略和路由策略以及阻塞策略。
2.1 bean模式
2.2 xxl-job的bean调度策略和jobHandler路由策略以及阻塞策略关系
在 XXL-JOB 的任务调度中,Bean 调度策略、JobHandler 路由策略以及阻塞策略是三个不同层面的配置选项,它们共同作用来决定任务的执行方式。下面我们来详细讲解这些概念及其相互关系。
1. Bean 调度策略 (Bean 的定义和注册)
Bean 调度策略主要用于定义任务的实际执行逻辑。在 Spring 项目中,通常通过 @XxlJob
注解将某个方法注册为 XXL-JOB 的 JobHandler。
例如:
import com.xxl.job.core.handler.annotation.XxlJob;
import org.springframework.stereotype.Component;
@Component
public class MyJobHandler {
@XxlJob("myJobHandler")
public void execute() throws Exception {
System.out.println("Task is running...");
}
}
在 XXL-JOB 管理后台中,配置任务的 JobHandler 为 myJobHandler
,这个任务就会执行上述 MyJobHandler
类中的 execute
方法。
2. JobHandler 路由策略
JobHandler 路由策略决定任务在多个可用的执行器 (Executor) 节点上如何分发和执行。XXL-JOB 支持以下几种常见的路由策略:
- FIRST:优先路由至第一个机器;这种策略在大部分集群部署场景中不推荐,因为它会导致负载不均衡。
- LAST:优先路由至最后一个机器。
- ROUND:按顺序轮询;适用于负载均衡较为均匀的场景。
- RANDOM:随机选择;适用于需要一定随机性的任务分发。
- CONSISTENT_HASH:一致性哈希;对于同类任务会固定路由到某个机器,适合需要状态保持的任务。
- LEAST_FREQUENTLY_USED:最不经常使用策略,即选择执行次数最少的机器。
- LEAST_RECENTLY_USED:最久未使用策略,即选择最久未执行任务的机器。
- FAILOVER:故障转移,优先选择故障机器以外的机器。
- BUSYOVER:忙转移,优先选择即使繁忙机器也要执行。
- SHARDING_BROADCAST(2.1中分片广播路由策略):分片广播;用于将任务分片、并行执行在不同的机器上。
3. 阻塞策略
阻塞策略决定了当执行器忙碌,无法立即执行新任务时如何处理。这可以有效地帮助我们控制任务调度的行为以及系统的资源利用。常见的阻塞策略有:
- SERIAL_EXECUTION:串行阻塞。当调度线上已有任务在执行时,新任务将被阻塞,直到前一个任务完成。
- DISCARD_LATER:丢弃后续调度。如果调度线上已有任务在执行,新任务将被丢弃。
- COVER_EARLY:覆盖早期任务。如果调度线上已有任务在执行,新任务将覆盖旧任务。
实际情况如果碰到阻塞的情况最好带有报警功能。
三者关系总结
- Bean 调度策略:决定任务的实际执行逻辑,通过 Spring Bean 和
@XxlJob
注解进行配置。 - JobHandler 路由策略:决定任务在多个执行器节点上的分发方式,通过 XXL-JOB 管理后台配置。
- 阻塞策略:决定当执行器忙碌时,如何处理新到达的任务,也通过 XXL-JOB 管理后台配置。
每个 JobHandler 可以根据具体需求配置上述策略,使得任务调度系统更灵活和智能。例如:
- Bean 调度策略定义了任务执行的具体方法。
- JobHandler 路由策略确保任务能在集群中合理分发,达到负载均衡或高可用性的目标。
- 阻塞策略控制任务调度器在资源紧张情况下的行为,避免系统过载或者无意义的任务重叠。
示例配置
在 XXL-JOB 管理后台,配置一个任务时,可以对每个任务单独设置这些策略:
- 选择 JobHandler (e.g.,
myJobHandler
) - 指定路由策略 (e.g.,
ROUND
) - 设置阻塞策略 (e.g.,
COVER_EARLY
)
通过这样配置,可以实现灵活而高效的任务调度系统。
3 xxl-job中分片广播和单播
3.1 分片广播和单播的区别
在 XXL-JOB 中,分片广播和单播是两种不同的任务调度模式,适用于不同的业务场景。下面详细说明两者的区别:
1. 单播(Broadcast)
单播是指任务在多个可用的执行器节点中选择一个节点进行执行。这种策略适用于任务在任何一个节点上执行都可以,并且无需分片处理的情况。
- 执行目标:只有一个执行器节点会被选中来执行任务。
- 实现方式:通过路由策略选择一个执行器节点,例如随机选择、轮询选择等。
- 适用场景:适用于需要单一节点处理,且不需要并行拆分的任务。例如,数据清理、日志归档等操作。
示例代码(单播任务):
import com.xxl.job.core.handler.annotation.XxlJob;
import org.springframework.stereotype.Component;
@Component
public class MyJobHandler {
@XxlJob("myJobHandler")
public void execute() throws Exception {
System.out.println("Single instance task is running...");
}
}
在管理后台中配置任务选择的路由策略,例如设置为 ROUND
(轮询):
- JobHandler:
myJobHandler
- 路由策略:
ROUND
- 阻塞策略:根据需要选择,例如
SERIAL_EXECUTION
2. 分片广播(Sharding Broadcast)
分片广播是指任务被拆分成多个子任务,并由多个执行器节点并行处理。每个执行器节点处理分片任务的一部分,适用于需要大规模并行处理的场景。
- 执行目标:任务会被拆分,并由多个执行器节点同时处理,每个节点处理任务的一部分。
- 实现方式:任务根据分片参数(分片索引和分片总数)拆分,并分发到不同的执行器节点。
- 适用场景:适用于任务需要并行处理和分片处理的场景,例如大规模数据处理、分布式计算等。
示例代码(分片广播任务):
import com.xxl.job.core.handler.annotation.XxlJob;
import com.xxl.job.core.context.XxlJobHelper;
import org.springframework.stereotype.Service;
@Service
public class ShardingJobHandler {
@XxlJob("shardingJobHandler")
public void execute() throws Exception {
// 获取当前分片信息
int shardIndex = XxlJobHelper.getShardIndex();
int shardTotal = XxlJobHelper.getShardTotal();
// 根据分片信息执行任务
System.out.println("Shard Index: " + shardIndex);
System.out.println("Shard Total: " + shardTotal);
// 这里可以根据分片信息执行具体业务逻辑
// 例如处理数据库中某一段数据
}
}
在管理后台中配置任务选择的路由策略和分片参数:
- JobHandler:
shardingJobHandler
- 路由策略:
SHARDING_BROADCAST
- 分片总数:设定合理的分片总数,例如 4,表示分成 4 片并行处理。
- 阻塞策略:根据需要选择,例如
SERIAL_EXECUTION
主要区别总结
特性 | 单播 (Broadcast) | 分片广播 (Sharding Broadcast) |
---|---|---|
执行节点数量 | 单个节点 | 多个节点,并行处理 |
任务拆分 | 不拆分,任务在一个节点上完整执行 | 被拆分成多个子任务,每个节点处理一部分 |
适用场景 | 需要单节点执行的任务,例如日志清理、定期备份 | 大规模并行处理的任务,例如数据处理、分布式计算 |
任务复杂度 | 低 | 较高,需要在代码中实现分片逻辑 |
应用场景示例
- 单播:定期清理系统日志,在多个节点中选择一个节点执行日志删除操作。
- 分片广播:对大量数据进行分析,将数据根据分片参数分配到不同的节点并行处理,以加速任务完成。
通过选择合适的调度策略,可以充分利用系统资源,提高任务处理效率。单播适用于简单、无需并行处理的任务,而分片广播则适用于需要并行处理的大规模任务。
3.2 也就是说在分片的时候需要我们在代码内部实现具体的分片处理逻辑,如果不设置,就会变成每个节点执行一样的任务对吗?假如有一个扫库任务,如果选择分片广播,咋么做
是的,您理解得非常正确。在使用分片广播策略时,具体的分片处理逻辑需要在任务代码中实现。下面是对此过程的详细解释:
1. 分片逻辑的必要性
使用分片广播策略的主要目的是为了并行处理大任务,通过将任务拆分成多个小任务并分发给多个实例执行。如果在代码中没有实现具体的分片逻辑,每个实例会执行相同的任务,这不仅会导致重复处理,还会浪费计算资源,达不到预期的并行加速效果。
2. 如何实现分片逻辑
在任务代码中,需要获取当前任务的分片信息(例如分片索引和分片总数),然后根据这些信息来决定每个实例应该处理的数据子集。这通常涉及到以下几个步骤:
-
获取分片信息:
使用XxlJobHelper
提供的方法来获取当前分片的索引和分片总数。 -
根据分片信息划分数据:
具体如何划分数据取决于数据的组织方式和任务的需求。可以根据主键、行号、哈希值等进行分片。 -
执行分片任务:
根据划分结果,实例仅处理属于自己分片的数据子集。
示例代码详解
以下是一个详细的代码示例,演示如何实现一个分片广播的扫库任务:
import com.xxl.job.core.handler.annotation.XxlJob;
import com.xxl.job.core.context.XxlJobHelper;
import org.springframework.stereotype.Service;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.Statement;
@Service
public class DatabaseShardingTask {
@XxlJob("shardingDatabaseJobHandler")
public void shardingDatabaseJobHandler() throws Exception {
// 获取分片参数
int shardIndex = XxlJobHelper.getShardIndex();
int shardTotal = XxlJobHelper.getShardTotal();
// 数据库连接配置(请根据实际情况修改)
String url = "jdbc:mysql://localhost:3306/your_database";
String username = "your_username";
String password = "your_password";
// 示例代码:读取分片对应的数据并处理
try (Connection connection = DriverManager.getConnection(url, username, password);
Statement statement = connection.createStatement()) {
// 假设数据库表有一个 auto_increment 主键 id,通过 MOD 函数进行分片处理
String query = String.format("SELECT * FROM your_table WHERE MOD(id, %d) = %d", shardTotal, shardIndex);
ResultSet rs = statement.executeQuery(query);
// 处理结果集
while (rs.next()) {
// 处理单条数据
int id = rs.getInt("id");
String data = rs.getString("data_column");
// 执行具体的业务处理逻辑
System.out.println("Processing id: " + id + ", data: " + data);
// 比如:更新数据状态
// updateDataStatus(id, newStatus);
}
} catch (Exception e) {
// 记录日志并抛出异常
XxlJobHelper.log(e);
throw e;
}
}
// 示例辅助方法:更新数据状态
private void updateDataStatus(int id, String newStatus) {
// 更新数据逻辑
}
}
总结
- 在 XXl-JOB 分片广播策略中,需要在任务代码中明确实现分片处理逻辑。
- 未设置分片逻辑会导致所有分片实例执行相同的任务,浪费资源。
- 通过获取当前任务的分片索引和分片总数,可以精确划分数据,并让每个实例只处理其对应的数据子集。
合理的分片和任务处理不仅可以提高效率,还能均衡负载,确保任务在分布式环境中高效稳定地执行。