示例代码对应仓库:lab-28-task-xxl-job 。
虽然说,Quartz 的功能,已经能够满足我们对定时任务的诉求,但是距离生产可用、好用,还是有一定的距离。在最早开始实习的时候,因为Quartz 只提供了任务调度的功能,不提供管理任务的管理与监控控制台,需要自己去做二次封装。当时,因为社区中找不到合适的实现这块功能的开源项目,所以我们就自己进行了简单的封装,满足我们的管理与监控的需求。
不过现在呢,开源社区中已经有了很多优秀的调度任务中间件。其中,比较有代表性的就是 XXL-JOB 。其对自己的定义如下:
XXL-JOB 是一个轻量级分布式任务调度平台,其核心设计目标是开发迅速、学习简单、轻量级、易扩展。
对于 XXL-JOB 的入门,已经在 《芋道 XXL-JOB 极简入门》 中编写,胖友先跳转到该文章阅读。重点是,要先搭建一个 XXL-JOB 调度中心。😈 因为,本文我们是来在 Spring Boot 项目中,实现一个 XXL-JOB 执行器。
引入依赖
在 pom.xml 文件中,引入相关依赖。
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.1.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>lab-28-task-xxl-job</artifactId>
<dependencies>
<!-- 实现对 Spring MVC 的自动化配置 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- XXL-JOB 相关依赖 -->
<dependency>
<groupId>com.xuxueli</groupId>
<artifactId>xxl-job-core</artifactId>
<version>2.1.1</version>
</dependency>
</dependencies>
</project>
具体每个依赖的作用,胖友自己认真看下添加的所有注释噢。比较可惜的是,目前 XXL-JOB 官方并未提供 Spring Boot Starter 包,略微有点尴尬。不过,社区已经有人在提交 Pull Request 了,详细可见 https://github.com/xuxueli/xxl-job/pull/820 。
应用配置文件
在 application.yml 中,添加 Quartz 的配置,如下:
server:
port: 9090 #指定一个端口,避免和 XXL-JOB 调度中心的端口冲突。仅仅测试之用
xxl-job
xxl:
job:
admin:
addresses: http://127.0.0.1:8080/xxl-job-admin # 调度中心部署跟地址 [选填]:如调度中心集群部署存在多个地址则用逗号分隔。执行器将会使用该地址进行"执行器心跳注册"和"任务结果回调";为空则关闭自动注册;
executor:
appname: lab-28-executor # 执行器 AppName [选填]:执行器心跳注册分组依据;为空则关闭自动注册
ip: # 执行器IP [选填]:默认为空表示自动获取IP,多网卡时可手动设置指定IP,该IP不会绑定Host仅作为通讯实用;地址信息用于 "执行器注册" 和 "调度中心请求并触发任务";
port: 6666 # ## 执行器端口号 [选填]:小于等于0则自动获取;默认端口为9999,单机部署多个执行器时,注意要配置不同执行器端口;
logpath: /Users/yunai/logs/xxl-job/lab-28-executor # 执行器运行日志文件存储磁盘路径 [选填] :需要对该路径拥有读写权限;为空则使用默认路径;
logretentiondays: 30 # 执行器日志文件保存天数 [选填] : 过期日志自动清理, 限制值大于等于3时生效; 否则, 如-1, 关闭自动清理功能;
accessToken: yudaoyuanma # 执行器通讯TOKEN [选填]:非空时启用;
具体每个参数的作用,胖友自己看下详细的注释哈。
XxlJobConfiguration
在 cn.iocoder.springboot.lab28.task.config 包路径下,创建 DataSourceConfiguration 类,配置 XXL-JOB 执行器。代码如下:
// XxlJobConfiguration.java
@Configuration
public class XxlJobConfiguration {
@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}")
private int port;
@Value("${xxl.job.accessToken}")
private String accessToken;
@Value("${xxl.job.executor.logpath}")
private String logPath;
@Value("${xxl.job.executor.logretentiondays}")
private int logRetentionDays;
@Bean
public XxlJobSpringExecutor xxlJobExecutor() {
// 创建 XxlJobSpringExecutor 执行器
XxlJobSpringExecutor xxlJobSpringExecutor = new XxlJobSpringExecutor();
xxlJobSpringExecutor.setAdminAddresses(adminAddresses);
xxlJobSpringExecutor.setAppName(appName);
xxlJobSpringExecutor.setIp(ip);
xxlJobSpringExecutor.setPort(port);
xxlJobSpringExecutor.setAccessToken(accessToken);
xxlJobSpringExecutor.setLogPath(logPath);
xxlJobSpringExecutor.setLogRetentionDays(logRetentionDays);
// 返回
return xxlJobSpringExecutor;
}
}
在 #xxlJobExecutor() 方法,创建了 Spring 容器下的 XXL-JOB 执行器 Bean 对象。要注意,方法上添加了的 @Bean 注解,配置了启动和销毁方法。
DemoJob
在 cn.iocoder.springboot.lab28.task.job 包路径下,创建 DemoJob 类,示例定时任务类。代码如下:
// DemoJob.java
@Component
@JobHandler("demoJob")
public class DemoJob extends IJobHandler {
private Logger logger = LoggerFactory.getLogger(getClass());
private final AtomicInteger counts = new AtomicInteger();
@Override
public ReturnT<String> execute(String param) throws Exception {
// 打印日志
logger.info("[execute][定时第 ({}) 次执行]", counts.incrementAndGet());
// 返回执行成功
return ReturnT.SUCCESS;
}
}
继承 XXL-JOB IJobHandler 抽象类,通过实现 #execute(String param) 方法,从而实现定时任务的逻辑。
在方法上,添加 @JobHandler 注解,设置 JobHandler 的名字。后续,我们在调度中心的控制台中,新增任务时,需要使用到这个名字。
#execute(String param) 方法的返回结果,为 ReturnT 类型。当返回值符合 “ReturnT.code == ReturnT.SUCCESS_CODE” 时表示任务执行成功,否则表示任务执行失败,而且可以通过 “ReturnT.msg” 回调错误信息给调度中心;从而,在任务逻辑中可以方便的控制任务执行结果。
#execute(String param) 方法的方法参数,为调度中心的控制台中,新增任务时,配置的“任务参数”。一般情况下,不会使用到。
Application
创建 Application.java 类,配置 @SpringBootApplication 注解即可。代码如下:
// Application.java
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
运行 Application 类,启动示例项目。输出日志精简如下:
XXL-JOB 启动日志
2019-11-29 00:58:42.429 INFO 46957 --- [ main] c.xxl.job.core.executor.XxlJobExecutor : >>>>>>>>>>> xxl-job register jobhandler success, name:demoJob, jobHandler:cn.iocoder.springboot.lab28.task.job.DemoJob@3af9aa66
2019-11-29 00:58:42.451 INFO 46957 --- [ main] c.x.r.r.provider.XxlRpcProviderFactory : >>>>>>>>>>> xxl-rpc, provider factory add service success. serviceKey = com.xxl.job.core.biz.ExecutorBiz, serviceBean = class com.xxl.job.core.biz.impl.ExecutorBizImpl
2019-11-29 00:58:42.454 INFO 46957 --- [ main] c.x.r.r.provider.XxlRpcProviderFactory : >>>>>>>>>>> xxl-rpc, provider factory add service success. serviceKey = com.xxl.job.core.biz.ExecutorBiz, serviceBean = class com.xxl.job.core.biz.impl.ExecutorBizImpl
2019-11-29 00:58:42.565 INFO 46957 --- [ main] o.s.s.concurrent.ThreadPoolTaskExecutor : Initializing ExecutorService 'applicationTaskExecutor'
2019-11-29 00:58:42.629 INFO 46957 --- [ Thread-7] com.xxl.rpc.remoting.net.Server : >>>>>>>>>>> xxl-rpc remoting server start success, nettype = com.xxl.rpc.remoting.net.impl.netty_http.server.NettyHttpServer, port = 6666
此时,因为我们并未在 XXL-JOB 调度中心进行相关的配置,所以 DemoJob 并不会执行。下面,让我们在 XXL-JOB 调度中心进行相应的配置。
新增执行器
浏览器打开 http://127.0.0.1:8080/xxl-job-admin/jobgroup 地址,即「执行器管理」菜单。如下图:
点击「新增执行器」按钮,弹出「新增执行器」界面。如下图:
填写完 "lab-28-executor" 执行器的信息,点击「保存」按钮,进行保存。耐心等待一会,执行器会自动注册上来。如下图:
执行器列表中显示在线的执行器列表, 可通过 "OnLine 机器" 查看对应执行器的集群机器。
相同执行器,有且仅需配置一次即可。
新建任务
浏览器打开 http://127.0.0.1:8080/xxl-job-admin/jobinfo 地址,即「任务管理」菜单。如下图:
点击最右边的「新增」按钮,弹出「新增」界面。如下图:
填写完 "demoJob" 任务的信息,点击「保存」按钮,进行保存。如下图:
点击 "demoJob" 任务的「操作」按钮,选择「启动」,确认后,该 "demoJob" 任务的状态就变成了 RUNNING 。如下图:
此时,我们打开执行器的 IDE 界面,可以看到 DemoJob 已经在每分钟执行一次了。日志如下:
2019-11-29 01:30:00.161 INFO 48374 --- [ Thread-18] c.i.springboot.lab28.task.job.DemoJob : [execute][定时第 (1) 次执行]
2019-11-29 01:31:00.012 INFO 48374 --- [ Thread-18] c.i.springboot.lab28.task.job.DemoJob : [execute][定时第 (2) 次执行]
2019-11-29 01:32:00.009 INFO 48374 --- [ Thread-18] c.i.springboot.lab28.task.job.DemoJob : [execute][定时第 (3) 次执行]
2019-11-29 01:33:00.010 INFO 48374 --- [ Thread-18] c.i.springboot.lab28.task.job.DemoJob : [execute][定时第 (4) 次执行]
2019-11-29 01:34:00.005 INFO 48374 --- [ Thread-18] c.i.springboot.lab28.task.job.DemoJob : [execute][定时第 (5) 次执行]
并且,我们在调度中心的界面上,点击 "demoJob" 任务的「操作」按钮,选择「查询日志」,可以看到相应的调度日志。如下图:
至此,我们已经完成了 XXL-JOB 执行器的入门。