java

关注公众号 jb51net

关闭
首页 > 软件编程 > java > SpringBoot整合XXLJob

SpringBoot整合XXLJob的实现示例

作者:Naylor

XXLJob是大众点评开源的分布式任务调度平台,支持Cron、固定间隔等触发方式,本文主要介绍了SpringBoot整合XXLJob的实现示例,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧

XXLJob简介

XXLJob是一个分布式任务调度平台,优点:开发迅速、学习简单、轻量级、易扩展。是大众点评员工xxl创建并维护,基于 GPL-3.0 开源,可放心商用,目前已经拥有庞大的使用群体。

简单来说,就是一个定时任务中间件,类似的产品有当当网开源的Elastic-Job。

特性

模块

由上可知,XXLJob为C/S架构,调度中心本身可以高可用部署,执行器集成在业务微服务中,当业务微服务多实例部署的时候,执行器也就可以达到分布式和高可用了。

安装调度中心

初始化数据库

调度中心依赖数据库,安装前需先初始化数据库

表说明:

配置

参见官方文档,本文重点放在SpringBoot整合XXLJob。调度中心的部署,尤其是高可用部署,后续单独开篇。

启动

调度中心就是一个SpringBoot程序,以xxljob2.4.0版本为例,其依赖的SpringBoot版本为 2.7.9,所以任何启动SpringBoot 的方式都可以,webui的默认访问地址:http://ip:8080/xxl-job-admin 

整合执行器

pom

<dependency>   
    <groupId>com.xuxueli</groupId>   
    <artifactId>xxl-job-core</artifactId>  
    <version>2.4.0</version>
</dependency>

yml

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

XxlJobConfig

@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注解详解

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");
     }

关联父子任务

执行器侧log

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整合XXLJob的实现示例的文章就介绍到这了,更多相关SpringBoot整合XXLJob内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家! 

您可能感兴趣的文章:
阅读全文