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

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

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

一、基本概念

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

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

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

二、Quartz-scheduler 的核心流程

Scheduler - 調(diào)度器

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

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

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

Job - 任務(wù)

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

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

JobDetail - 任務(wù)詳情

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

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

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

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

Trigger - 觸發(fā)器

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

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

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

JobDataMap

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

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

三、實(shí)踐

1、新建一個(gè)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ù)庫(kù),所以需要單據(jù)創(chuàng)建一個(gè)庫(kù)來(lái)給quartz使用,我新建了一個(gè)scheduler的庫(kù)

#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 存儲(chǔ)程序的悲觀鎖的信息(假如使用了悲觀鎖)
DROP TABLE IF EXISTS QRTZ_LOCKS;
#5 簡(jiǎn)單的觸發(fā)器表
DROP TABLE IF EXISTS QRTZ_SIMPLE_TRIGGERS;
#6 存儲(chǔ)兩種類型的觸發(fā)器表
DROP TABLE IF EXISTS QRTZ_SIMPROP_TRIGGERS;
#7 定時(shí)觸發(fā)器表
DROP TABLE IF EXISTS QRTZ_CRON_TRIGGERS;
#8 以blob 類型存儲(chǔ)的觸發(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)
);
#簡(jiǎn)單的觸發(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)
);
#定時(shí)觸發(fā)器表,存儲(chǔ) cron trigger,包括 cron 表達(dá)式和時(shí)區(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)
);
#存儲(chǔ)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 類型存儲(chǔ)的觸發(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可配置一個(gè)日歷來(lái)指定一個(gè)時(shí)間范圍
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)
);
# 存儲(chǔ)與已觸發(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、 存儲(chǔ)程序的悲觀鎖的信息(假如使用了悲觀鎖)
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 使用一個(gè)名為 quartz.properties 的屬性文件進(jìn)行信息配置,必須位于 classpath 下,是 StdSchedulerFactory 用于創(chuàng)建 Scheduler 的默認(rèn)屬性文件。默認(rèn)情況下 StdSchedulerFactory 從類路徑下加載名為 “quartz.properties” 的屬性文件,如果失敗,則加載 org/quartz 包中的“quartz.properties”文件,因?yàn)槲倚枰氖切陆ㄒ粋€(gè)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
  #定時(shí)配置
  quartz:
    #相關(guān)屬性配置
    properties:
      org:
        quartz:
          scheduler:
            instanceName: local-scheduler-svc
            instanceId: AUTO
          jobStore:
            #表示 quartz 中的所有數(shù)據(jù),比如作業(yè)和觸發(fā)器的信息都保存在內(nèi)存中(而不是數(shù)據(jù)庫(kù)中)
            class: org.springframework.scheduling.quartz.LocalDataSourceJobStore
            # 驅(qū)動(dòng)配置
            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)先級(jí)
            threadPriority: 5
            #線程繼承上下文類加載器的初始化線程
            threadsInheritContextClassLoaderOfInitializingThread: true
    #數(shù)據(jù)庫(kù)方式
    job-store-type: JDBC
    #初始化表結(jié)構(gòu)
    jdbc:
      initialize-schema: NEVER

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

jobName:任務(wù)名稱

jobGroup:任務(wù)組

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

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

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

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

@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、新建一個(gè)任務(wù)執(zhí)行類HttpRemoteJob 實(shí)現(xiàn) Job接口,重寫execute()方法

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

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

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

③ 編輯請(qǐng)求信息,通過URL類編輯請(qǐng)求信息,使用HttpURLConnection發(fā)送請(qǐng)求,并接收請(qǐng)求返回的狀態(tài)碼,根據(jù)狀態(tài)碼,判斷是否請(qǐng)求成功,請(qǐng)求成功便通過BufferedReader讀取響應(yīng)信息,返回請(qǐ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ò)請(qǐng)求
      HttpURLConnection connection = null;
      //用于接收請(qǐng)求返回的結(jié)果
      BufferedReader bufferedReader = null;
      //獲取任務(wù)Description述,之前我們把接口請(qǐng)求的信息放在Description里面了
      String jsonParams = context.getJobDetail().getDescription();
      if (StringUtils.isEmpty(jsonParams)){
         return;
      }
      //解析Description,獲取請(qǐng)求url
      JSONObject jsonObj= (JSONObject) JSONObject.parse(jsonParams);
      String callUrl = jsonObj.getString("callUrl");
      if(StringUtils.isEmpty(callUrl)) {
         return;
      }
      try {
         //編輯請(qǐng)求信息
         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ā)送請(qǐng)求
         connection.connect();
         //獲取請(qǐng)求返回的狀態(tài)嗎
         int statusCode = connection.getResponseCode();
         if (statusCode != 200){
            //請(qǐng)求失敗拋出異常
            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)閉流與請(qǐng)求連接
         try {
            if (Objects.nonNull(bufferedReader)){
               bufferedReader.close();
            }
            if (Objects.nonNull(connection)){
               connection.disconnect();
            }
         } catch (Exception e) {
            log.error(e.getMessage());
         }
      }
   }
}

@DisallowConcurrentExecution 的作用:

Quartz定時(shí)任務(wù)默認(rèn)是并發(fā)執(zhí)行的,不會(huì)等待上一次任務(wù)執(zhí)行完畢,只要有間隔時(shí)間到就會(huì)執(zhí)行, 如果定時(shí)任執(zhí)行太長(zhǎng),會(huì)長(zhǎng)時(shí)間占用資源,導(dǎo)致其它任務(wù)堵塞。@DisallowConcurrentExecution 這個(gè)注解是加在Job類上的,是禁止并發(fā)執(zhí)行多個(gè)相同定義的JobDetail, , 但并不是不能同時(shí)執(zhí)行多個(gè)Job, 而是不能并發(fā)執(zhí)行同一個(gè)Job Definition(由JobDetail定義), 但是可以同時(shí)執(zhí)行多個(gè)不同的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,獲取請(qǐng)求url
      JSONObject jsonObj= (JSONObject) JSONObject.parse(jsonParams);
      String callUrl = jsonObj.getString("callUrl");

之前我們封裝JobInfo信息是將Map<String, Object> jsonParams保存接口請(qǐng)求信息,們?nèi)ソ馕鼋涌谡?qǐng)求的時(shí)候也要用callUrl去取,那么在封裝JobInfo類時(shí),Map中就需要指定key是callUrl,不然取不到就會(huì)報(bào)錯(cuò)了。

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

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

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

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

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

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

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

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

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

以后我們?cè)賳为?dú)寫一片介紹;

CronScheduleBuilder和SimpleTrigger的區(qū)別在于:CronScheduleBuilder是通過cron表達(dá)式定時(shí)某個(gè)時(shí)間點(diǎn)或多個(gè)時(shí)間點(diǎn)定時(shí)直接,而SimpleTrigger是周期性執(zhí)行,著重與時(shí)間間隔,著重與周期執(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));

在下面的實(shí)現(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 {
    /**
     * 新建一個(gè)定時(shí)任務(wù)
     * @param jobInfo 任務(wù)信息
     * @return 任務(wù)信息
     */
    JobInfo save(@RequestBody JobInfo jobInfo);
    /**
     * 新建一個(gè)簡(jiǎn)單定時(shí)任務(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ù)的場(chǎng)景
     * @param jobName 任務(wù)名稱
     * @param jobGroup 任務(wù)組
     */
    void trigger(String jobName,  String jobGroup);
}

JobServiceImpl實(shí)現(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可以唯一確定一個(gè)任務(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時(shí)區(qū),指定執(zhí)行計(jì)劃
        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()
                ;
        //簡(jiǎn)單觸發(fā)器,著重與時(shí)間間隔
        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());
        }
    }
}

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

我們調(diào)用test接口,新建一個(gè)任務(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");  //時(shí)區(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ù)已刪除");
    }
}

四、驗(yàn)證結(jié)果

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

然后我們?cè)僬{(diào)用deleteTest接口刪除任務(wù)

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

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

相關(guān)文章

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

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

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

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

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

    spring注入配置文件屬性到j(luò)ava類

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

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

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

    詳解Spring與MyBatis的整合的方法

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

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

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

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

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

    SpringBoot讀取配置的常用方式總結(jié)

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

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

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

    使用@SpringBootTest注解進(jìn)行單元測(cè)試

    這篇文章主要介紹了使用@SpringBootTest注解進(jìn)行單元測(cè)試,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2020-09-09

最新評(píng)論