分布式任务调度平台XXL-JOB

分手后的思念是犯贱 2022-01-31 04:11 531阅读 0赞

一. 简介

1. 概述

XXL-JOB是一个轻量级分布式任务调度平台,其核心设计目标是开发迅速、学习简单、轻量级、易扩展。现已开放源代码并接入多家公司线上产品线,开箱即用。

github地址:https://github.com/xuxueli/xxl-job

官方文档:http://www.xuxueli.com/xxl-job/#/

2. 架构图

watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L0FraXJhTmlja3k_size_16_color_FFFFFF_t_70

步骤:

① 部署:xxl-job-admin 作为注册中心

② 创建执行器(具体调度地址) 可以支持集群

③ 配置文件需要填写xxl-job注册中心地址

④ 每个具体执行job服务器需要创建一个netty连接端口号

⑤ 需要执行job的任务类,集成IJobHandler抽象类注册到job容器中

⑥ Execute方法中编写具体job任务

核心:

任务调度平台根据执行地址,转发到真实执行器。

3. 原理

watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L0FraXJhTmlja3k_size_16_color_FFFFFF_t_70 1

首先要搭建一个xxl-job平台,在平台创建一个执行器,创建执行器的时候,netty会帮你创建一个端口号,
执行器启动的时候,会把服务器信息注册到xxl-job平台。
在xxl-job平台创建任务管理,任务管理核心参数有执行器,JobHandler(对应代码中@JobHandler的value名称),Cron表达式
首先会在xxl-job平台先去触发定时job,会获取到执行器对应的实际服务器地址,然后使用xxl-job发送请求到实际的定时job任务地址执行,使用demoJobHandler名称查找对应的jvm服务器上JobHandler容器获取到类的信息,再使用反射机制进行执行。

二. 环境搭建

1. 首先搭建xxl-job-admin平台

下载官方demo,建好数据库,导入xxl-job-admin项目,在xxl-job-admin.properties修改几个配置,如数据源,邮箱,账号等

由于不是springboot项目,需要单独部署tomcat启动,我配的是8090端口,浏览器访问:http://localhost:8090/,默认用户名密码可以在xxl-job-admin.properties修改,登录成功后出现如下页面:

watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L0FraXJhTmlja3k_size_16_color_FFFFFF_t_70 2

接下来配置几个东西:

① 配置执行器:点击执行器管理 — 新增执行器 — 填写参数保存即可

(注册方式选择手动录入,机器地址为下面客户端springboot项目yml中的xxl.job.executor.port参数)

watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L0FraXJhTmlja3k_size_16_color_FFFFFF_t_70 3

② 新建任务:任务管理 — 新建任务 — 填写参数保存即可

(执行器选择上面创建的job-执行器,Cron表示每三秒执行一次,JobHandler即客户端springboot项目中@JobHandler的value)

watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L0FraXJhTmlja3k_size_16_color_FFFFFF_t_70 4

2. 搭建客户端(即实际中我们的项目)

这里以官方demo为例:xxl-job-executor-sample-springboot

配置:

  1. server.port=8082
  2. logging.config=classpath:logback.xml
  3. xxl.job.admin.addresses=http://127.0.0.1:8090/ #即xxl-job-admin的地址
  4. xxl.job.executor.appname=job-zhaobin #创建的AppName
  5. xxl.job.executor.ip=
  6. xxl.job.executor.port=9092 #执行器的执行地址端口号
  7. ### xxl-job, access token
  8. xxl.job.accessToken=
  9. ### xxl-job log path
  10. xxl.job.executor.logpath=/data/applogs/xxl-job/jobhandler
  11. ### xxl-job log retention days
  12. xxl.job.executor.logretentiondays=-1

编写核心定时job:

  1. package com.xxl.job.executor.service.jobhandler;
  2. import com.xxl.job.core.biz.model.ReturnT;
  3. import com.xxl.job.core.handler.IJobHandler;
  4. import com.xxl.job.core.handler.annotation.JobHandler;
  5. import com.xxl.job.core.log.XxlJobLogger;
  6. import org.springframework.beans.factory.annotation.Value;
  7. import org.springframework.stereotype.Component;
  8. import java.util.concurrent.TimeUnit;
  9. /**
  10. * 任务Handler示例(Bean模式)
  11. * 开发步骤: 1、继承"IJobHandler":“com.xxl.job.core.handler.IJobHandler”;
  12. * 2、注册到Spring容器:添加“@Component”注解,被Spring容器扫描为Bean实例;
  13. * 3、注册到执行器工厂:添加“@JobHandler(value="自定义jobhandler名称")”注解,
  14. * 注解value值对应的是调度中心新建任务的JobHandler属性的值。 4、执行日志:需要通过 "XxlJobLogger.log" 打印执行日志;
  15. */
  16. @JobHandler(value = "demoJobHandler")
  17. @Component
  18. public class DemoJobHandler extends IJobHandler {
  19. @Value("${xxl.job.executor.port}")
  20. private String executorPort;
  21. // @JobHandler 底层实现 value demoJobHandler 名称 对应存放类的class地址
  22. // com.xxl.job.executor.service.jobhandler 在使用反射机制 执行execute
  23. // 目的是为了反射统一执行方法
  24. @Override
  25. public ReturnT<String> execute(String param) throws Exception {
  26. // 任务调度执行地址
  27. System.out.println("####DemoJobHandler####execute()执行 executorPort:" + executorPort);
  28. return SUCCESS;
  29. }
  30. }

此时,启动xxl-job-executor-sample-springboot,会发现控制台会每隔3秒打印一次

2019051517101480.png

如想更改Cron规则,直接在xxl-job-admin的任务管理 - 编辑任务 - 更改Cron表达式即可。

拓展:任务管理有暂停和恢复的功能,必要时可以使用:

watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L0FraXJhTmlja3k_size_16_color_FFFFFF_t_70 5

3. 搭建执行器集群

模拟集群,同时启动两个xxl-job-executor-sample-springboot,yml如下:

  1. server.port=8081
  2. logging.config=classpath:logback.xml
  3. xxl.job.admin.addresses=http://127.0.0.1:8090/
  4. ### xxl-job executor address
  5. xxl.job.executor.appname=job-zhaobin
  6. xxl.job.executor.ip=
  7. xxl.job.executor.port=9091
  8. ### xxl-job, access token
  9. xxl.job.accessToken=
  10. ### xxl-job log path
  11. xxl.job.executor.logpath=/data/applogs/xxl-job/jobhandler
  12. ### xxl-job log retention days
  13. xxl.job.executor.logretentiondays=-1
  14. server.port=8082
  15. logging.config=classpath:logback.xml
  16. xxl.job.admin.addresses=http://127.0.0.1:8090/
  17. ### xxl-job executor address
  18. xxl.job.executor.appname=job-zhaobin
  19. xxl.job.executor.ip=
  20. xxl.job.executor.port=9092
  21. ### xxl-job, access token
  22. xxl.job.accessToken=
  23. ### xxl-job log path
  24. xxl.job.executor.logpath=/data/applogs/xxl-job/jobhandler
  25. ### xxl-job log retention days
  26. xxl.job.executor.logretentiondays=-1

② xxl-job-admin配置

编辑执行器,新增一个机器地址9092,逗号隔开

watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L0FraXJhTmlja3k_size_16_color_FFFFFF_t_70 6

任务管理,路由策略改为轮训:

watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L0FraXJhTmlja3k_size_16_color_FFFFFF_t_70 7

同时启动,可以看到8081和8082轮训执行!

三. 搭建xxl-job-admin集群并整合SpringBoot

1. xxl-job-admin宕机了怎么,会导致所有的任务无法执行,怎么解决? 没错,xxl-job-admin实现集群

调度中心支持集群部署,提升调度系统容灾和可用性。

调度中心集群部署时,几点要求和建议:(拷贝的官方文档)

  • DB配置保持一致;
  • 登陆账号配置保持一致;
  • 集群机器时钟保持一致(单机集群忽视);
  • 建议:推荐通过nginx为调度中心集群做负载均衡,分配域名。调度中心访问、执行器回调配置、调用API服务等操作均通过该域名进行。

watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L0FraXJhTmlja3k_size_16_color_FFFFFF_t_70 8

这里以Nginx为例,首先在本机host文件配置伪域名,然后配置nginx.conf如下:

  1. upstream backServer{
  2. server 127.0.0.1:8080 weight=1;
  3. server 127.0.0.1:8081 weight=1;
  4. }
  5. server {
  6. listen 80;
  7. server_name xxl-job-bin.com;
  8. #charset koi8-r;
  9. #access_log logs/host.access.log main;
  10. location / {
  11. proxy_pass http://backServer;
  12. index index.html index.htm;
  13. }
  14. error_page 500 502 503 504 /50x.html;
  15. location = /50x.html {
  16. root html;
  17. }
  18. }

启动两个xxl-job-admin,此时,先启动8080,再启动8081,浏览器访问:xxl-job-bin.com,执行任务。点击回复/执行

watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L0FraXJhTmlja3k_size_16_color_FFFFFF_t_70 9

会发现会一直走8081服务器,即后面启动的服务器(报错是因为找不到执行器,不用考虑):

watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L0FraXJhTmlja3k_size_16_color_FFFFFF_t_70 10

当关闭8081,则会走8080端口去执行(符合上面图中一主一备的说法)

2. 编写完成属于自己的SpringBoot项目

首先需要把官方的demo打到本地仓库(尤其是xxl-job-core)

然后复制官方demo的以下文件:

20190516232142232.png 20190516232158718.png

pom如下:

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  3. xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  4. <modelVersion>4.0.0</modelVersion>
  5. <!--<parent>-->
  6. <!--<groupId>org.springframework.boot</groupId>-->
  7. <!--<artifactId>spring-boot-starter-parent</artifactId>-->
  8. <!--<version>2.1.5.RELEASE</version>-->
  9. <!--<relativePath/> <!– lookup parent from repository –>-->
  10. <!--</parent>-->
  11. <groupId>com.example</groupId>
  12. <artifactId>springboot-xxl-job-demo</artifactId>
  13. <version>0.0.1-SNAPSHOT</version>
  14. <name>springboot-xxl-job-demo</name>
  15. <description>Demo project for Spring Boot</description>
  16. <properties>
  17. <javax.servlet-api.version>3.0.1</javax.servlet-api.version>
  18. <jsp-api.version>2.2</jsp-api.version>
  19. <spring.version>4.3.14.RELEASE</spring.version>
  20. <jackson.version>2.9.4</jackson.version>
  21. <aspectjweaver.version>1.8.13</aspectjweaver.version>
  22. <slf4j-api.version>1.7.25</slf4j-api.version>
  23. <freemarker.version>2.3.23</freemarker.version>
  24. <junit.version>4.12</junit.version>
  25. <jetty-server.version>9.2.24.v20180105</jetty-server.version>
  26. <hessian.version>4.0.51</hessian.version>
  27. <httpclient.version>4.5.5</httpclient.version>
  28. <commons-exec.version>1.3</commons-exec.version>
  29. <commons-collections4.version>4.1</commons-collections4.version>
  30. <commons-lang3.version>3.7</commons-lang3.version>
  31. <commons-email.version>1.5</commons-email.version>
  32. <c3p0.version>0.9.5.2</c3p0.version>
  33. <mysql-connector-java.version>5.1.45</mysql-connector-java.version>
  34. <mybatis-spring.version>1.3.1</mybatis-spring.version>
  35. <mybatis.version>3.4.5</mybatis.version>
  36. <groovy-all.version>2.4.13</groovy-all.version>
  37. <quartz.version>2.3.0</quartz.version>
  38. <spring-boot.version>1.5.10.RELEASE</spring-boot.version>
  39. <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
  40. <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
  41. <java.version>1.8</java.version>
  42. <project.parent.version>1.9.2-SNAPSHOT</project.parent.version>
  43. </properties>
  44. <dependencyManagement>
  45. <dependencies>
  46. <dependency>
  47. <!-- Import dependency management from Spring Boot (依赖管理:继承一些默认的依赖,工程需要依赖的jar包的管理,申明其他dependency的时候就不需要version) -->
  48. <groupId>org.springframework.boot</groupId>
  49. <artifactId>spring-boot-starter-parent</artifactId>
  50. <version>${spring-boot.version}</version>
  51. <type>pom</type>
  52. <scope>import</scope>
  53. </dependency>
  54. <!-- jetty -->
  55. <dependency>
  56. <groupId>org.eclipse.jetty</groupId>
  57. <artifactId>jetty-server</artifactId>
  58. <version>${jetty-server.version}</version>
  59. </dependency>
  60. <dependency>
  61. <groupId>org.eclipse.jetty</groupId>
  62. <artifactId>jetty-util</artifactId>
  63. <version>${jetty-server.version}</version>
  64. </dependency>
  65. <dependency>
  66. <groupId>org.eclipse.jetty</groupId>
  67. <artifactId>jetty-http</artifactId>
  68. <version>${jetty-server.version}</version>
  69. </dependency>
  70. <dependency>
  71. <groupId>org.eclipse.jetty</groupId>
  72. <artifactId>jetty-io</artifactId>
  73. <version>${jetty-server.version}</version>
  74. </dependency>
  75. </dependencies>
  76. </dependencyManagement>
  77. <dependencies>
  78. <!-- spring-boot-starter-web (spring-webmvc + tomcat) -->
  79. <dependency>
  80. <groupId>org.springframework.boot</groupId>
  81. <artifactId>spring-boot-starter-web</artifactId>
  82. </dependency>
  83. <dependency>
  84. <groupId>org.springframework.boot</groupId>
  85. <artifactId>spring-boot-starter-test</artifactId>
  86. <scope>test</scope>
  87. </dependency>
  88. <!-- xxl-job-core -->
  89. <dependency>
  90. <groupId>com.xuxueli</groupId>
  91. <artifactId>xxl-job-core</artifactId>
  92. <version>1.9.2-SNAPSHOT</version>
  93. </dependency>
  94. </dependencies>
  95. <build>
  96. <plugins>
  97. <plugin>
  98. <groupId>org.springframework.boot</groupId>
  99. <artifactId>spring-boot-maven-plugin</artifactId>
  100. </plugin>
  101. </plugins>
  102. </build>
  103. </project>

注意官方demo中不用parent的方式引入springboot版本,而是直接在一个 标签引入:





org.springframework.boot
spring-boot-starter-parent
${spring-boot.version}
pom
import

新建job:

  1. package com.example.job;
  2. import com.xxl.job.core.biz.model.ReturnT;
  3. import com.xxl.job.core.handler.IJobHandler;
  4. import com.xxl.job.core.handler.annotation.JobHandler;
  5. import org.springframework.beans.factory.annotation.Value;
  6. import org.springframework.stereotype.Component;
  7. @Component
  8. @JobHandler("myJob")
  9. public class MyJob extends IJobHandler {
  10. @Value("${server.port}")
  11. private String serverPort;
  12. @Override
  13. public ReturnT<String> execute(String param) throws Exception {
  14. System.out.println("xxl-job传递参数param:" + param);
  15. return SUCCESS;
  16. }
  17. }

注意:@JobHandler(“myJob”)的value要与任务管理的JobHandler一致

watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L0FraXJhTmlja3k_size_16_color_FFFFFF_t_70 11

application.peoperties配置如下:

  1. server.port=8082
  2. logging.config=classpath:logback.xml
  3. xxl.job.admin.addresses=http://127.0.0.1:8080/
  4. xxl.job.executor.appname=job-zhaobin
  5. xxl.job.executor.ip=
  6. xxl.job.executor.port=9091
  7. xxl.job.accessToken=
  8. xxl.job.executor.logpath=/data/applogs/xxl-job/jobhandler
  9. xxl.job.executor.logretentiondays=-1

注意:appname和xxl执行端口要和下图一致:

watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L0FraXJhTmlja3k_size_16_color_FFFFFF_t_70 12

此时,启动xxl-job-admin,启动springboot项目,修改参数分别为hahaha和xixixi,cron为2秒执行一次:

watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L0FraXJhTmlja3k_size_16_color_FFFFFF_t_70 13

可以看到后台打印日志:

watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L0FraXJhTmlja3k_size_16_color_FFFFFF_t_70 14

至此,分布式任务调度平台xxl-job正式搭建并整合完毕!

补充知识:

watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L0FraXJhTmlja3k_size_16_color_FFFFFF_t_70 15

  1. 常用的路由策略:

故障转移:会一直走第一个,当第一个挂了,会走第二个(即一主一备)

② 分片广播:第一个和第二个会一起执行(不推荐,job会重复执行)

③ 第一个和最后一个:会一直在第一个和最后一个执行

轮训:类似nginx轮训机制

  1. 如果第一次job没有执行完毕,第二次还会执行吗? 会执行

  2. 任务超时时间:任务在规定时间内没有完成的情况下,就会进行重试
    阻塞处理策略:选择单机串行即可

发表评论

表情:
评论列表 (有 0 条评论,531人围观)

还没有评论,来说两句吧...

相关阅读