Browse Source

add [重大更新]增加 ruoyi-job 任务调度模块(基于xxl-job)

疯狂的狮子li 3 years ago
parent
commit
aaa15b403a

+ 21 - 0
pom.xml

@@ -209,6 +209,13 @@
                 <version>${lock4j.version}</version>
             </dependency>
 
+            <!-- xxl-job-core -->
+            <dependency>
+                <groupId>com.xuxueli</groupId>
+                <artifactId>xxl-job-core</artifactId>
+                <version>${xxl-job-core-version}</version>
+            </dependency>
+
             <dependency>
                 <groupId>com.yomahub</groupId>
                 <artifactId>tlog-spring-boot-configuration</artifactId>
@@ -237,6 +244,12 @@
                 <version>${tlog.version}</version>
             </dependency>
 
+            <dependency>
+                <groupId>com.yomahub</groupId>
+                <artifactId>tlog-xxl-job</artifactId>
+                <version>${tlog.version}</version>
+            </dependency>
+
             <!-- 定时任务 @deprecated 3.5.0删除 迁移至xxl-job -->
             <dependency>
                 <groupId>com.ruoyi</groupId>
@@ -244,6 +257,13 @@
                 <version>${ruoyi-vue-plus.version}</version>
             </dependency>
 
+            <!-- 定时任务 -->
+            <dependency>
+                <groupId>com.ruoyi</groupId>
+                <artifactId>ruoyi-job</artifactId>
+                <version>${ruoyi-vue-plus.version}</version>
+            </dependency>
+
             <!-- 代码生成-->
             <dependency>
                 <groupId>com.ruoyi</groupId>
@@ -294,6 +314,7 @@
         <module>ruoyi-framework</module>
         <module>ruoyi-system</module>
         <module>ruoyi-quartz</module>
+        <module>ruoyi-job</module>
         <module>ruoyi-generator</module>
         <module>ruoyi-common</module>
         <module>ruoyi-demo</module>

+ 5 - 0
ruoyi-admin/pom.xml

@@ -47,6 +47,11 @@
             <artifactId>ruoyi-quartz</artifactId>
         </dependency>
 
+        <dependency>
+            <groupId>com.ruoyi</groupId>
+            <artifactId>ruoyi-job</artifactId>
+        </dependency>
+
         <dependency>
             <groupId>com.ruoyi</groupId>
             <artifactId>ruoyi-oss</artifactId>

+ 38 - 15
ruoyi-admin/src/main/resources/application-dev.yml

@@ -1,4 +1,41 @@
-# 数据源配置
+--- # 监控配置
+spring:
+  boot:
+    admin:
+      # Spring Boot Admin Client 客户端的相关配置
+      client:
+        # 增加客户端开关
+        enabled: true
+        # 设置 Spring Boot Admin Server 地址
+        url: http://localhost:9090/admin
+        instance:
+          prefer-ip: true # 注册实例时,优先使用 IP
+        username: ruoyi
+        password: 123456
+
+--- # xxl-job 配置
+xxl:
+  job:
+    # 调度中心地址:如调度中心集群部署存在多个地址则用逗号分隔。
+    admin-addresses: http://localhost:9100/xxl-job-admin
+    # 执行器通讯TOKEN:非空时启用
+    access-token: xxl-job
+    # 执行器配置
+    executor:
+      # 执行器AppName:执行器心跳注册分组依据;为空则关闭自动注册
+      appname: xxl-job-executor
+      # 执行器端口号 执行器从9101开始往后写
+      port: 9101
+      # 执行器注册:默认IP:PORT
+      address:
+      # 执行器IP:默认自动获取IP
+      ip:
+      # 执行器运行日志文件存储磁盘路径
+      logpath: ./logs/xxl-job
+      # 执行器日志文件保存天数:大于3生效
+      logretentiondays: 30
+
+--- # 数据源配置
 spring:
   datasource:
     type: com.alibaba.druid.pool.DruidDataSource
@@ -179,17 +216,3 @@ redisson:
 #    # 订阅模式
 #    subscriptionMode: "MASTER"
 
---- # 监控配置
-spring:
-  boot:
-    admin:
-      # Spring Boot Admin Client 客户端的相关配置
-      client:
-        # 增加客户端开关
-        enabled: true
-        # 设置 Spring Boot Admin Server 地址
-        url: http://localhost:9090/admin
-        instance:
-          prefer-ip: true # 注册实例时,优先使用 IP
-        username: ruoyi
-        password: 123456

+ 38 - 16
ruoyi-admin/src/main/resources/application-prod.yml

@@ -1,4 +1,41 @@
-# 数据源配置
+--- # 监控配置
+spring:
+  boot:
+    admin:
+      # Spring Boot Admin Client 客户端的相关配置
+      client:
+        # 增加客户端开关
+        enabled: true
+        # 设置 Spring Boot Admin Server 地址
+        url: http://172.30.0.90:9090/admin
+        instance:
+          prefer-ip: true # 注册实例时,优先使用 IP
+        username: ruoyi
+        password: 123456
+
+--- # xxl-job 配置
+xxl:
+  job:
+    # 调度中心地址:如调度中心集群部署存在多个地址则用逗号分隔。
+    admin-addresses: http://172.30.0.92:9100/xxl-job-admin
+    # 执行器通讯TOKEN:非空时启用
+    access-token: xxl-job
+    # 执行器配置
+    executor:
+      # 执行器AppName:执行器心跳注册分组依据;为空则关闭自动注册
+      appname: xxl-job-executor
+      # 执行器端口号 执行器从9101开始往后写
+      port: 9101
+      # 执行器注册:默认IP:PORT
+      address:
+      # 执行器IP:默认自动获取IP
+      ip:
+      # 执行器运行日志文件存储磁盘路径
+      logpath: ./logs/xxl-job
+      # 执行器日志文件保存天数:大于3生效
+      logretentiondays: 30
+
+--- # 数据源配置
 spring:
   datasource:
     type: com.alibaba.druid.pool.DruidDataSource
@@ -178,18 +215,3 @@ redisson:
 #    readMode: "SLAVE"
 #    # 订阅模式
 #    subscriptionMode: "MASTER"
-
---- # 监控配置
-spring:
-  boot:
-    admin:
-      # Spring Boot Admin Client 客户端的相关配置
-      client:
-        # 增加客户端开关
-        enabled: true
-        # 设置 Spring Boot Admin Server 地址
-        url: http://172.30.0.90:9090/admin
-        instance:
-          prefer-ip: true # 注册实例时,优先使用 IP
-        username: ruoyi
-        password: 123456

+ 0 - 1
ruoyi-extend/ruoyi-xxl-job-admin/pom.xml

@@ -75,7 +75,6 @@
 		<dependency>
 			<groupId>com.xuxueli</groupId>
 			<artifactId>xxl-job-core</artifactId>
-			<version>${xxl-job-core-version}</version>
 		</dependency>
 
 	</dependencies>

+ 39 - 0
ruoyi-job/pom.xml

@@ -0,0 +1,39 @@
+<?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>
+        <artifactId>ruoyi-vue-plus</artifactId>
+        <groupId>com.ruoyi</groupId>
+        <version>3.2.0</version>
+    </parent>
+    <modelVersion>4.0.0</modelVersion>
+    <packaging>jar</packaging>
+    <artifactId>ruoyi-job</artifactId>
+
+    <description>
+        任务调度
+    </description>
+
+    <dependencies>
+
+        <!-- 通用工具-->
+        <dependency>
+            <groupId>com.ruoyi</groupId>
+            <artifactId>ruoyi-common</artifactId>
+        </dependency>
+
+        <!-- xxl-job-core -->
+        <dependency>
+            <groupId>com.xuxueli</groupId>
+            <artifactId>xxl-job-core</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>com.yomahub</groupId>
+            <artifactId>tlog-xxl-job</artifactId>
+        </dependency>
+
+    </dependencies>
+
+</project>

+ 44 - 0
ruoyi-job/src/main/java/com/ruoyi/job/config/XxlJobConfig.java

@@ -0,0 +1,44 @@
+package com.ruoyi.job.config;
+
+import com.ruoyi.job.config.properties.XxlJobProperties;
+import com.xxl.job.core.executor.impl.XxlJobSpringExecutor;
+import com.yomahub.tlog.springboot.lifecircle.TLogXxljobEnhanceInit;
+import lombok.AllArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.boot.context.properties.EnableConfigurationProperties;
+import org.springframework.context.annotation.Bean;
+
+/**
+ * xxl-job config
+ *
+ * @author Lion Li
+ */
+@Slf4j
+@EnableConfigurationProperties(XxlJobProperties.class)
+@AllArgsConstructor
+public class XxlJobConfig {
+
+    private final XxlJobProperties xxlJobProperties;
+
+    @Bean
+    public XxlJobSpringExecutor xxlJobExecutor() {
+        log.info(">>>>>>>>>>> xxl-job config init.");
+        XxlJobSpringExecutor xxlJobSpringExecutor = new XxlJobSpringExecutor();
+        xxlJobSpringExecutor.setAdminAddresses(xxlJobProperties.getAdminAddresses());
+        xxlJobSpringExecutor.setAccessToken(xxlJobProperties.getAccessToken());
+        XxlJobProperties.Executor executor = xxlJobProperties.getExecutor();
+        xxlJobSpringExecutor.setAppname(executor.getAppname());
+        xxlJobSpringExecutor.setAddress(executor.getAddress());
+        xxlJobSpringExecutor.setIp(executor.getIp());
+        xxlJobSpringExecutor.setPort(executor.getPort());
+        xxlJobSpringExecutor.setLogPath(executor.getLogPath());
+        xxlJobSpringExecutor.setLogRetentionDays(executor.getLogRetentionDays());
+        return xxlJobSpringExecutor;
+    }
+
+    @Bean
+    public TLogXxljobEnhanceInit tLogXxljobEnhanceInit(){
+        return new TLogXxljobEnhanceInit();
+    }
+
+}

+ 38 - 0
ruoyi-job/src/main/java/com/ruoyi/job/config/properties/XxlJobProperties.java

@@ -0,0 +1,38 @@
+package com.ruoyi.job.config.properties;
+
+import lombok.Data;
+import lombok.NoArgsConstructor;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+
+/**
+ * xxljob配置类
+ *
+ * @author Lion Li
+ */
+@Data
+@ConfigurationProperties(prefix = "xxl.job")
+public class XxlJobProperties {
+
+    private String adminAddresses;
+
+    private String accessToken;
+
+    private Executor executor;
+
+    @Data
+    @NoArgsConstructor
+    public static class Executor {
+
+        private String appname;
+
+        private String address;
+
+        private String ip;
+
+        private int port;
+
+        private String logPath;
+
+        private int logRetentionDays;
+    }
+}

+ 254 - 0
ruoyi-job/src/main/java/com/ruoyi/job/service/SampleService.java

@@ -0,0 +1,254 @@
+package com.ruoyi.job.service;
+
+import com.xxl.job.core.context.XxlJobHelper;
+import com.xxl.job.core.handler.annotation.XxlJob;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Service;
+
+import java.io.BufferedInputStream;
+import java.io.BufferedReader;
+import java.io.DataOutputStream;
+import java.io.InputStreamReader;
+import java.net.HttpURLConnection;
+import java.net.URL;
+import java.util.Arrays;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * XxlJob开发示例(Bean模式)
+ * <p>
+ * 开发步骤:
+ * 1、任务开发:在Spring Bean实例中,开发Job方法;
+ * 2、注解配置:为Job方法添加注解 "@XxlJob(value="自定义jobhandler名称", init = "JobHandler初始化方法", destroy = "JobHandler销毁方法")",注解value值对应的是调度中心新建任务的JobHandler属性的值。
+ * 3、执行日志:需要通过 "XxlJobHelper.log" 打印执行日志;
+ * 4、任务结果:默认任务结果为 "成功" 状态,不需要主动设置;如有诉求,比如设置任务结果为失败,可以通过 "XxlJobHelper.handleFail/handleSuccess" 自主设置任务结果;
+ *
+ * @author xuxueli 2019-12-11 21:52:51
+ */
+@Slf4j
+@Service
+public class SampleService {
+
+
+    /**
+     * 1、简单任务示例(Bean模式)
+     */
+    @XxlJob("demoJobHandler")
+    public void demoJobHandler() throws Exception {
+        XxlJobHelper.log("XXL-JOB, Hello World.");
+
+        for (int i = 0; i < 5; i++) {
+            XxlJobHelper.log("beat at:" + i);
+            TimeUnit.SECONDS.sleep(2);
+        }
+        // default success
+    }
+
+
+    /**
+     * 2、分片广播任务
+     */
+    @XxlJob("shardingJobHandler")
+    public void shardingJobHandler() throws Exception {
+
+        // 分片参数
+        int shardIndex = XxlJobHelper.getShardIndex();
+        int shardTotal = XxlJobHelper.getShardTotal();
+
+        XxlJobHelper.log("分片参数:当前分片序号 = {}, 总分片数 = {}", shardIndex, shardTotal);
+
+        // 业务逻辑
+        for (int i = 0; i < shardTotal; i++) {
+            if (i == shardIndex) {
+                XxlJobHelper.log("第 {} 片, 命中分片开始处理", i);
+            } else {
+                XxlJobHelper.log("第 {} 片, 忽略", i);
+            }
+        }
+
+    }
+
+
+    /**
+     * 3、命令行任务
+     */
+    @XxlJob("commandJobHandler")
+    public void commandJobHandler() throws Exception {
+        String command = XxlJobHelper.getJobParam();
+        int exitValue = -1;
+
+        BufferedReader bufferedReader = null;
+        try {
+            // command process
+            ProcessBuilder processBuilder = new ProcessBuilder();
+            processBuilder.command(command);
+            processBuilder.redirectErrorStream(true);
+
+            Process process = processBuilder.start();
+            //Process process = Runtime.getRuntime().exec(command);
+
+            BufferedInputStream bufferedInputStream = new BufferedInputStream(process.getInputStream());
+            bufferedReader = new BufferedReader(new InputStreamReader(bufferedInputStream));
+
+            // command log
+            String line;
+            while ((line = bufferedReader.readLine()) != null) {
+                XxlJobHelper.log(line);
+            }
+
+            // command exit
+            process.waitFor();
+            exitValue = process.exitValue();
+        } catch (Exception e) {
+            XxlJobHelper.log(e);
+        } finally {
+            if (bufferedReader != null) {
+                bufferedReader.close();
+            }
+        }
+
+        if (exitValue == 0) {
+            // default success
+        } else {
+            XxlJobHelper.handleFail("command exit value(" + exitValue + ") is failed");
+        }
+
+    }
+
+
+    /**
+     * 4、跨平台Http任务
+     * 参数示例:
+     * "url: http://www.baidu.com\n" +
+     * "method: get\n" +
+     * "data: content\n";
+     */
+    @XxlJob("httpJobHandler")
+    public void httpJobHandler() throws Exception {
+
+        // param parse
+        String param = XxlJobHelper.getJobParam();
+        if (param == null || param.trim().length() == 0) {
+            XxlJobHelper.log("param[" + param + "] invalid.");
+
+            XxlJobHelper.handleFail();
+            return;
+        }
+
+        String[] httpParams = param.split("\n");
+        String url = null;
+        String method = null;
+        String data = null;
+        for (String httpParam : httpParams) {
+            if (httpParam.startsWith("url:")) {
+                url = httpParam.substring(httpParam.indexOf("url:") + 4).trim();
+            }
+            if (httpParam.startsWith("method:")) {
+                method = httpParam.substring(httpParam.indexOf("method:") + 7).trim().toUpperCase();
+            }
+            if (httpParam.startsWith("data:")) {
+                data = httpParam.substring(httpParam.indexOf("data:") + 5).trim();
+            }
+        }
+
+        // param valid
+        if (url == null || url.trim().length() == 0) {
+            XxlJobHelper.log("url[" + url + "] invalid.");
+
+            XxlJobHelper.handleFail();
+            return;
+        }
+        if (method == null || !Arrays.asList("GET", "POST").contains(method)) {
+            XxlJobHelper.log("method[" + method + "] invalid.");
+
+            XxlJobHelper.handleFail();
+            return;
+        }
+        boolean isPostMethod = method.equals("POST");
+
+        // request
+        HttpURLConnection connection = null;
+        BufferedReader bufferedReader = null;
+        try {
+            // connection
+            URL realUrl = new URL(url);
+            connection = (HttpURLConnection) realUrl.openConnection();
+
+            // connection setting
+            connection.setRequestMethod(method);
+            connection.setDoOutput(isPostMethod);
+            connection.setDoInput(true);
+            connection.setUseCaches(false);
+            connection.setReadTimeout(5 * 1000);
+            connection.setConnectTimeout(3 * 1000);
+            connection.setRequestProperty("connection", "Keep-Alive");
+            connection.setRequestProperty("Content-Type", "application/json;charset=UTF-8");
+            connection.setRequestProperty("Accept-Charset", "application/json;charset=UTF-8");
+
+            // do connection
+            connection.connect();
+
+            // data
+            if (isPostMethod && data != null && data.trim().length() > 0) {
+                DataOutputStream dataOutputStream = new DataOutputStream(connection.getOutputStream());
+                dataOutputStream.write(data.getBytes("UTF-8"));
+                dataOutputStream.flush();
+                dataOutputStream.close();
+            }
+
+            // valid StatusCode
+            int statusCode = connection.getResponseCode();
+            if (statusCode != 200) {
+                throw new RuntimeException("Http Request StatusCode(" + statusCode + ") Invalid.");
+            }
+
+            // result
+            bufferedReader = new BufferedReader(new InputStreamReader(connection.getInputStream(), "UTF-8"));
+            StringBuilder result = new StringBuilder();
+            String line;
+            while ((line = bufferedReader.readLine()) != null) {
+                result.append(line);
+            }
+            String responseMsg = result.toString();
+
+            XxlJobHelper.log(responseMsg);
+
+            return;
+        } catch (Exception e) {
+            XxlJobHelper.log(e);
+
+            XxlJobHelper.handleFail();
+            return;
+        } finally {
+            try {
+                if (bufferedReader != null) {
+                    bufferedReader.close();
+                }
+                if (connection != null) {
+                    connection.disconnect();
+                }
+            } catch (Exception e2) {
+                XxlJobHelper.log(e2);
+            }
+        }
+
+    }
+
+    /**
+     * 5、生命周期任务示例:任务初始化与销毁时,支持自定义相关逻辑;
+     */
+    @XxlJob(value = "demoJobHandler2", init = "init", destroy = "destroy")
+    public void demoJobHandler2() throws Exception {
+        XxlJobHelper.log("XXL-JOB, Hello World.");
+    }
+
+    public void init() {
+        log.info("init");
+    }
+
+    public void destroy() {
+        log.info("destory");
+    }
+
+
+}