XXLJob是一个分布式任务调度平台,优点:开发迅速、学习简单、轻量级、易扩展。是大众点评员工xxl创建并维护,基于 GPL-3.0 开源,可放心商用,目前已经拥有庞大的使用群体。
简单来说,就是一个定时任务中间件,类似的产品有当当网开源的Elastic-Job。
由上可知,XXLJob为C/S架构,调度中心本身可以高可用部署,执行器集成在业务微服务中,当业务微服务多实例部署的时候,执行器也就可以达到分布式和高可用了。
调度中心依赖数据库,安装前需先初始化数据库,初始化脚本可从github中获取 https://github.com/xuxueli/xxl-job/tree/master/doc/db
表说明:
参见官方文档,本文重点放在SpringBoot整合XXLJob。调度中心的部署,尤其是高可用部署,后续单独开篇。
调度中心就是一个SpringBoot程序,以xxljob2.4.0版本为例,其依赖的SpringBoot版本为 2.7.9,所以任何启动SpringBoot 的方式都可以,webui的默认访问地址:http://ip:8080/xxl-job-admin
<dependency>
<groupId>com.xuxueli</groupId>
<artifactId>xxl-job-core</artifactId>
<version>2.4.0</version>
</dependency>
server:
port: 9009
logging:
level:
com.ramble: debug
xxl:
job:
admin:
#调度中心部署根地址 [选填]:如调度中心集群部署存在多个地址则用逗号分隔。执行器将会使用该地址进行"执行器心跳注册"和"任务结果回调";为空则关闭自动注册;
addresses: http://127.0.0.1:8080/xxl-job-admin
#执行器通讯TOKEN [选填]:非空时启用;
accessToken:
executor:
#执行器AppName [选填]:执行器心跳注册分组依据;为空则关闭自动注册
appname: xxljob-demo-service
#${spring.application.name}
#执行器注册 [选填]:优先使用该配置作为注册地址,为空时使用内嵌服务 ”IP:PORT“ 作为注册地址。从而更灵活的支持容器类型执行器动态IP和动态映射端口问题。
address: ""
#执行器IP [选填]:默认为空表示自动获取IP,多网卡时可手动设置指定IP,该IP不会绑定Host仅作为通讯实用;地址信息用于 "执行器注册" 和 "调度中心请求并触发任务";
ip: ""
#执行器端口号 [选填]:小于等于0则自动获取;默认端口为9999,单机部署多个执行器时,注意要配置不同执行器端口;
port: 0
###${server-port}
#执行器运行日志文件存储磁盘路径 [选填] :需要对该路径拥有读写权限;为空则使用默认路径;
logpath: ./logs/xxl-job/jobhandler
#执行器日志文件保存天数 [选填] : 过期日志自动清理, 限制值大于等于3时生效; 否则, 如-1, 关闭自动清理功能;
logretentiondays: 30
@Slf4j
@Configuration
public class XxlJobConfig {
@Value("${xxl.job.admin.addresses}")
private String adminAddresses;
@Value("${xxl.job.accessToken}")
private String accessToken;
@Value("${xxl.job.executor.appname}")
private String appname;
@Value("${xxl.job.executor.address}")
private String address;
@Value("${xxl.job.executor.ip}")
private String ip;
@Value("${xxl.job.executor.port}")
private int port;
@Value("${xxl.job.executor.logpath}")
private String logPath;
@Value("${xxl.job.executor.logretentiondays}")
private int logRetentionDays;
@Bean
public XxlJobSpringExecutor xxlJobExecutor() {
log.info(">>>>>>>>>>> start xxl-job config init");
XxlJobSpringExecutor xxlJobSpringExecutor = new XxlJobSpringExecutor();
xxlJobSpringExecutor.setAdminAddresses(adminAddresses);
xxlJobSpringExecutor.setAppname(appname);
xxlJobSpringExecutor.setAddress(address);
xxlJobSpringExecutor.setIp(ip);
xxlJobSpringExecutor.setPort(port);
xxlJobSpringExecutor.setAccessToken(accessToken);
xxlJobSpringExecutor.setLogPath(logPath);
xxlJobSpringExecutor.setLogRetentionDays(logRetentionDays);
return xxlJobSpringExecutor;
}
}
如果一切顺利,将在控制台看到如下输出:
2023-10-09 11:36:23.162 INFO 15736 --- [ Thread-4] com.xxl.job.core.server.EmbedServer : >>>>>>>>>>> xxl-job remoting server start success, nettype = class com.xxl.job.core.server.EmbedServer, port = 9999
看到这个,说明执行器配置已生效,执行器已经顺利和调度中心联系上了。
可以简单的将任务分两个步骤,第一在执行器中定义一个任务具体需要干什么,第二在调度中心触发定义的任务
在业务微服务中创建
@Slf4j
@Component
public class DemoJob {
/**
* 简单的job,调度器
*/
@XxlJob("job1")
public void job1() {
log.debug("do job1");
}
}
创建成功之后可以在执行器列表看到。图片为编辑页面,所以可以看到已经有机器地址了。
新增完毕之后启动,如果一切顺利,将可以在业务微服务中看到如下log:
2023-10-09 11:50:33.050 DEBUG 34868 --- [6-1696823413294] com.ramble.xxljob.task.DemoJob : do job1
2023-10-09 11:50:38.044 DEBUG 34868 --- [6-1696823413294] com.ramble.xxljob.task.DemoJob : do job1
2023-10-09 11:50:44.100 DEBUG 34868 --- [6-1696823413294] com.ramble.xxljob.task.DemoJob : do job1
2023-10-09 11:50:48.052 DEBUG 34868 --- [6-1696823413294] com.ramble.xxljob.task.DemoJob : do job1
2023-10-09 11:50:53.043 DEBUG 34868 --- [6-1696823413294] com.ramble.xxljob.task.DemoJob : do job1
每5s执行了一次任务
XxlJob注解有三个参数:
value:JobHandler的名称,需要在执行器和调度中心保持一致
init:定时任务前置处理,仅在定时任务首次运行前执行一次
destory:定时任务后置处理,仅在定时任务销毁的时候执行一次
这里需要注意:
@Slf4j
@Component
public class DemoJob {
/**
* 创建带前(后)置处理的任务
* Job方法添加注解 "@XxlJob(value="自定义jobhandler名称", init = "JobHandler初始化方法", destroy = "JobHandler销毁方法")",注解value值对应的是调度中心新建任务的JobHandler属性的值。
* <p>
* 执行日志:需要通过 "XxlJobHelper.log" 打印执行日志;
* <p>
* 任务结果:默认任务结果为 "成功" 状态,不需要主动设置;如有诉求,比如设置任务结果为失败,可以通过 "XxlJobHelper.handleFail/handleSuccess" 自主设置任务结果;
*/
@XxlJob(value = "job2", init = "job2Init", destroy = "job2Destroy")
public void job2() throws InterruptedException {
LocalDateTime now = LocalDateTime.now();
XxlJobHelper.log("进入job2,time={}", now.toString());
log.debug("job2 - doSomething ...");
Thread.sleep(2000);
XxlJobHelper.log("离开job2,time={}", now.toString());
}
public void job2Init() {
log.debug("job2Init - doSomething ...");
}
public void job2Destroy() {
log.debug("job2Destroy - doSomething ...");
}
}
调度中心需要创建对应job2的任务并启动。如果一切顺利将在执行器控制台看到如下log:
2023-10-09 13:31:44.104 DEBUG 35848 --- [7-1696829504104] com.ramble.xxljob.task.DemoJob : job2Init - doSomething ...
2023-10-09 13:31:44.110 DEBUG 35848 --- [7-1696829504104] com.ramble.xxljob.task.DemoJob : job2 - doSomething ...
2023-10-09 13:31:54.052 DEBUG 35848 --- [7-1696829504104] com.ramble.xxljob.task.DemoJob : job2 - doSomething ...
2023-10-09 13:32:04.053 DEBUG 35848 --- [7-1696829504104] com.ramble.xxljob.task.DemoJob : job2 - doSomething ...
2023-10-09 13:32:14.054 DEBUG 35848 --- [7-1696829504104] com.ramble.xxljob.task.DemoJob : job2 - doSomething ...
Disconnected from the target VM, address: '127.0.0.1:50819', transport: 'socket'
2023-10-09 13:32:21.606 INFO 35848 --- [ Thread-4] com.xxl.job.core.server.EmbedServer : >>>>>>>>>>> xxl-job remoting server stop.
2023-10-09 13:32:21.618 INFO 35848 --- [rRegistryThread] c.x.j.c.thread.ExecutorRegistryThread : >>>>>>>>>>> xxl-job registry-remove success, registryParam:RegistryParam{registryGroup='EXECUTOR', registryKey='xxljob-demo-service', registryValue='http://192.168.3.191:9999/'}, registryResult:ReturnT [code=200, msg=null, content=null]
2023-10-09 13:32:21.618 INFO 35848 --- [rRegistryThread] c.x.j.c.thread.ExecutorRegistryThread : >>>>>>>>>>> xxl-job, executor registry thread destroy.
2023-10-09 13:32:21.621 INFO 35848 --- [ionShutdownHook] com.xxl.job.core.server.EmbedServer : >>>>>>>>>>> xxl-job remoting server destroy success.
2023-10-09 13:32:21.622 DEBUG 35848 --- [7-1696829504104] com.ramble.xxljob.task.DemoJob : job2Destroy - doSomething ...
2023-10-09 13:32:21.622 INFO 35848 --- [7-1696829504104] com.xxl.job.core.thread.JobThread : >>>>>>>>>>> xxl-job JobThread stoped, hashCode:Thread[xxl-job, JobThread-27-1696829504104,10,main]
2023-10-09 13:32:21.623 INFO 35848 --- [FileCleanThread] c.x.j.core.thread.JobLogFileCleanThread : >>>>>>>>>>> xxl-job, executor JobLogFileCleanThread thread destroy.
通过log可以观测到:
当两个任务需要关联触发的时候可以使用父子任务的功能,当然了子任务还可以有子任务。
这种情况只需要启动父任务,不需要启动子任务,当父任务执行成功了,会触发子任务的启动。当父任务执行失败了,不会触发子任务的启动。
/**
* 父任务
*/
@XxlJob("jobFather")
public void jobFather() {
// 创建一个新的随机数生成器
Random random = new Random();
// 生成一个0到100之间的随机整数
int randomNumber = random.nextInt(101);
if (randomNumber % 2 == 0) {
log.debug("do - jobFather - success");
XxlJobHelper.handleSuccess();
} else {
log.debug("do - jobFather - fail");
XxlJobHelper.handleFail("调用XxlJobHelper.handleFail,调度中心就任务此任务执行失败");
}
}
/**
* 子任务
*/
@XxlJob("jobChild")
public void jobChild() {
log.debug("do - jobChild");
}
2023-10-10 09:13:00.276 INFO 19228 --- [ Thread-4] com.xxl.job.core.server.EmbedServer : >>>>>>>>>>> xxl-job remoting server start success, nettype = class com.xxl.job.core.server.EmbedServer, port = 9999
2023-10-10 09:13:23.549 INFO 19228 --- [Pool-1699379094] c.xxl.job.core.executor.XxlJobExecutor : >>>>>>>>>>> xxl-job regist JobThread success, jobId:29, handler:com.xxl.job.core.handler.impl.MethodJobHandler@2b43f314[class com.ramble.xxljob.task.DemoJob#jobFather]
2023-10-10 09:13:23.552 DEBUG 19228 --- [9-1696900403549] com.ramble.xxljob.task.DemoJob : do - jobFather - fail
2023-10-10 09:13:28.495 DEBUG 19228 --- [9-1696900403549] com.ramble.xxljob.task.DemoJob : do - jobFather - fail
2023-10-10 09:13:34.500 DEBUG 19228 --- [9-1696900403549] com.ramble.xxljob.task.DemoJob : do - jobFather - success
2023-10-10 09:13:34.511 INFO 19228 --- [Pool-1699379094] c.xxl.job.core.executor.XxlJobExecutor : >>>>>>>>>>> xxl-job regist JobThread success, jobId:30, handler:com.xxl.job.core.handler.impl.MethodJobHandler@7e3d2ebd[class com.ramble.xxljob.task.DemoJob#jobChild]
2023-10-10 09:13:34.512 DEBUG 19228 --- [0-1696900414511] com.ramble.xxljob.task.DemoJob : do - jobChild
2023-10-10 09:13:38.494 DEBUG 19228 --- [9-1696900403549] com.ramble.xxljob.task.DemoJob : do - jobFather - fail
2023-10-10 09:13:43.526 DEBUG 19228 --- [9-1696900403549] com.ramble.xxljob.task.DemoJob : do - jobFather - success
2023-10-10 09:13:43.539 DEBUG 19228 --- [0-1696900414511] com.ramble.xxljob.task.DemoJob : do - jobChild
通过日志可以观察到:
任务需要绑定到执行器,任务触发调度时将会自动发现注册成功的执行器, 实现任务自动发现功能; 另一方面也可以方便的进行任务分组。每个任务必须绑定一个执行器, 可在 "执行器管理" 进行设置
当执行器集群部署时,提供丰富的路由策略,包括:
调度过于密集执行器来不及处理时的处理策略
今天给大家分享一个SpringBoot整合Tess4j库实现图片文字识别的小案例,希望xdm喜欢。文末有案例代码的Git地址,可以自己下载了去玩玩儿或继续扩展也行。