借鉴大佬的,写这个博客只是记录一下快速使用XXL-JOB的步骤,方便自己日后查看,原文地址
1. 背景
随着系统规模的发展,定时任务数量日益增多,任务也变得越来越复杂,尤其是在分布式环境下,存在多个业务系统,每个业务系统都有定时任务的需求,如果都在自身系统中调度,一方面增加业务系统的复杂度,另一方面也不方便管理,因此需要有一个任务平台对分散的任务进行统一管理调度。
基于目前的情况,任务平台需要支持以下几个方面:
- 任务统一管理,提供图形化界面对任务进行配置和调度。
- 任务并发控制,同一个任务在同一时间只能允许一个执行。
- 任务弹性扩容,可根据繁忙情况动态增减服务器分摊压力,对大任务进行分片处理。
- 任务依赖问题,能够处理任务包含子任务的情况,前一个完成后触发子任务执行。
- 支持多类型的任务,支持Spring Bean、Shell等。
- 任务节点高可用,任务节点异常或者繁忙时能够转移到其他节点执行。
- 调度中心高可用,支持集群部署,避免出现单点故障。
- 执行状态监控,方便查看任务执行状态,异常情况告警,支持多渠道通知。
发展史:
定时任务随着技术发展,从单线程调度到多线程调度,从单机部署到集群部署,从独立执行到多任务协同执行。
第一阶段
单线程调度,在Java1.5之前,基于线程的等待(sleep或wait)机制定时执行,需要开发者实现调度逻辑,单个线程(Thread)处理单个任务有些浪费,但是一个线程(Timer)处理多个任务容易因为某个任务繁忙导致其他任务阻塞。
第二阶段
线程池调度,在Java1.5开始提供ScheduledExecutorService调度线程池,调度线程池支持固定的延时和固定间隔模式,对于需要在某天或者某月的时间点执行就不大方便,需要计算时间间隔,转换成启动延时和固定间隔,处理起来比较麻烦。
第三阶段
Spring任务调度,Spring简化了任务调度,通过@Scheduled注解支持将某个Bean的方法定时执行,除了支持固定延时和固定间隔模式外,还支持cron表达式,使得定时任务的开发变得极其简单。
第四阶段
Quartz任务调度,在任务服务集群部署下,Quartz通过数据库锁,实现任务的调度并发控制,避免同一个任务同时执行的情况。Quartz通过Scheduler提供了任务调度API,开发可以基于此开发自己的任务调度管理平台。
第五阶段
分布式任务平台,提供一个统一的平台,无需再去做和调度相关的开发,业务系统只需要实现具体的任务逻辑,自动注册到任务调度平台,在上面进行相关的配置就完成了定时任务的开发。
解决方案:
现在分布式下任务调度有很多解决方案,可以基于Quartz开发任务管理平台,也可以使用开源的任务调度平台,比如xxl-job,elastic-job。
xxl-job:
大众点评员工徐雪里于2015年发布的分布式任务调度平台,是一个轻量级分布式任务调度框架,其核心设计目标是开发迅速、学习简单、轻量级、易扩展。xxl-job官网地址
elastic-job:
当当开发的弹性分布式任务调度系统,功能丰富强大,采用zookeeper实现分布式协调,实现任务高可用以及分片,并且可以支持云开发,由两个相互独立的子项目Elastic-Job-Lite和Elastic-Job-Cloud组成。elastic-job官网地址
二者对比:
2. 简介
XXL-JOB是一个轻量级、分布式、易扩展的调度框架,主要功能是进行任务调度和管理。由中国人许雪里(Xuxueli)开发,因此得名XXL-JOB。xxl-job官网地址
XXL-JOB的主要特点如下:
- 简单易用:XXL-JOB提供了简单的操作界面,用户可以通过Web界面进行任务的管理和调度;
- 分布式调度:XXL-JOB支持分布式调度,能够在多个服务器上分配任务,提高任务处理的效率和可靠性;
- 弹性扩容缩容:在任务量增加时,可以动态地增加服务器来处理更多的任务;在任务量减少时,可以减少服务器以节省资源;
- 任务依赖:XXL-JOB支持设置任务之间的依赖关系,任务可以按照预定的顺序执行;
- 报警机制:XXL-JOB提供任务执行的报警机制,当任务执行失败或者执行超时时,可以通知任务负责人;
- 多种任务类型:支持常见的任务类型,如Java、Shell、Python等;
- 日志记录:XXL-JOB会记录任务的执行日志,方便用户追踪任务的执行情况;
- 高可用性:通过集群部署,保证系统的高可用性;
- …
xxl-job架构:
xxl-job通过将调度行为与任务执行解耦的设计思想,形成了两个核心组件:调度中心和执行器。
- 调度中心:负责统一管理任务的调度触发逻辑。它提供了一个可视化的操作界面,允许用户创建、编辑、启动、停止任务等。调度中心还负责监控任务的状态,处理任务执行结果,以及在任务执行异常时提供报警机制;
- 执行器:负责接收调度中心的任务请求,并执行具体的任务逻辑。执行器通常以分布式的方式部署在多个服务器上,可以根据任务量进行弹性伸缩。执行器会向调度中心注册自己,以便调度中心能够发现并分配任务;
- 数据库:存储任务配置、执行日志、调度记录等数据,保证调度中心与执行器之间的信息同步。支持多种关系型数据库,如MySQL、Oracle等。
3. 快速使用
xxl-job需要分成两个步骤:1.部署调度中心,2.业务系统对接(执行器)。
3.1 准备
下载:
要部署调度中心需要下载xxl-job的源码,将其打成jar包部署
Plain Text
目录结构
xxl-job-admin:调度中心
xxl-job-core:公共依赖
xxl-job-executor-samples:执行器Sample示例
:xxl-job-executor-sample-springboot:SpringBoot示例
:xxl-job-executor-sample-spring:spring示例
:xxl-job-executor-sample-frameless:无框架示例
中央仓库:
业务系统还需要引入xxl-job的依赖
XML
<!-- http://repo1.maven.org/maven2/com/xuxueli/xxl-job-core/ -->
<dependency>
<groupId>com.xuxueli</groupId>
<artifactId>xxl-job-core</artifactId>
<version>${最新稳定版本}</version>
</dependency>
环境:
- Maven3+
- JDK1.8+
- MySQL8.0+
3.2 部署调度中心
Windows:
-
初始化调度中心数据库:新建数据库xxl-job,执行/xxl-job/doc/db目录下的tables_xxl_job.sql
- 调度中心支持集群部署,集群情况下各节点务必连接同一个mysql实例;
- 如果mysql做主从,调度中心集群节点务必强制走主库;
-
修改xxl-job-admin中的配置文件
- 配置文件地址:/xxl-job/xxl-job-admin/src/main/resources/application.properties
- 根据实际修改,主要修改两部分,一个是数据库配置,另一个是报警邮箱
-
构建项目,打包启动
- 在xxl-job\xxl-job-admin目录下执行mvn clean package,默认打成了jar包,可在pom.xml中修改成war包,这里生成了jar
- 然后把xxl-job-admin/target目录下生成的jar包启动java -jar xxl-job-admin-*.jar
Linux:
#下载源码
cd /root/
wget https://github.com/xuxueli/xxl-job/archive/v2.0.1.zip
unzip v2.0.1.zip
mv xxl-job-2.0.1 xxl-job
#修改配置文件
vim /root/xxl-job/xxl-job-admin/src/main/resources/application.properties
## 修改mysql、邮件等配置
#编译
cd /root/xxl-job
mvn clean package
# 创建部署目录
mkdir -p /xxl-job
cd /root/xxl-job/xxl-job-admin/target/
cp /root/xxl-job/xxl-job-admin/target/xxl-job-admin-2.0.1.jar /xxl-job/xxl-job-admin-2.0.1.jar
#mysql 数据初始化(用户名和密码为root,数据库编码推荐使用utf8mb4)
mysql -u root -proot -e "source /root/xxl-job/doc/db/tables_xxl_job.sql;"
#启动
nohup java -jar /xxl-job/xxl-job-admin-2.3.1.jar > log.file 2>&1 &
#检查 admin 123456
curl http://localhost:8080/xxl-job-admin
调度中心访问地址:http://localhost:8080/xxl-job-admin (该地址执行器将会使用到,作为回调地址)
默认登录账号 “admin/123456”
3.3 业务系统接入
采用SpringBoot项目接入,可以借鉴示例项目:xxl-job-executor-samples/xxl-job-executor-sample-springboot
3.3.1 引入Maven依赖,同时也必须包含springboot-web依赖
<dependency>
<groupId>com.xuxueli</groupId>
<artifactId>xxl-job-core</artifactId>
<version>2.3.1</version>
</dependency>
3.3.2 配置文件yml配置
server:
#项目端口号
port: 8081
logging:
#日志文件
config: classpath:logback.xml
xxl:
job:
admin:
# 调度中心部署根地址:如调度中心集群部署存在多个地址则用逗号分隔。
# 执行器将会使用该地址进行"执行器心跳注册"和"任务结果回调"。这里端口对应xxl-job配置文件中端口
addresses: http://127.0.0.1:8080/xxl-job-admin
# 执行器通讯TOKEN [选填]:非空时启用;
accessToken:
#分别配置执行器的名称、ip地址、端口号
#注意:如果配置多个执行器时,防止端口冲突
executor:
appname: executorDemo
# 执行器注册 [选填]:优先使用该配置作为注册地址,为空时使用内嵌服务 ”IP:PORT“ 作为注册地址
address:
# 执行器IP [选填]:默认为空表示自动获取IP
ip:
# 执行器端口号 [选填]:小于等于0则自动获取;默认端口为9999
port: 9999
#执行器运行日志文件存储的磁盘位置,需要对该路径拥有读写权限
logpath: /data/applogs/xxl-job/jobhandler
#执行器Log文件定期清理功能,指定日志保存天数,日志文件过期自动删除。限制至少保持3天,否则功能不生效;
#-1表示永不删除
logretentiondays: -1
3.3.3 执行器组件配置
@Configuration
public class XxlJobConfig {
@Value("${spring.application.name:}")
private String springAppName;
@Value("${xxl.job.admin.addresses}")
private String adminAddresses;
@Value("${xxl.job.executor.appname:}")
private String appName;
@Value("${xxl.job.executor.ip:}")
private String ip;
@Value("${xxl.job.executor.port:9999}")
private int port;
@Value("${xxl.job.accessToken:}")
private String accessToken;
@Value("${xxl.job.executor.logpath:job-logs}")
private String logPath;
@Value("${xxl.job.executor.logretentiondays}")
private int logRetentionDays;
@Bean
public XxlJobSpringExecutor xxlJobExecutor() {
logger.info(">>>>>>>>>>> xxl-job config init.");
XxlJobSpringExecutor xxlJobSpringExecutor = new XxlJobSpringExecutor();
xxlJobSpringExecutor.setAdminAddresses(adminAddresses);
if (StringUtils.isEmpty(appName)) {
if (StringUtils.isEmpty(springAppName)) {
throw new IllegalStateException("missing xxl-job appname config");
}
appName = springAppName;
}
xxlJobSpringExecutor.setAppName(appName);
xxlJobSpringExecutor.setIp(ip);
xxlJobSpringExecutor.setPort(port);
xxlJobSpringExecutor.setAccessToken(accessToken);
xxlJobSpringExecutor.setLogPath(logPath);
xxlJobSpringExecutor.setLogRetentionDays(logRetentionDays);
logger.info(">>>>>>>>>>> xxl-job config end.");
return xxlJobSpringExecutor;
}
}
3.3.4 定时任务
-
在2.1.2之前版本每个任务需要单独开发一个Bean,实现IJobHandler接口,并且在类上加@JobHandler注解,这种方式较麻烦,推荐使用下面一种方式
@JobHandler(value="demoJobHandler") @Component public class DemoJobHandler extends IJobHandler { @Override public ReturnT<String> execute(String param) throws Exception { XxlJobLogger.log("XXL-JOB, Hello World."); for (int i = 0; i < 5; i++) { XxlJobLogger.log("beat at:" + i); TimeUnit.SECONDS.sleep(2); } return SUCCESS; } }
-
在2.1.2或者之后版本可以直接在方法上加@XxlJob来声明任务
@Component public class SampleXxlJob { private static Logger logger = LoggerFactory.getLogger(SampleXxlJob.class); /** * 1、简单任务示例(Bean模式) */ @XxlJob("demoJobHandler") public ReturnT<String> demoJobHandler(String param) throws Exception { XxlJobLogger.log("XXL-JOB, Hello World."); for (int i = 0; i < 5; i++) { XxlJobLogger.log("beat at:" + i); TimeUnit.SECONDS.sleep(2); } return ReturnT.SUCCESS; } /** * 2、分片广播任务 */ @XxlJob("shardingJobHandler") public ReturnT<String> shardingJobHandler(String param) throws Exception { // 分片参数 ShardingUtil.ShardingVO shardingVO = ShardingUtil.getShardingVo(); XxlJobLogger.log("分片参数:当前分片序号 = {}, 总分片数 = {}", shardingVO.getIndex(), shardingVO.getTotal()); // 业务逻辑 for (int i = 0; i < shardingVO.getTotal(); i++) { if (i == shardingVO.getIndex()) { XxlJobLogger.log("第 {} 片, 命中分片开始处理", i); } else { XxlJobLogger.log("第 {} 片, 忽略", i); } } return ReturnT.SUCCESS; } }
3.3.5 配置定时任务
-
配置定时任务,需要先配置执行器【执行器管理】,推荐使用自动注册方式,避免集群部署时还需要调整机器地址,添加界面如下(注意appname要和业务系统中配置一致)
-
添加完执行器后,添加任务【任务管理】,JobHandler要和代码中配置的名称一致,执行器集群部署可以通过配置路由方式来控制执行,xxl-job调度只支持cron表达式。
-
启动或者执行任务,查询执行日志、注册节点等