最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

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

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

一、基本概念

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

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

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

二、Quartz-scheduler 的核心流程

Scheduler - 調(diào)度器

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

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

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

Job - 任務(wù)

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

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

JobDetail - 任務(wù)詳情

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

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

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

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

Trigger - 觸發(fā)器

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

2、Trigger也有一個相關(guān)聯(lián)的 JobDataMap,用于給Job傳遞一些觸發(fā)相關(guān)的參數(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è)置參數(shù)信息,Job 執(zhí)行 execute() 方法的時候,JobExecutionContext 可以獲取到 JobDataMap 中的信息。

三、實踐

1、新建一個quartz-service服務(wù)

添加依賴:

<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ù)源相關(guān)鏈接

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 調(diào)度器狀態(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 詳細(xì)信息表
DROP TABLE IF EXISTS QRTZ_JOB_DETAILS;
#11 日歷信息表
DROP TABLE IF EXISTS QRTZ_CALENDARS;
#job 詳細(xì)信息表
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ā)器表,包括重復(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 表達(dá)式和時區(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 相關(guān)的狀態(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、 調(diào)度器狀態(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 的屬性文件進(jìn)行信息配置,必須位于 classpath 下,是 StdSchedulerFactory 用于創(chuàng)建 Scheduler 的默認(rèn)屬性文件。默認(rèn)情況下 StdSchedulerFactory 從類路徑下加載名為 “quartz.properties” 的屬性文件,如果失敗,則加載 org/quartz 包中的“quartz.properties”文件,因為我需要的是新建一個Scheduler服務(wù),所以直接使用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:
    #相關(guān)屬性配置
    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
            # 驅(qū)動配置
            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
    #初始化表結(jié)構(gòu)
    jdbc:
      initialize-schema: NEVER

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

jobName:任務(wù)名稱

jobGroup:任務(wù)組

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

cron:定時任務(wù)的cron表達(dá)式

timeZoneId:定制執(zhí)行任務(wù)的時區(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、新建一個任務(wù)執(zhí)行類HttpRemoteJob 實現(xiàn) Job接口,重寫execute()方法

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

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

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

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

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)絡(luò)請求
      HttpURLConnection connection = null;
      //用于接收請求返回的結(jié)果
      BufferedReader bufferedReader = null;
      //獲取任務(wù)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)絡(luò)中是以流的形式得到服務(wù)端返回的數(shù)據(jù)
         bufferedReader = new BufferedReader(new InputStreamReader(connection.getInputStream()));
         StringBuilder stringBuilder = new StringBuilder();
         String line;
         // 從流中讀取響應(yīng)信息
         while ((line = bufferedReader.readLine()) != null) {
            stringBuilder.append(line);
         }
         String responseMsg = stringBuilder.toString();
         log.info(responseMsg);
      } catch (Exception e) {
         log.error(e.getMessage());
      } finally {
         //關(guān)閉流與請求連接
         try {
            if (Objects.nonNull(bufferedReader)){
               bufferedReader.close();
            }
            if (Objects.nonNull(connection)){
               connection.disconnect();
            }
         } catch (Exception e) {
            log.error(e.getMessage());
         }
      }
   }
}

@DisallowConcurrentExecution 的作用:

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

JobExecutionContext 類可以獲取很多任務(wù)的信息:

    @Override
    public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {
        JobKey jobKey = jobExecutionContext.getJobDetail().getKey();
        //工作任務(wù)名稱
        String jobName = jobKey.getName();
        //工作任務(wù)組名稱
        String groupName = jobKey.getGroup();
        //任務(wù)類名稱(帶路徑)
        String classPathName = jobExecutionContext.getJobDetail().getJobClass().getName();
        //任務(wù)類名稱
        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)建定時任務(wù)業(yè)務(wù)層,創(chuàng)建 JobService接口和 JobServiceImpl實現(xiàn)類

JobServiceImpl業(yè)務(wù)邏輯:

JobInfo攜帶這我們需要新建任務(wù)的信息

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

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

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

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

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

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

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

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

④ 將任務(wù)添加到Scheduler中

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

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

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

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

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

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

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

根據(jù)jobName,jobGroup獲取jobKey 立即執(zhí)行任務(wù): 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 {
    /**
     * 新建一個定時任務(wù)
     * @param jobInfo 任務(wù)信息
     * @return 任務(wù)信息
     */
    JobInfo save(@RequestBody JobInfo jobInfo);
    /**
     * 新建一個簡單定時任務(wù)
     * @param jobInfo 任務(wù)信息
     * @return 任務(wù)信息
     */
    JobInfo simpleSave(@RequestBody JobInfo jobInfo);
    /**
     * 刪除任務(wù)
     * @param jobName 任務(wù)名稱
     * @param jobGroup 任務(wù)組
     */
    void remove( String jobName,String jobGroup);
    /**
     * 恢復(fù)任務(wù)
     * @param jobName 任務(wù)名稱
     * @param jobGroup 任務(wù)組
     */
    void resume(String jobName,  String jobGroup);
    /**
     * 暫停任務(wù)
     * @param jobName 任務(wù)名稱
     * @param jobGroup 任務(wù)組
     */
    void pause(String jobName,  String jobGroup);
    /**
     * 立即執(zhí)行任務(wù)一主要是用于執(zhí)行一次任務(wù)的場景
     * @param jobName 任務(wù)名稱
     * @param jobGroup 任務(wù)組
     */
    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) {
        //查詢是否已有相同任務(wù) jobKey可以唯一確定一個任務(wù)
        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();
        }
        //任務(wù)詳情
        JobDetail jobDetail = JobBuilder.newJob(HttpRemoteJob.class)
                .withDescription(JsonUtils.object2Json(jobInfo.getJsonParams()))  //任務(wù)描述
                .withIdentity(jobKey) //指定任務(wù)
                .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();
        //添加任務(wù)
        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è)的詳細(xì)信息,并設(shè)置要執(zhí)行的作業(yè)類名,設(shè)置作業(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) {
        //獲取任務(wù)觸發(fā)器
        TriggerKey triggerKey = TriggerKey.triggerKey(jobName, jobGroup);
        try {
            //停止觸發(fā)器
            scheduler.pauseTrigger(triggerKey);
            //移除觸發(fā)器
            scheduler.unscheduleJob(triggerKey);
            //刪除任務(wù)
            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 恢復(fù)任務(wù)
            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 暫停任務(wù)
            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í)行任務(wù)
            scheduler.triggerJob(JobKey.jobKey(jobName,jobGroup));
        } catch (SchedulerException e) {
            System.out.println(e.getMessage());
        }
    }
}

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

我們調(diào)用test接口,新建一個任務(wù),讓任務(wù)每隔5秒調(diào)用runTest接口,然后在調(diào)用deleteTest接口刪除任務(wù);

@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<>();
        //添加需要調(diào)用的接口信息
        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("任務(wù)已刪除");
    }
}

四、驗證結(jié)果

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

然后我們再調(diào)用deleteTest接口刪除任務(wù)

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

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

相關(guān)文章

  • Java后臺與微信小程序的數(shù)據(jù)交互實現(xiàn)

    Java后臺與微信小程序的數(shù)據(jù)交互實現(xiàn)

    這篇文章主要介紹了Java后臺與微信小程序的數(shù)據(jù)交互實現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2020-12-12
  • Java Spring Boot 集成Zookeeper

    Java Spring Boot 集成Zookeeper

    這篇文章主要介紹了Java Spring Boot 集成Zookeeper,文章圍繞主題展開詳細(xì)的內(nèi)容介紹,具有一定的參考價值,需要的小伙伴可以參考一下
    2022-08-08
  • 關(guān)于mybatis的一級緩存和二級緩存的那些事兒

    關(guān)于mybatis的一級緩存和二級緩存的那些事兒

    MyBatis自帶的緩存有一級緩存和二級緩存,今天我們就來學(xué)習(xí)一下,文中有非常詳細(xì)的總結(jié),對正在學(xué)習(xí)的小伙伴們很有幫助,需要的朋友可以參考下
    2021-06-06
  • Java及Android中常用鏈?zhǔn)秸{(diào)用寫法簡單示例

    Java及Android中常用鏈?zhǔn)秸{(diào)用寫法簡單示例

    這篇文章主要介紹了Java及Android中常用鏈?zhǔn)秸{(diào)用寫法,結(jié)合實例形式分析了java編程中的鏈?zhǔn)秸{(diào)用概念、簡單使用方法及相關(guān)操作技巧,需要的朋友可以參考下
    2018-01-01
  • IDEA “Cannot resolve symbol”爆紅問題解決

    IDEA “Cannot resolve symbol”爆紅問題解決

    最近發(fā)現(xiàn)個問題,IDEA 無法識別同一個 package 里的其他類,將其顯示為紅色,本文就來介紹一下IDEA “Cannot resolve symbol”爆紅問題解決,感興趣的可以了解一下
    2023-10-10
  • 解決slf4j 和 logback-classic遇到的坑

    解決slf4j 和 logback-classic遇到的坑

    這篇文章主要介紹了解決slf4j 和 logback-classic遇到的坑,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2021-07-07
  • 淺析 Java多線程

    淺析 Java多線程

    這篇文章主要介紹了Java多線程的相關(guān)資料,幫助大家更好的理解和學(xué)習(xí)Java線程相關(guān)知識,感興趣的朋友可以了解下
    2020-09-09
  • Java使用Curator進(jìn)行ZooKeeper操作的詳細(xì)教程

    Java使用Curator進(jìn)行ZooKeeper操作的詳細(xì)教程

    Apache Curator 是一個基于 ZooKeeper 的 Java 客戶端庫,它極大地簡化了使用 ZooKeeper 的開發(fā)工作,在分布式系統(tǒng)中,ZooKeeper 通常被用來作為協(xié)調(diào)服務(wù),而 Curator 則為我們提供了更簡潔易用的接口,本文將介紹 Curator 的核心功能及實踐樣例,需要的朋友可以參考下
    2025-04-04
  • Java中檢查字符串是否以特定字符結(jié)尾

    Java中檢查字符串是否以特定字符結(jié)尾

    這篇文章主要介紹了Java中檢查字符串是否以特定字符結(jié)尾,文章圍繞主題展開字符串匹配問題,具有一定的參考價值需要的小伙伴可以參考一下
    2022-06-06
  • 智能 AI 代碼生成工具 Cursor 安裝和使用超詳細(xì)教程

    智能 AI 代碼生成工具 Cursor 安裝和使用超詳細(xì)教程

    Cursor.so 是一個集成了 GPT-4 的國內(nèi)直接可以訪問的,優(yōu)秀而強大的免費代碼生成器,可以幫助你快速編寫、編輯和討論代碼,這篇文章主要介紹了智能 AI 代碼生成工具 Cursor 安裝和使用介紹,需要的朋友可以參考下
    2023-05-05

最新評論

安平县| 八宿县| 海门市| 华阴市| 武清区| 新沂市| 长岭县| 来凤县| 靖州| 芮城县| 乌拉特后旗| 峨边| 宝鸡市| 桐梓县| 上饶市| 射阳县| 柏乡县| 昌宁县| 咸宁市| 三穗县| 远安县| 青阳县| 巨鹿县| 田阳县| 烟台市| 天水市| 行唐县| 天长市| 个旧市| 大兴区| 望江县| 肇庆市| 平度市| 芦山县| 屏东县| 东海县| 将乐县| 西平县| 普兰店市| 秭归县| 乐至县|