欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

SpringBoot集成Quartz實現(xiàn)持久化定時接口調用任務

 更新時間:2023年07月05日 10:22:49   作者:不瑤碧蓮  
Quartz是功能強大的開源作業(yè)調度庫,幾乎可以集成到任何?Java?應用程序中,從最小的獨立應用程序到最大的電子商務系統(tǒng),本文將通過代碼示例給大家介紹SpringBoot集成Quartz實現(xiàn)持久化定時接口調用任務,需要的朋友可以參考下

一、基本概念

Quartz 是功能強大的開源作業(yè)調度庫,幾乎可以集成到任何 Java 應用程序中,從最小的獨立應用程序到最大的電子商務系統(tǒng)。Quartz 可用于創(chuàng)建簡單或復雜的計劃,以執(zhí)行數(shù)以萬計的工作;可以執(zhí)行您編寫的所有內(nèi)容。

Spring Boot 官方也對 Quartz 調度器進行了集成,Spring boot 官網(wǎng)文檔:Quartz Scheduler,Java JDK 也帶有 計時器 Timer 以及 定時執(zhí)行服務 ScheduledExecutorService ,Spring 也提供了 @Scheduled 執(zhí)行定時任務。

如果實際環(huán)境中定時任務過多,處理頻繁,建議適應第三方封裝的調度框架,因為定時器操作底層都是多線程的操作,任務的啟動、暫停、恢復、刪除、實質是線程的啟動、暫停、中斷、喚醒等操作。

二、Quartz-scheduler 的核心流程

Scheduler - 調度器

1、Scheduler 用來對 Trigger 和 Job 進行管理,Trigger 和 JobDetail 可以注冊到 Scheduler 中,兩者在 Scheduler 中都擁有自己的唯一的組(group)和名稱(name)用來進行彼此的區(qū)分,Scheduler 可以通過任務組和名稱來對 Trigger 和 JobDetail 進行管理。

2、每個 Scheduler 都有一個 SchedulerContext,用來保存 Scheduler 的上下文數(shù)據(jù),Job 和 Trigger 都可以獲取其中的信息。

3、Scheduler 是由 SchedulerFactory 創(chuàng)建,它有兩個實現(xiàn):DirectSchedulerFactory 、StdSchdulerFactory ,前者可以用來在代碼里定制 Schduler 參數(shù),后者直接讀取 classpath 下的 quartz.properties(不存在就都使用默認值)配置來實例化 Scheduler。

Job - 任務

1、Job 是一個任務接口,開發(fā)者可以實現(xiàn)該接口定義自己的任務,JobExecutionContext 中提供了調度上下文的各種信息。

2、Job 中的任務有可能并發(fā)執(zhí)行,例如任務的執(zhí)行時間過長,而每次觸發(fā)的時間間隔太短,則會導致任務會被并發(fā)執(zhí)行。如果是并發(fā)執(zhí)行,就需要一個數(shù)據(jù)庫鎖去避免一個數(shù)據(jù)被多次處理??梢栽?execute()方法上添加 @DisallowConcurrentExecution 注解解決這個問題。

JobDetail - 任務詳情

1、JobDetail 對象是在將 job 注冊到 scheduler 時,由客戶端程序創(chuàng)建的,它包含 job 的各種屬性設置,以及用于存儲 job 實例狀態(tài)信息的 JobDataMap。

2、JobDetail 由 JobBuilder 創(chuàng)建/定義,Quartz 不存儲 Job 的實際實例,但是允許通過使用 JobDetail 定義一個實例。

3、Job 有一個與其關聯(lián)的名稱和組,應該在單個 Scheduler 中唯一標識它們。

4、一個 Trigger(觸發(fā)器) 只能對應一個 Job(任務),但是一個 Job 可以對應多個 Trigger。JobDetal 與 Trigger 一對多

Trigger - 觸發(fā)器

1、Trigger 用于觸發(fā) Job 的執(zhí)行。TriggerBuilder 用于定義/構建觸發(fā)器實例。

2、Trigger也有一個相關聯(lián)的 JobDataMap,用于給Job傳遞一些觸發(fā)相關的參數(shù)。

3、Quartz自帶了各種不同類型的 Trigger,最常用的主要是SimpleTrigger和CronTrigger。

JobDataMap

1、JobDataMap 實現(xiàn)了 JDK 的 Map 接口,可以以 Key-Value 的形式存儲數(shù)據(jù)。

2、JobDetail、Trigger 實現(xiàn)類中都定義 JobDataMap 成員變量及其 getter、setter 方法,可以用來設置參數(shù)信息,Job 執(zhí)行 execute() 方法的時候,JobExecutionContext 可以獲取到 JobDataMap 中的信息。

三、實踐

1、新建一個quartz-service服務

添加依賴:

<modelVersion>4.0.0</modelVersion>
    <groupId>org.example</groupId>
    <artifactId>quartz-servicer</artifactId>
    <version>1.0.0</version>
    <properties>
        <maven.compiler.source>11</maven.compiler.source>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <maven.compiler.target>11</maven.compiler.target>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
            <version>2.7.5</version>
        </dependency>
        <!-- 公共依賴 -->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>2.0.19</version>
        </dependency>
        <!-- quartz依賴 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-quartz</artifactId>
            <version>2.5.4</version>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>8.0.31</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-jdbc</artifactId>
            <version>2.7.3</version>
        </dependency>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid-spring-boot-starter</artifactId>
            <version>1.2.14</version>
            <exclusions>
                <exclusion>
                    <artifactId>spring-boot-autoconfigure</artifactId>
                    <groupId>org.springframework.boot</groupId>
                </exclusion>
            </exclusions>
        </dependency>
    </dependencies>
    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

2、配置數(shù)據(jù)源相關鏈接

quartz需要單據(jù)的數(shù)據(jù)庫,所以需要單據(jù)創(chuàng)建一個庫來給quartz使用,我新建了一個scheduler的庫

#1 保存已經(jīng)觸發(fā)的觸發(fā)器狀態(tài)信息
DROP TABLE IF EXISTS QRTZ_FIRED_TRIGGERS;
#2 存放暫停掉的觸發(fā)器表表
DROP TABLE IF EXISTS QRTZ_PAUSED_TRIGGER_GRPS;
#3 調度器狀態(tài)表
DROP TABLE IF EXISTS QRTZ_SCHEDULER_STATE;
#4 存儲程序的悲觀鎖的信息(假如使用了悲觀鎖)
DROP TABLE IF EXISTS QRTZ_LOCKS;
#5 簡單的觸發(fā)器表
DROP TABLE IF EXISTS QRTZ_SIMPLE_TRIGGERS;
#6 存儲兩種類型的觸發(fā)器表
DROP TABLE IF EXISTS QRTZ_SIMPROP_TRIGGERS;
#7 定時觸發(fā)器表
DROP TABLE IF EXISTS QRTZ_CRON_TRIGGERS;
#8 以blob 類型存儲的觸發(fā)器
DROP TABLE IF EXISTS QRTZ_BLOB_TRIGGERS;
#9 觸發(fā)器表
DROP TABLE IF EXISTS QRTZ_TRIGGERS;
#10 job 詳細信息表
DROP TABLE IF EXISTS QRTZ_JOB_DETAILS;
#11 日歷信息表
DROP TABLE IF EXISTS QRTZ_CALENDARS;
#job 詳細信息表
CREATE TABLE QRTZ_JOB_DETAILS
  (
    SCHED_NAME VARCHAR(120) NOT NULL,
    JOB_NAME  VARCHAR(200) NOT NULL,
    JOB_GROUP VARCHAR(200) NOT NULL,
    DESCRIPTION VARCHAR(250) NULL,
    JOB_CLASS_NAME   VARCHAR(250) NOT NULL,
    IS_DURABLE VARCHAR(1) NOT NULL,
    IS_NONCONCURRENT VARCHAR(1) NOT NULL,
    IS_UPDATE_DATA VARCHAR(1) NOT NULL,
    REQUESTS_RECOVERY VARCHAR(1) NOT NULL,
    JOB_DATA BLOB NULL,
    PRIMARY KEY (SCHED_NAME,JOB_NAME,JOB_GROUP)
);
#觸發(fā)器表
CREATE TABLE QRTZ_TRIGGERS
  (
    SCHED_NAME VARCHAR(120) NOT NULL,
    TRIGGER_NAME VARCHAR(200) NOT NULL,
    TRIGGER_GROUP VARCHAR(200) NOT NULL,
    JOB_NAME  VARCHAR(200) NOT NULL,
    JOB_GROUP VARCHAR(200) NOT NULL,
    DESCRIPTION VARCHAR(250) NULL,
    NEXT_FIRE_TIME BIGINT(13) NULL,
    PREV_FIRE_TIME BIGINT(13) NULL,
    PRIORITY INTEGER NULL,
    TRIGGER_STATE VARCHAR(16) NOT NULL,
    TRIGGER_TYPE VARCHAR(8) NOT NULL,
    START_TIME BIGINT(13) NOT NULL,
    END_TIME BIGINT(13) NULL,
    CALENDAR_NAME VARCHAR(200) NULL,
    MISFIRE_INSTR SMALLINT(2) NULL,
    JOB_DATA BLOB NULL,
    PRIMARY KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP),
    FOREIGN KEY (SCHED_NAME,JOB_NAME,JOB_GROUP)
        REFERENCES QRTZ_JOB_DETAILS(SCHED_NAME,JOB_NAME,JOB_GROUP)
);
#簡單的觸發(fā)器表,包括重復次數(shù),間隔,以及已觸發(fā)的次數(shù)
CREATE TABLE QRTZ_SIMPLE_TRIGGERS
  (
    SCHED_NAME VARCHAR(120) NOT NULL,
    TRIGGER_NAME VARCHAR(200) NOT NULL,
    TRIGGER_GROUP VARCHAR(200) NOT NULL,
    REPEAT_COUNT BIGINT(7) NOT NULL,
    REPEAT_INTERVAL BIGINT(12) NOT NULL,
    TIMES_TRIGGERED BIGINT(10) NOT NULL,
    PRIMARY KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP),
    FOREIGN KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP)
        REFERENCES QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP)
);
#定時觸發(fā)器表,存儲 cron trigger,包括 cron 表達式和時區(qū)信息
CREATE TABLE QRTZ_CRON_TRIGGERS
  (
    SCHED_NAME VARCHAR(120) NOT NULL,
    TRIGGER_NAME VARCHAR(200) NOT NULL,
    TRIGGER_GROUP VARCHAR(200) NOT NULL,
    CRON_EXPRESSION VARCHAR(200) NOT NULL,
    TIME_ZONE_ID VARCHAR(80),
    PRIMARY KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP),
    FOREIGN KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP)
        REFERENCES QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP)
);
#存儲calendarintervaltrigger和dailytimeintervaltrigger兩種類型的觸發(fā)器
CREATE TABLE QRTZ_SIMPROP_TRIGGERS
  (
    SCHED_NAME VARCHAR(120) NOT NULL,
    TRIGGER_NAME VARCHAR(200) NOT NULL,
    TRIGGER_GROUP VARCHAR(200) NOT NULL,
    STR_PROP_1 VARCHAR(512) NULL,
    STR_PROP_2 VARCHAR(512) NULL,
    STR_PROP_3 VARCHAR(512) NULL,
    INT_PROP_1 INT NULL,
    INT_PROP_2 INT NULL,
    LONG_PROP_1 BIGINT NULL,
    LONG_PROP_2 BIGINT NULL,
    DEC_PROP_1 NUMERIC(13,4) NULL,
    DEC_PROP_2 NUMERIC(13,4) NULL,
    BOOL_PROP_1 VARCHAR(1) NULL,
    BOOL_PROP_2 VARCHAR(1) NULL,
    PRIMARY KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP),
    FOREIGN KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP)
    REFERENCES QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP)
);
#以blob 類型存儲的觸發(fā)器
CREATE TABLE QRTZ_BLOB_TRIGGERS
  (
    SCHED_NAME VARCHAR(120) NOT NULL,
    TRIGGER_NAME VARCHAR(200) NOT NULL,
    TRIGGER_GROUP VARCHAR(200) NOT NULL,
    BLOB_DATA BLOB NULL,
    PRIMARY KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP),
    FOREIGN KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP)
        REFERENCES QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP)
);
#日歷信息表, quartz可配置一個日歷來指定一個時間范圍
CREATE TABLE QRTZ_CALENDARS
  (
    SCHED_NAME VARCHAR(120) NOT NULL,
    CALENDAR_NAME  VARCHAR(200) NOT NULL,
    CALENDAR BLOB NOT NULL,
    PRIMARY KEY (SCHED_NAME,CALENDAR_NAME)
);
#存放暫停掉的觸發(fā)器表表
CREATE TABLE QRTZ_PAUSED_TRIGGER_GRPS
  (
    SCHED_NAME VARCHAR(120) NOT NULL,
    TRIGGER_GROUP  VARCHAR(200) NOT NULL,
    PRIMARY KEY (SCHED_NAME,TRIGGER_GROUP)
);
# 存儲與已觸發(fā)的 trigger 相關的狀態(tài)信息,以及相聯(lián) job 的執(zhí)行信息
CREATE TABLE QRTZ_FIRED_TRIGGERS
  (
    SCHED_NAME VARCHAR(120) NOT NULL,
    ENTRY_ID VARCHAR(95) NOT NULL,
    TRIGGER_NAME VARCHAR(200) NOT NULL,
    TRIGGER_GROUP VARCHAR(200) NOT NULL,
    INSTANCE_NAME VARCHAR(200) NOT NULL,
    FIRED_TIME BIGINT(13) NOT NULL,
    SCHED_TIME BIGINT(13) NOT NULL,
    PRIORITY INTEGER NOT NULL,
    STATE VARCHAR(16) NOT NULL,
    JOB_NAME VARCHAR(200) NULL,
    JOB_GROUP VARCHAR(200) NULL,
    IS_NONCONCURRENT VARCHAR(1) NULL,
    REQUESTS_RECOVERY VARCHAR(1) NULL,
    PRIMARY KEY (SCHED_NAME,ENTRY_ID)
);
 3、 調度器狀態(tài)表
CREATE TABLE QRTZ_SCHEDULER_STATE
  (
    SCHED_NAME VARCHAR(120) NOT NULL,
    INSTANCE_NAME VARCHAR(200) NOT NULL,
    LAST_CHECKIN_TIME BIGINT(13) NOT NULL,
    CHECKIN_INTERVAL BIGINT(13) NOT NULL,
    PRIMARY KEY (SCHED_NAME,INSTANCE_NAME)
);
4、 存儲程序的悲觀鎖的信息(假如使用了悲觀鎖)
CREATE TABLE QRTZ_LOCKS
  (
    SCHED_NAME VARCHAR(120) NOT NULL,
    LOCK_NAME  VARCHAR(40) NOT NULL,
    PRIMARY KEY (SCHED_NAME,LOCK_NAME)
);

3、創(chuàng)建配置文件

Quartz 使用一個名為 quartz.properties 的屬性文件進行信息配置,必須位于 classpath 下,是 StdSchedulerFactory 用于創(chuàng)建 Scheduler 的默認屬性文件。默認情況下 StdSchedulerFactory 從類路徑下加載名為 “quartz.properties” 的屬性文件,如果失敗,則加載 org/quartz 包中的“quartz.properties”文件,因為我需要的是新建一個Scheduler服務,所以直接使用application.yml,配置如下:

datasource:
    url: jdbc:mysql://127.0.0.1:3306/scheduler
    username: ***
    password: ****
    driver-class-name: com.mysql.cj.jdbc.Driver
    type: com.alibaba.druid.pool.DruidDataSource
  #定時配置
  quartz:
    #相關屬性配置
    properties:
      org:
        quartz:
          scheduler:
            instanceName: local-scheduler-svc
            instanceId: AUTO
          jobStore:
            #表示 quartz 中的所有數(shù)據(jù),比如作業(yè)和觸發(fā)器的信息都保存在內(nèi)存中(而不是數(shù)據(jù)庫中)
            class: org.springframework.scheduling.quartz.LocalDataSourceJobStore
            # 驅動配置
            driverDelegateClass: org.quartz.impl.jdbcjobstore.StdJDBCDelegate
            # 表前綴
            tablePrefix: QRTZ_
            #是否為集群
            isClustered: false
            clusterCheckinInterval: 10000
            useProperties: false
            dataSource: quartzDs
          #線程池配置
          threadPool:
            class: org.quartz.simpl.SimpleThreadPool
            #線程數(shù)
            threadCount: 10
            #優(yōu)先級
            threadPriority: 5
            #線程繼承上下文類加載器的初始化線程
            threadsInheritContextClassLoaderOfInitializingThread: true
    #數(shù)據(jù)庫方式
    job-store-type: JDBC
    #初始化表結構
    jdbc:
      initialize-schema: NEVER

4、新建一個任務實體類JobInfo,用戶新建,傳遞任務信息

jobName:任務名稱

jobGroup:任務組

jsonParams:任務執(zhí)行信息(在用戶定時遠程調用接口的時候,我們可以接口信息封裝到這個Map中)

cron:定時任務的cron表達式

timeZoneId:定制執(zhí)行任務的時區(qū)

triggerTime:定時器時間(目前沒用上)

@Data
public class JobInfo {
    private String jobName;
    private String jobGroup;
    private Map<String, Object> jsonParams;
    private String cron;
    private String timeZoneId;
    private Date triggerTime; 
}

5、新建一個任務執(zhí)行類HttpRemoteJob 實現(xiàn) Job接口,重寫execute()方法

execute()里面就是任務的邏輯:

① 使用HttpURLConnection發(fā)送網(wǎng)絡請求,利用BufferedReader接收請求返回的結果

② 在任務的Description中取出定時請求的接口信息,解析Description,獲取請求url

③ 編輯請求信息,通過URL類編輯請求信息,使用HttpURLConnection發(fā)送請求,并接收請求返回的狀態(tài)碼,根據(jù)狀態(tài)碼,判斷是否請求成功,請求成功便通過BufferedReader讀取響應信息,返回請求結果

import com.alibaba.fastjson.JSONObject;
import org.quartz.DisallowConcurrentExecution;
import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.StringUtils;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.Objects;
@DisallowConcurrentExecution
public class HttpRemoteJob implements Job {
   //日志
    private static final Logger log = LoggerFactory.getLogger(HttpRemoteJob.class);
   @Override
    public void execute(JobExecutionContext context)throws JobExecutionException {
      //用于發(fā)送網(wǎng)絡請求
      HttpURLConnection connection = null;
      //用于接收請求返回的結果
      BufferedReader bufferedReader = null;
      //獲取任務Description述,之前我們把接口請求的信息放在Description里面了
      String jsonParams = context.getJobDetail().getDescription();
      if (StringUtils.isEmpty(jsonParams)){
         return;
      }
      //解析Description,獲取請求url
      JSONObject jsonObj= (JSONObject) JSONObject.parse(jsonParams);
      String callUrl = jsonObj.getString("callUrl");
      if(StringUtils.isEmpty(callUrl)) {
         return;
      }
      try {
         //編輯請求信息
         URL realUrl = new URL(callUrl);
         connection = (HttpURLConnection) realUrl.openConnection();
         connection.setRequestMethod("GET");
         connection.setDoOutput(true);
         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");
         //發(fā)送請求
         connection.connect();
         //獲取請求返回的狀態(tài)嗎
         int statusCode = connection.getResponseCode();
         if (statusCode != 200){
            //請求失敗拋出異常
            throw new RuntimeException("Http Request StatusCode(" + statusCode + ") Invalid.");
         }
         //如果返回值正常,數(shù)據(jù)在網(wǎng)絡中是以流的形式得到服務端返回的數(shù)據(jù)
         bufferedReader = new BufferedReader(new InputStreamReader(connection.getInputStream()));
         StringBuilder stringBuilder = new StringBuilder();
         String line;
         // 從流中讀取響應信息
         while ((line = bufferedReader.readLine()) != null) {
            stringBuilder.append(line);
         }
         String responseMsg = stringBuilder.toString();
         log.info(responseMsg);
      } catch (Exception e) {
         log.error(e.getMessage());
      } finally {
         //關閉流與請求連接
         try {
            if (Objects.nonNull(bufferedReader)){
               bufferedReader.close();
            }
            if (Objects.nonNull(connection)){
               connection.disconnect();
            }
         } catch (Exception e) {
            log.error(e.getMessage());
         }
      }
   }
}

@DisallowConcurrentExecution 的作用:

Quartz定時任務默認是并發(fā)執(zhí)行的,不會等待上一次任務執(zhí)行完畢,只要有間隔時間到就會執(zhí)行, 如果定時任執(zhí)行太長,會長時間占用資源,導致其它任務堵塞。@DisallowConcurrentExecution 這個注解是加在Job類上的,是禁止并發(fā)執(zhí)行多個相同定義的JobDetail, , 但并不是不能同時執(zhí)行多個Job, 而是不能并發(fā)執(zhí)行同一個Job Definition(由JobDetail定義), 但是可以同時執(zhí)行多個不同的JobDetail。

JobExecutionContext 類可以獲取很多任務的信息:

    @Override
    public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {
        JobKey jobKey = jobExecutionContext.getJobDetail().getKey();
        //工作任務名稱
        String jobName = jobKey.getName();
        //工作任務組名稱
        String groupName = jobKey.getGroup();
        //任務類名稱(帶路徑)
        String classPathName = jobExecutionContext.getJobDetail().getJobClass().getName();
        //任務類名稱
        String className = jobExecutionContext.getJobDetail().getJobClass().getSimpleName();
        //獲取Trigger內(nèi)容
        TriggerKey triggerKey = jobExecutionContext.getTrigger().getKey();
        //觸發(fā)器名稱
        String triggerName = triggerKey.getName();
        //出發(fā)組名稱(帶路徑)
        String triggerPathName = jobExecutionContext.getTrigger().getClass().getName();
        //觸發(fā)器類名稱
        String triggerClassName = jobExecutionContext.getTrigger().getClass().getSimpleName();
    }

注意:

      //解析Description,獲取請求url
      JSONObject jsonObj= (JSONObject) JSONObject.parse(jsonParams);
      String callUrl = jsonObj.getString("callUrl");

之前我們封裝JobInfo信息是將Map<String, Object> jsonParams保存接口請求信息,們?nèi)ソ馕鼋涌谡埱蟮臅r候也要用callUrl去取,那么在封裝JobInfo類時,Map中就需要指定key是callUrl,不然取不到就會報錯了。

6、創(chuàng)建定時任務業(yè)務層,創(chuàng)建 JobService接口和 JobServiceImpl實現(xiàn)類

JobServiceImpl業(yè)務邏輯:

JobInfo攜帶這我們需要新建任務的信息

① 通過jobName和jobGroup可以查詢到任務唯一的jobKey,通過getJobDetail(jobKey),判斷是否已有這個任務,有的話先刪除在新增這個任務

② 新建一個任務JobDetail,withDescription屬性中是指任務描述,JobInfo類中JsonParams屬性是一個Map,這里需要將Map格式化一下,不然無法賦給withDescription,這個JsonUtils在下面:

        //任務詳情
        JobDetail jobDetail = JobBuilder.newJob(HttpRemoteJob.class)
                .withDescription(JsonUtils.object2Json(jobInfo.getJsonParams()))  //任務描述
                .withIdentity(jobKey) //指定任務
                .build();

③ 創(chuàng)建觸發(fā)器,觸發(fā)器有多種類型,需要定時執(zhí)行就使用cron表達式,創(chuàng)建cron調度器建造器CronScheduleBuilder,再創(chuàng)建Trigger觸發(fā)器,調度器建造器有很多種,除了我下面用到的簡單調度器構造器SimpleTrigger,還有:

CalendarIntervalScheduleBuilder : 每隔一段時間執(zhí)行一次(年月日)

DailyTimeIntervalScheduleBuilder : 設置年月日中的某些固定日期,可以設置執(zhí)行總次數(shù)

以后我們再單獨寫一片介紹;

CronScheduleBuilder和SimpleTrigger的區(qū)別在于:CronScheduleBuilder是通過cron表達式定時某個時間點或多個時間點定時直接,而SimpleTrigger是周期性執(zhí)行,著重與時間間隔,著重與周期執(zhí)行;

④ 將任務添加到Scheduler中

此外還有一些Scheduler的其他方法:

獲取任務觸發(fā)器 :TriggerKey triggerKey = TriggerKey.triggerKey(jobName, jobGroup);

停止觸發(fā)器 :scheduler.pauseTrigger(triggerKey);

移除觸發(fā)器:scheduler.unscheduleJob(triggerKey);

刪除任務:scheduler.deleteJob(JobKey.jobKey(jobName,jobGroup));

根據(jù)jobName,jobGroup獲取jobKey 恢復任務:scheduler.resumeJob(JobKey.jobKey(jobName,jobGroup));

根據(jù)jobName,jobGroup獲取jobKey 暫停任務: scheduler.pauseJob(JobKey.jobKey(jobName,jobGroup));

根據(jù)jobName,jobGroup獲取jobKey 立即執(zhí)行任務: scheduler.triggerJob(JobKey.jobKey(jobName,jobGroup));

在下面的實現(xiàn)類代碼中有很好的用例;

JsonUtils工具類

public class JsonUtils {
    public static final ObjectMapper OBJECT_MAPPER = createObjectMapper();
    private static final ObjectMapper IGNORE_OBJECT_MAPPER = createIgnoreObjectMapper();
    private static ObjectMapper createIgnoreObjectMapper() {
        ObjectMapper objectMapper = createObjectMapper();
        objectMapper.addMixIn(Object.class, DynamicMixIn.class);
        return objectMapper;
    }
    public static ObjectMapper createObjectMapper() {
        ObjectMapper objectMapper = new ObjectMapper();
        objectMapper.configure(JsonParser.Feature.ALLOW_UNQUOTED_FIELD_NAMES, true);
        objectMapper.configure(DeserializationFeature.ACCEPT_EMPTY_STRING_AS_NULL_OBJECT, true);
        objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
        objectMapper.configure(DeserializationFeature.USE_BIG_DECIMAL_FOR_FLOATS, true);
        objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
        objectMapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);
        objectMapper.registerModule(new JavaTimeModule());
        return objectMapper;
    }
    public static String object2Json(Object o) {
        StringWriter sw = new StringWriter();
        JsonGenerator gen = null;
        try {
            gen = new JsonFactory().createGenerator(sw);
            OBJECT_MAPPER.writeValue(gen, o);
        } catch (IOException e) {
            throw new RuntimeException("Cannot serialize object as JSON", e);
        } finally {
            if (null != gen) {
                try {
                    gen.close();
                } catch (IOException e) {
                    throw new RuntimeException("Cannot serialize object as JSON", e);
                }
            }
        }
        return sw.toString();
    }
}

JobService接口

import liu.qingxu.domain.JobInfo;
import org.springframework.web.bind.annotation.RequestBody;
/**
 * @module
 * @author: qingxu.liu
 * @date: 2022-11-15 14:37
 * @copyright
 **/
public interface JobService {
    /**
     * 新建一個定時任務
     * @param jobInfo 任務信息
     * @return 任務信息
     */
    JobInfo save(@RequestBody JobInfo jobInfo);
    /**
     * 新建一個簡單定時任務
     * @param jobInfo 任務信息
     * @return 任務信息
     */
    JobInfo simpleSave(@RequestBody JobInfo jobInfo);
    /**
     * 刪除任務
     * @param jobName 任務名稱
     * @param jobGroup 任務組
     */
    void remove( String jobName,String jobGroup);
    /**
     * 恢復任務
     * @param jobName 任務名稱
     * @param jobGroup 任務組
     */
    void resume(String jobName,  String jobGroup);
    /**
     * 暫停任務
     * @param jobName 任務名稱
     * @param jobGroup 任務組
     */
    void pause(String jobName,  String jobGroup);
    /**
     * 立即執(zhí)行任務一主要是用于執(zhí)行一次任務的場景
     * @param jobName 任務名稱
     * @param jobGroup 任務組
     */
    void trigger(String jobName,  String jobGroup);
}

JobServiceImpl實現(xiàn)類

import liu.qingxu.domain.JobInfo;
import liu.qingxu.executors.HttpRemoteJob;
import liu.qingxu.service.JobService;
import liu.qingxu.utils.JsonUtils;
import org.quartz.CronScheduleBuilder;
import org.quartz.JobBuilder;
import org.quartz.JobDetail;
import org.quartz.JobKey;
import org.quartz.Scheduler;
import org.quartz.SchedulerException;
import org.quartz.SimpleTrigger;
import org.quartz.Trigger;
import org.quartz.TriggerBuilder;
import org.quartz.TriggerKey;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.Objects;
import java.util.TimeZone;
/**
 * @module
 * @author: qingxu.liu
 * @date: 2022-11-15 14:48
 * @copyright
 **/
@Service
public class JobServiceImpl implements JobService {
    @Autowired
    private Scheduler scheduler;
    @Override
    public JobInfo save(JobInfo jobInfo) {
        //查詢是否已有相同任務 jobKey可以唯一確定一個任務
        JobKey jobKey = JobKey.jobKey(jobInfo.getJobName(), jobInfo.getJobGroup());
        try {
            JobDetail jobDetail = scheduler.getJobDetail(jobKey);
            if (Objects.nonNull(jobDetail)){
                scheduler.deleteJob(jobKey);
            }
        } catch (SchedulerException e) {
            e.printStackTrace();
        }
        //任務詳情
        JobDetail jobDetail = JobBuilder.newJob(HttpRemoteJob.class)
                .withDescription(JsonUtils.object2Json(jobInfo.getJsonParams()))  //任務描述
                .withIdentity(jobKey) //指定任務
                .build();
        //根據(jù)cron,TimeZone時區(qū),指定執(zhí)行計劃
        CronScheduleBuilder builder = CronScheduleBuilder.cronSchedule(jobInfo.getCron())
                .inTimeZone(TimeZone.getTimeZone(jobInfo.getTimeZoneId()));
        //觸發(fā)器
        Trigger trigger = TriggerBuilder.newTrigger()
                .withIdentity(jobInfo.getJobName(), jobInfo.getJobGroup()).startNow()
                .withSchedule(builder)
                .build();
        //添加任務
        try {
            scheduler.scheduleJob(jobDetail, trigger);
        } catch (SchedulerException e) {
            System.out.println(e.getMessage());
        }
        return jobInfo;
    }
    @Override
    public JobInfo simpleSave(JobInfo jobInfo) {
        JobKey jobKey = JobKey.jobKey(jobInfo.getJobName(), jobInfo.getJobGroup());  //作業(yè)名稱及其組名
        //判斷是否有相同的作業(yè)
        try {
            JobDetail jobDetail = scheduler.getJobDetail(jobKey);
            if(jobDetail != null){
                scheduler.deleteJob(jobKey);
            }
        } catch (SchedulerException e) {
            e.printStackTrace();
        }
        //定義作業(yè)的詳細信息,并設置要執(zhí)行的作業(yè)類名,設置作業(yè)名稱及其組名
        JobDetail jobDetail = JobBuilder.newJob(HttpRemoteJob.class)
                .withDescription(JsonUtils.object2Json(jobInfo.getJsonParams()))
                .withIdentity(jobKey)
                .build()
                ;
        //簡單觸發(fā)器,著重與時間間隔
        SimpleTrigger trigger = (SimpleTrigger) TriggerBuilder.newTrigger()
                .withIdentity(jobInfo.getJobName(), jobInfo.getJobGroup())
                .startAt(jobInfo.getTriggerTime())
                .build();
        try {
            scheduler.scheduleJob(jobDetail, trigger);
        } catch (SchedulerException e) {
            System.out.println(e.getMessage());
        }
        return jobInfo;
    }
    @Override
    public void remove(String jobName, String jobGroup) {
        //獲取任務觸發(fā)器
        TriggerKey triggerKey = TriggerKey.triggerKey(jobName, jobGroup);
        try {
            //停止觸發(fā)器
            scheduler.pauseTrigger(triggerKey);
            //移除觸發(fā)器
            scheduler.unscheduleJob(triggerKey);
            //刪除任務
            scheduler.deleteJob(JobKey.jobKey(jobName,jobGroup));
        } catch (SchedulerException e) {
            System.out.println(e.getMessage());
        }
    }
    @Override
    public void resume(String jobName, String jobGroup) {
        try {
            //根據(jù)jobName,jobGroup獲取jobKey 恢復任務
            scheduler.resumeJob(JobKey.jobKey(jobName,jobGroup));
        } catch (SchedulerException e) {
            System.out.println(e.getMessage());
        }
    }
    @Override
    public void pause(String jobName, String jobGroup) {
        try {
            //根據(jù)jobName,jobGroup獲取jobKey 暫停任務
            scheduler.pauseJob(JobKey.jobKey(jobName,jobGroup));
        } catch (SchedulerException e) {
            System.out.println(e.getMessage());
        }
    }
    @Override
    public void trigger(String jobName, String jobGroup) {
        try {
            //根據(jù)jobName,jobGroup獲取jobKey 立即執(zhí)行任務
            scheduler.triggerJob(JobKey.jobKey(jobName,jobGroup));
        } catch (SchedulerException e) {
            System.out.println(e.getMessage());
        }
    }
}

到此為止,我的定時調用接口任務已經(jīng)完成了,現(xiàn)在我們寫個Controller來試著調用一下:

我們調用test接口,新建一個任務,讓任務每隔5秒調用runTest接口,然后在調用deleteTest接口刪除任務;

@RestController
@RequestMapping("/quartz/job")
public class QuartzController {
    private  final  JobService jobService;
    public QuartzController(JobService jobService) {
        this.jobService = jobService;
    }
    @GetMapping("/test")
    public void test(){
        JobInfo jobInfo = new JobInfo();
        jobInfo.setJobName("test-job");
        jobInfo.setJobGroup("test");
        jobInfo.setTimeZoneId("Asia/Shanghai");  //時區(qū)指定上海
        jobInfo.setCron("0/5 * * * * ? ");  //每5秒執(zhí)行一次
        Map<String, Object> params = new HashMap<>();
        //添加需要調用的接口信息
        String callUrl = "http://127.0.0.1:8080/quartz/job/test/run";
        params.put("callUrl", callUrl);
        jobInfo.setJsonParams(params);
        jobService.save(jobInfo);
    }
    @GetMapping("/test/run")
    public void runTest(){
        System.out.println(new Date());
    }
    @GetMapping("/test/delete")
    public void deleteTest(){
        jobService.remove("test-job","test");
        System.out.println("任務已刪除");
    }
}

四、驗證結果

可以看到runTest接口每隔5秒就被任務調用了一個,這時候我們?nèi)タ磾?shù)據(jù)庫中的scheduler庫的QRTZ_JOB_DETAILS表和QRTZ_TRIGGERS表,可以看到我們我們定時任務的信息,表示我們的定時任務持久化成功,這個時候你關掉服務器,再重啟任務也會定時去執(zhí)行runTest接口;

然后我們再調用deleteTest接口刪除任務

此時再看去看數(shù)據(jù)庫中的scheduler庫的QRTZ_JOB_DETAILS表和QRTZ_TRIGGERS表中就沒有之前的任務數(shù)據(jù)了

以上就是SpringBoot集成Quartz實現(xiàn)持久化定時接口調用任務的詳細內(nèi)容,更多關于SpringBoot Quartz實現(xiàn)定時任務的資料請關注腳本之家其它相關文章!

相關文章

  • SpringBoot3.x打包Docker容器的實現(xiàn)

    SpringBoot3.x打包Docker容器的實現(xiàn)

    這篇文章主要介紹了SpringBoot3.x打包Docker容器的實現(xiàn),文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友可以參考下
    2020-11-11
  • SpringBoot使用Flyway進行數(shù)據(jù)庫遷移的實現(xiàn)示例

    SpringBoot使用Flyway進行數(shù)據(jù)庫遷移的實現(xiàn)示例

    Flyway是一個數(shù)據(jù)庫遷移工具,它提供遷移歷史和回滾的功能,本文主要介紹了如何使用Flyway來管理Spring Boot應用程序中的SQL數(shù)據(jù)庫架構,感興趣的可以了解一下
    2023-08-08
  • spring注入配置文件屬性到java類

    spring注入配置文件屬性到java類

    這篇文章主要為大家介紹了spring注入配置文件屬性到java類實現(xiàn)示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2023-07-07
  • Java數(shù)組創(chuàng)建的3種方法6種寫法代碼示例

    Java數(shù)組創(chuàng)建的3種方法6種寫法代碼示例

    這篇文章主要給大家介紹了關于Java數(shù)組創(chuàng)建的3種方法6種寫法,在Java中我們可以使用關鍵字new來創(chuàng)建一個數(shù)組,文中通過代碼介紹的非常詳細,需要的朋友可以參考下
    2024-01-01
  • 詳解Spring與MyBatis的整合的方法

    詳解Spring與MyBatis的整合的方法

    這篇文章主要為大家詳細介紹了Spring與MyBatis的整合,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下,希望能夠給你帶來幫助
    2022-03-03
  • 劍指Offer之Java算法習題精講二叉樹專題篇上

    劍指Offer之Java算法習題精講二叉樹專題篇上

    跟著思路走,之后從簡單題入手,反復去看,做過之后可能會忘記,之后再做一次,記不住就反復做,反復尋求思路和規(guī)律,慢慢積累就會發(fā)現(xiàn)質的變化
    2022-03-03
  • Spring擴展之基于HandlerMapping實現(xiàn)接口灰度發(fā)布實例

    Spring擴展之基于HandlerMapping實現(xiàn)接口灰度發(fā)布實例

    這篇文章主要介紹了Spring擴展之基于HandlerMapping實現(xiàn)接口灰度發(fā)布實例,灰度發(fā)布是指在黑與白之間,能夠平滑過渡的一種發(fā)布方式,灰度發(fā)布可以保證整體系統(tǒng)的穩(wěn)定,在初始灰度的時候就可以發(fā)現(xiàn)、調整問題,以保證其影響度,需要的朋友可以參考下
    2023-08-08
  • SpringBoot讀取配置的常用方式總結

    SpringBoot讀取配置的常用方式總結

    在SpringBoot應用開發(fā)中,配置文件是不可或缺的一部分,它們幫助我們管理應用的運行時參數(shù),使得應用的部署和維護變得更加靈活,本文將介紹六種常用的SpringBoot讀取配置方式,需要的朋友跟著小編一起來看看吧
    2024-07-07
  • Java 深入淺出講解泛型與包裝類

    Java 深入淺出講解泛型與包裝類

    泛型是在Java SE 1.5引入的的新特性,本質是參數(shù)化類型,也就是說所操作的數(shù)據(jù)類型被指定為一個參數(shù)。這種參數(shù)類型可以用在類、接口和方法的創(chuàng)建中,分別稱為泛型類、泛型接口、泛型方法,本篇我們一起來學習泛型以及包裝類
    2022-04-04
  • 使用@SpringBootTest注解進行單元測試

    使用@SpringBootTest注解進行單元測試

    這篇文章主要介紹了使用@SpringBootTest注解進行單元測試,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2020-09-09

最新評論