文章目录
- 1. 分布式任务调度
- 2. @Scheduled注解的局限
- 3. 什么是XXL-JOB
- 4. 通过源码部署调度中心
- 5. 通过docker部署调度中心
- 6. 在SpringBoot项目中接入XXL-JOB
- 7. XXL-JOB中的核心概念
- 8. 集群环境下任务的轮询路由策略
- 9. 集群环境下任务的分片广播路由策略
- 10. 执行器无法注册到配置中心
1. 分布式任务调度
分布式任务调度是一种在分布式系统中协调和执行任务的方法。在分布式计算环境中,任务调度器负责将任务分配到不同的计算节点上,以优化资源利用、提高任务处理效率、保证任务执行的可靠性。
分布式任务调度的几个关键点:
- 任务分配:根据任务的性质、资源的需求以及各节点的状态,将任务合理地分配到不同的节点上执行。
- 负载均衡:在多个节点之间平衡任务负载,避免某些节点过载而其他节点空闲的情况,从而提高系统整体的处理能力。
- 容错机制:当某个节点发生故障时,任务调度系统能够将任务重新分配到其他健康的节点上,确保任务的连续性和系统的稳定性。
- 资源管理:管理各个节点的资源使用情况,包括CPU、内存、存储和网络等,确保任务能够获得必要的资源。
- 任务监控:监控任务的执行状态,收集性能数据,为调度决策提供依据。
- 任务依赖管理:处理任务之间的依赖关系,确保前置任务完成后才开始后续任务。
分布式任务调度的常见应用场景:
- 大数据处理:在处理大规模数据集时,通过分布式任务调度来实现数据的并行处理。
- 实时计算:在需要实时响应的场景中,如实时数据分析、在线推荐系统等,分布式任务调度可以保证计算的时效性。
- 批量作业:定时执行批量作业,如数据备份、报表生成等。
- 微服务架构:在微服务架构中,分布式任务调度有助于服务之间的协调和通信。
分布式任务调度的工具和框架有很多,如Apache Hadoop的YARN、Apache Mesos、Kubernetes、以及国产的调度工具如XXL-JOB、Elastic-Job等。这些工具和框架提供了任务调度、资源管理、容错处理等功能,使得在分布式环境中管理和执行任务变得更加高效和可靠
2. @Scheduled注解的局限
利用 Spring 框架提供的 @Scheduled
注解可以比较方便地设置定时任务,但是使用 @Scheduled
注解存在以下问题:
- 集群环境下任务的重复执行问题(虽然可以通过分布式锁解决这个问题,但是比较麻烦)
- CRON 表达式定义在代码之中,不方便修改
- 如果定时任务执行失败,无法重试,也没有统计信息
- 如果任务量过大,任务不能有效地分片执行
3. 什么是XXL-JOB
XXL-JOB 是一个分布式任务调度平台,由中国人许雪里(一个来自美团的程序员)开发
XXL-JOB 的核心设计目标是开发迅速、学习简单、轻量级、易扩展,XXL-JOB 现已开放源代码并接入多家公司的线上产品线
源码地址:许雪里/xxl-job
文档地址:XXL开源社区
4. 通过源码部署调度中心
调度中心环境要求
- Maven3+
- Jdk1.8+
- Mysql5.7+
4.1 下载源码
我们先下载把 XXL-JOB 的源码(下载地址:许雪里/xxl-job)
下载 zip 压缩包
4.2 源码说明
由于 XXL-JOB 是用 Java 开发的,我们可以用 IDEA 打开 XXL-JOB 的源码
4.3 运行数据库脚本
找到 doc 目录下的 tables_xxl_job.sql
文件,执行 SQL 文件(XXL-JOB 的运行依赖于一个名为 xxl-job
数据库,数据库中有 8 张表)
4.4 补充:xxl_job数据库中八张表的作用
八张表的作用:
表名 | 作用 |
---|---|
xxl_job_info | 存储任务的基本信息,如任务名称、执行器信息、调度规则等。 |
xxl_job_log | 存储任务的执行日志,包括任务执行状态、执行耗时、执行结果等。 |
xxl_job_logglue | 存储任务的 Glue 配置信息,如 Glue 类型、Glue 源码等。 |
xxl_job_trigger_log | 存储任务的触发日志,包括任务触发时间、触发结果等。 |
xxl_job_user | 存储用户信息,如用户名、密码、角色等。 |
xxl_job_group | 存储执行器分组信息,如执行器分组名称、执行器信息等。 |
xxl_job_registry | 存储执行器的注册信息,如执行器 IP、端口、心跳间隔等。 |
xxl_job_lock | 存储任务执行时的锁信息,以避免任务并发执行。 |
4.5 调度中心配置
4.5.1 数据库相关配置
配置文件位置:xxl-job-master/xxl-job-admin/src/main/resources/application.properties
修改与数据库相关的信息
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
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
4.5.2 日志相关配置
配置文件位置:xxl-job-master/xxl-job-admin/src/main/resources/logback.xml
修改日志文件的存放路径
4.6 启动调度中心
启动 XxlJobAdminApplication
4.7 访问调度中心
在浏览器输入以下地址
http://localhost:8080/xxl-job-admin
- 默认的登录账号:admin
- 默认的登录密码:123456
成功登录后的界面
4.8 修改密码(可选)
在调度中心修改密码
4.9 补充:账号和密码的存放位置
XXL-JOB 的账号和密码存放在 xxl_job
数据库的 xxl_job_user
表中
表中默认有一个名为 admin 的用户,密码是 123456(数据库中存储的密码是经过加密后的字符串)
- role:角色:0-普通用户、1-管理员
- permission:权限:执行器ID列表,多个逗号分割
5. 通过docker部署调度中心
5.1 拉取镜像
sudo docker pull xuxueli/xxl-job-admin:2.4.1
下载好 xxl-job 的镜像后,可以将镜像保存为 tar 文件,下载到 Windows 本地,方便下一次在另一个 Linux 系统上运行
sudo docker save xuxueli/xxl-job-admin:2.4.1 -o /tmp/xxl-job-admin-2.4.1.tar
sudo chmod +rx /tmp/xxl-job-admin-2.4.1.tar
5.2 启动容器
修改与数据库有关的信息
sudo docker run \
-e 'PARAMS=--spring.datasource.url=jdbc:mysql://127.0.0.1:3306/xxl_job?Unicode=true&characterEncoding=UTF-8 \
--spring.datasource.username=wuyanzu \
--spring.datasource.password=!UpuSZAxG#1&2^cG \
--xxl.job.accessToken=default_token \
--server.port=8080' \
-p 8080:8080 \
-v /tmp:/data/applogs \
--name xxl-job-admin \
-d xuxueli/xxl-job-admin:2.4.1
指令的解释
-e 'PARAMS=... --server.port=8080'
: 这是设置环境变量PARAMS
,它包含了 XXL-JOB admin 服务启动时需要的一系列参数。参数之间用空格分隔,具体包括:--spring.datasource.url
: 指定 Spring Boot 应用的数据源 URL。--spring.datasource.username
: 数据库的用户名。--spring.datasource.password
: 数据库的密码。--xxl.job.accessToken
: XXL-JOB 的访问令牌,用于 API 访问控制。--server.port
: 指定服务运行的端口号。
-p 8080:8080
: 这是指定端口映射,将容器的 8080 端口映射到宿主机的 8080 端口。-v /tmp:/data/applogs
: 这是指定卷映射,将宿主机的/tmp
目录映射到容器的/data/applogs
目录,用于存储日志文件。--name xxl-job-admin
: 这是为容器设置一个名称,这里命名为xxl-job-admin
。-d
: 这是指定容器在后台运行。xuxueli/xxl-job-admin:2.4.1
: 这是指定要运行的 Docker 镜像,这里是xuxueli/xxl-job-admin
镜像的2.4.1
版本。
5.3 开放防火墙的端口
为了能够从外界访问 XXL-JOB,需要为 XXL-JOB 开放防火墙的 8080 端口
- 如果你使用的是云服务器,在安全组中放行 8080 端口
- 如果你安装了宝塔,除了在安全组中放行 8080 端口,还要在宝塔中放行 8080 端口
完成以上两个操作后,输入以下指令开放 9000 端口 和 9090 端口
ubuntu
sudo ufw allow 8080
sudo ufw reload
CentOS
sudo firewall-cmd --zone=public --add-port=8080 /tcp --permanent
sudo firewall-cmd --reload
5.4 访问调度中心
在浏览器输入以下地址(将 localhost 更改为你的 IP 地址)
http://localhost:8080/xxl-job-admin
- 默认的登录账号:admin
- 默认的登录密码:123456
5.5 注意事项
XXL-JOB 的运行依赖于数据库,通过 docker 部署 XXL-JOB 时,确保调度中心能连接到数据库
使用一个公网可以访问的数据库是最简单的方法
6. 在SpringBoot项目中接入XXL-JOB
6.1 新建执行器
在调度中心中新建执行器(默认情况下会有一个名为 xxl-job-executor-sample
的执行器)
6.2 新建任务
在调度中心中新建任务(默认情况下会有一个名为 测试任务1
的任务)
任务的 JobHandler
属性下面会用到
0/1 * * * * ? *
表示每秒执行一次
0/1 * * * * ? *
6.3 引入依赖
<dependency>
<groupId>com.xuxueli</groupId>
<artifactId>xxl-job-core</artifactId>
<version>2.3.0</version>
</dependency>
6.4 编写与XXL-JOB相关的配置
XXL-JOB 会启动一个内嵌的服务器,该内嵌服务器默认会占用 9999 端口,可以指定
application.yml
server:
port: 8881
xxl:
job:
access-token: default_token
admin:
addresses: http://127.0.0.1:8080/xxl-job-admin
executor:
app-name: xxl-job-executor-sample
ip : localhost
port: 11015
6.5 编写XXL-JOB配置类
import com.xxl.job.core.executor.impl.XxlJobSpringExecutor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class XxlJobConfiguration {
private final Logger logger = LoggerFactory.getLogger(XxlJobConfiguration.class);
@Value("${xxl.job.admin.addresses}")
private String adminAddresses;
@Value("${xxl.job.executor.app-name}")
private String appName;
@Value("${xxl.job.executor.ip}")
private String ip;
@Value("${xxl.job.executor.port}")
private Integer port;
@Value("${xxl.job.access-token}")
private String accessToken;
@Bean
public XxlJobSpringExecutor xxlJobSpringExecutor() {
logger.info("============================== xxlJobSpringExecutor ==============================");
XxlJobSpringExecutor xxlJobSpringExecutor = new XxlJobSpringExecutor();
xxlJobSpringExecutor.setAdminAddresses(adminAddresses);
xxlJobSpringExecutor.setAppname(appName);
xxlJobSpringExecutor.setAccessToken(accessToken);
xxlJobSpringExecutor.setIp(ip);
xxlJobSpringExecutor.setPort(port);
xxlJobSpringExecutor.setLogPath("F:\\HeiMaTouTiao\\xxl-job\\executor");
logger.info("============================== xxlJobSpringExecutor ==============================");
return xxlJobSpringExecutor;
}
}
6.6 编写任务代码
使用 @XxlJob
注解指定要执行哪个任务
import com.xxl.job.core.handler.annotation.XxlJob;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
@Component
public class HelloXxlJob {
@Value("${server.port}")
private String port;
@XxlJob("demoJobHandler")
public void echo() {
System.out.println("Hello, XXL-JOB" + port);
}
}
6.7 启动任务
先启动 SpringBoot 应用程序(SpringBoot 应用程序启动后会自动注册到调度中心)
接着在调度中心启动任务
任务启动后就能在控制台看到输出了
7. XXL-JOB中的核心概念
7.1 执行器(Executor)
在 XXL-JOB 中,执行器(Executor)是指那些实际执行任务的远程服务。每个执行器都会向 XXL-JOB 的调度中心(Admin)注册自己,并定期发送心跳以保持其注册状态。
XXL-JOB 中执行器的几个关键点:
- 注册和心跳:执行器启动后,会向调度中心注册,并在
xxl_job_registry
表中记录其地址(通常是 IP 地址和端口号)。执行器还会定期发送心跳,以证明自己仍然在线并能够接收任务。 - 任务执行:当调度中心接收到任务执行请求时,它会根据任务的配置选择一个合适的执行器来执行任务。
- 地址记录:在
xxl_job_registry
表中,执行器的地址记录为registry_value
。如果这个值是localhost
,那么它通常意味着执行器和调度中心部署在同一台服务器上。 - 本地通信:如果执行器的
registry_value
是localhost
,调度中心通过本地回环接口与执行器通信,不经过网络接口,因此没有网络延迟。 - 端口监听:执行器需要监听一个端口(例如
9999
),以便调度中心可以发送任务执行请求到这个端口。 - 配置注意事项:
- 确保执行器监听的端口没有被其他服务占用。
- 如果服务器上有防火墙,需要允许本地回环接口上的通信。
- 考虑到可维护性和可扩展性,建议在非测试和开发环境中使用具体的 IP 地址或域名,而不是
localhost
。
- 迁移和扩展:如果将来需要将执行器迁移到不同的服务器,或者需要扩展系统以处理更多任务,那么需要更新
xxl_job_registry
表中的registry_value
,以反映执行器的新地址。
总的来说,XXL-JOB 中的执行器是任务执行的关键组件,它需要正确地注册和配置,以确保调度中心可以有效地分配和执行任务。
7.2 任务(JOB)
7.2.1 执行器
每个任务必须绑定一个执行器,方便对任务进行分组
7.2.2 任务描述
任务的描述信息,便于任务管理
7.2.3 负责人
任务的负责人
7.2.4 报警邮件
任务调度失败时邮件通知的邮箱地址,支持配置多邮箱地址,配置多个邮箱地址时用逗号分隔
7.2.5 调度类型
- 无:该类型不会主动触发调度
- CRON:该类型将会通过 CRON 表达式触发任务调度
- 固定速度:该类型将会以固定速度,触发任务调度(按照固定的间隔时间,周期性触发)
7.2.6 运行模式
- Bean 类型任务:
- 概述:Bean 类型任务通常指的是通过 Spring 容器管理的 Java Bean 类。
- 执行方式:调度中心会将任务交给 Spring 容器管理的 Java Bean 类进行执行。这种方式适合需要依赖 Spring 环境的任务。
- 依赖:任务需要依赖 Spring 容器,因此需要将任务对应的 Java Bean 类添加到 Spring 容器中。
- 配置:在 XXL-JOB 的配置中,需要指定任务的 Bean 类名。
- Glue 类型任务:
- 概述:Glue 类型任务通常指的是外部的脚本文件,如 Shell、Python、Java 等。
- 执行方式:调度中心会将任务交给指定的脚本文件进行执行。这种方式适合需要执行外部脚本的任务。
- 依赖:任务不需要依赖 Spring 容器,可以直接执行外部脚本。
- 配置:在 XXL-JOB 的配置中,需要指定任务的 Glue 类型(如 Shell、Python、Java 等)和对应的脚本文件路径。
7.2.7 JobHandler
运行模式为 BEAN
模式时生效,对应 Bean 中 @XxlJob
注解中声明的 value 值
7.2.8 任务参数
任务执行所需要的参数
7.2.9 阻塞处理策略
调度过于密集,执行器来不及处理时的处理策略:
- 单机串行:调度请求进入单机执行器后,调度请求进入 FIF0(First Input First 0utput)队列,以串行方式运行
- 丢弃后续调度:调度请求进入单机执行器后,发现执行器存在运行的调度任务,本次请求将会被丢弃并标记为失败
- 覆盖之前调度:调度请求进入单机执行器后,发现执行器存在运行的调度任务,将会终止运行中的调度任务并清空队列,然后运行本地调度任务
7.2.10 路由策略
在 XXL-JOB 中,执行器集群部署时,有以下路由策略:
- 第一个(FIRST):任务总是固定选择第一个可用的执行器。
- 最后一个(LAST):任务总是固定选择最后一个可用的执行器。
- 轮询(ROUND):按照顺序轮流选择可用的执行器。
- 随机(RANDOM):随机选择在线的机器。
- 一致性HASH(CONSISTENT HASH):使用一致性哈希算法,每个任务固定选择某一台机器,且所有任务均匀散列在不同机器上。
- 最不经常使用(LEAST FREQUENTLY_USED):使用频率最低的机器优先被选举。
- 最近最久未使用(LEAST RECENTLY_USED):最久未使用的机器优先被选举。
- 故障转移(FAILOVER):当第一个执行器失败时,依次尝试下一个执行器,直到找到可用的执行器。
- 忙碌转移(BUSYOVER):当第一个执行器忙碌时,依次尝试下一个执行器,直到找到空闲的执行器。
- 分片广播(SHARDING BROADCAST):广播触发对应集群中所有机器执行一次任务,同时系统自动传递分片参数。
8. 集群环境下任务的轮询路由策略
我们演示一下集群环境下任务的轮询路由策略
8.1 修改任务的执行策略为轮询
8.2 启动多个微服务
右键微服务,点击复制配置
点击修改选项
点击添加虚拟机选项
在虚拟机选项中填入以下内容
-Dserver.port=8882 -Dexecutor.port=11016
接着修改 application.yml
文件,修改内容如下
port: ${executor.port:11015}
右键复制出来的微服务,启动程序
最后启动任务,就可以分别在两个微服务的控制台中看到运行结果了
9. 集群环境下任务的分片广播路由策略
9.1 什么情况下采用分片广播
集群环境下,如果任务的路由策略为分片广播,一次任务调度将会广播集群中的所有执行器
举个例子,支付宝的花呗每个月 10 号都会通知用户还款,由于支付宝花呗的用户很多,任务量会特别大,如果使用轮询的方式,任务的执行效率就会变得很低
也就是说,在同一个时间点,要执行大量任务的情况下,会用到分片广播
假如现在有 8 个任务,集群中有三台机器,如果我们要同时地执行任务,就需要为每一个实例分配任务
那 XXL-JOB 是如何做的呢
假设现在集群中还是有三个实例,XXL-JOB 会通过任务 id 取模(对集群中的实例数取模)的方式让具体的分片执行任务
9.2 分片广播案例
需求:让两个节点同时执行 10000 个任务,每个节点分别执行 5000 个任务
9.2.1 创建执行器
我们新建一个分片执行器
xxl-job-sharding-executor
9.2.2 创建任务
我们新建一个任务,用于演示分片广播(记得勾选更改执行器)
0/5 * * * * ? *
shardingJobHandler
9.2.3 修改配置
修改 application.yml 文件中执行器的名字
xxl-job-sharding-executor
9.2.4 修改任务代码
在 HelloXxlJob 类中添加以下代码
- index:当前分片的序号(从0开始),执行器集群列表中当前执行器的序号
- total:总分片数,执行器集群的总机器数量
@XxlJob("shardingJobHandler")
public void shardingJobHandler() {
// 分片参数
int shardIndex = XxlJobHelper.getShardIndex();
int shardTotal = XxlJobHelper.getShardTotal();
// 业务逻辑
List<Integer> list = getList();
for (Integer integer : list) {
if (integer % shardTotal == shardIndex) {
System.out.println("当前第" + shardIndex + "分片执行了,任务项为:" + integer);
}
}
}
public List<Integer> getList() {
List<Integer> list = new ArrayList<>();
for (int i = 0; i < 10000; i++) {
list.add(i);
}
return list;
}
9.2.5 重启微服务
分片广播方式:实例 A 和 B 每秒同时接收 10000 个任务,但根据各自分片 id,实例 A 只会处理其中的 5000 个,跳过另外 5000 个,实例 B 同理
重启两个微服务,就可以在控制台中看到分片广播的效果了
10. 执行器无法注册到配置中心
参考我的另一篇博文:XXL-JOB执行任务的SpringBoot程序无法注册到调度中心
标签:执行器,SpringBoot,JOB,job,任务,XXL,xxl From: https://blog.csdn.net/m0_62128476/article/details/143891852