国产 无码 综合区,色欲AV无码国产永久播放,无码天堂亚洲国产AV,国产日韩欧美女同一区二区

你知道怎么實(shí)現(xiàn)定時(shí)任務(wù)嗎?

這篇具有很好參考價(jià)值的文章主要介紹了你知道怎么實(shí)現(xiàn)定時(shí)任務(wù)嗎?。希望對大家有所幫助。如果存在錯(cuò)誤或未考慮完全的地方,請大家不吝賜教,您也可以點(diǎn)擊"舉報(bào)違法"按鈕提交疑問。

諸位讀者都知道筆者寫東西都是用到才寫,筆者的學(xué)習(xí)足跡自從參加工作之后就是 非系統(tǒng) 學(xué)習(xí)了,公司里源代碼只要有筆者不知道的技術(shù)細(xì)節(jié),筆者就會仔細(xì)的研究清楚,筆者是不喜歡給自己留下問題的那種學(xué)習(xí)習(xí)慣。

為何要寫

筆者最近負(fù)責(zé)了消息發(fā)送的一些業(yè)務(wù)需求,由于筆者工作年限不到一年,且筆者目前只是普通本科大四學(xué)生,技術(shù)棧并不是很完善(crud程序員只是起點(diǎn)),例如國內(nèi)很多公司都在用的許雪里大神開發(fā)的xxl-job計(jì)劃任務(wù)框架,tk-mybaytis框架(筆者也不知道公司為什么不直接用mybatis而是選用了它的其他版本),自封裝的ORM框架(用法與mybatis不一樣的地方在于它需要自己寫Sql語句進(jìn)行封裝),以及各種自封裝的自研框架。由于接觸了需求,定時(shí)任務(wù)發(fā)送消息提醒用戶。所以筆者不得不學(xué)習(xí)測試一下定時(shí)任務(wù)框架來加深更多的了解,不能在開發(fā)過程中知其然不知其所以然。

定時(shí)任務(wù)的實(shí)現(xiàn)方式

說到頭,定時(shí)任務(wù)就是類似于將cron語句:?秒 分 時(shí) 日 月 周 年? 交給linux系統(tǒng)進(jìn)行執(zhí)行,到了指定的時(shí)間就得執(zhí)行這些命令,在業(yè)務(wù)需求里,我們需要系統(tǒng)去定時(shí)催促實(shí)現(xiàn)某些事情,其實(shí)這種場景非常多,要寫程序?qū)崿F(xiàn)計(jì)時(shí)的功能我覺得讀者們應(yīng)該都有自己的方式:例如 利用java線程sleep的方式,利用 timerTak的工具包,利用redis鍵值對過期消息回調(diào),利用前端計(jì)時(shí)器,利用spring框架自帶的注解@Scheduled (計(jì)劃的)搭配cron語句,利用數(shù)據(jù)庫event實(shí)現(xiàn),利用xxl-job框架實(shí)現(xiàn),利用quartz框架實(shí)現(xiàn)等等......

之前筆者就分享了一套用java線程池實(shí)現(xiàn)的定時(shí)任務(wù)工具,底層就是sleep方法,缺陷太多,不適合生產(chǎn)環(huán)境,所以為了加深對公司項(xiàng)目中xxl-job業(yè)務(wù)代碼的理解,筆者研究了quartz框架實(shí)現(xiàn)計(jì)劃任務(wù)的原理與實(shí)現(xiàn)方式,記錄下來供筆者日后進(jìn)一步深研。

下面筆者舉例部分代碼供讀者閱讀:

1、java線程sleep實(shí)現(xiàn)計(jì)時(shí)任務(wù):

你知道怎么實(shí)現(xiàn)定時(shí)任務(wù)嗎?

?2、java工具包TimerTask實(shí)現(xiàn)定時(shí)任務(wù):

你知道怎么實(shí)現(xiàn)定時(shí)任務(wù)嗎?

?3、利用redis鍵值對過期回調(diào):

你知道怎么實(shí)現(xiàn)定時(shí)任務(wù)嗎?

你知道怎么實(shí)現(xiàn)定時(shí)任務(wù)嗎?

4、前端js函數(shù)setTimeOut(函數(shù),毫秒)也可定時(shí);

5、 spring注解@Scheduled實(shí)現(xiàn)定時(shí)任務(wù):

你知道怎么實(shí)現(xiàn)定時(shí)任務(wù)嗎?

你知道怎么實(shí)現(xiàn)定時(shí)任務(wù)嗎?

6、利用數(shù)據(jù)庫事件event實(shí)現(xiàn):

事件調(diào)度器:?Event Scheduler可以用做定時(shí)執(zhí)行某些特定任務(wù)(例如:刪除記錄、數(shù)據(jù)統(tǒng)計(jì)報(bào)告、數(shù)據(jù)備份等等),來取代原先只能由操作系統(tǒng)的計(jì)劃任務(wù)來執(zhí)行的工作(可以精確到秒)區(qū)分表級別的觸發(fā)器Tigger(事件觸發(fā)的任務(wù)是周期時(shí)間決定,而觸發(fā)器是由表級動作決定的)。

(1)開啟數(shù)據(jù)庫事件調(diào)度:

SHOW VARIABLES LIKE 'event%';? 表中的event_scheduler值為on即為已經(jīng)開啟。

開啟:SET GLOBAL event_scheduler = 1;SET GLOBAL event_scheduler = ON;
關(guān)閉:SET GLOBAL event_scheduler = 0;SET GLOBAL event_scheduler = OFF;

如果想持久化配置項(xiàng)可以將event_scheduler=1寫入到數(shù)據(jù)庫配置文件my.cnf文件(區(qū)別my.inf文件)中。

(2)創(chuàng)建事件調(diào)度:

CREATE EVENT 【調(diào)度事件名稱】
ON SCHEDULE 【指定好調(diào)度時(shí)間】
DO 【SQL可執(zhí)行語句】

具體細(xì)節(jié)以及其他語法讀者們請自行www.baidu,com;

(3)舉例實(shí)現(xiàn)調(diào)度語句:

CREATE EVENT delete_db_and_run
ON SCHEDULE? AT? TIMESTEP '2023-04-16 16:00:00'
DO? DELETE FROM?alldata

以上就是在2023年4月16日16點(diǎn)整刪除數(shù)據(jù)表alldata的定時(shí)計(jì)劃語法

Quartz框架的技術(shù)細(xì)節(jié)與底層原理

筆者總結(jié)的技術(shù)點(diǎn)若有任何問題,歡迎讀者指出,閱即改正。

技術(shù)細(xì)節(jié):Quartz是通過數(shù)據(jù)庫持久化來實(shí)現(xiàn)計(jì)劃的數(shù)據(jù)保留,但它的原理并不是基于數(shù)據(jù)庫event的方式進(jìn)行實(shí)現(xiàn)的計(jì)劃任務(wù)。它可以擴(kuò)展更多的需求也業(yè)務(wù),你可以自定義事件只要它們繼承了Job接口。Quartz優(yōu)質(zhì)的地方在于它與xxl-job一樣是基于數(shù)據(jù)庫進(jìn)行事件調(diào)度的,這樣就算是server宕機(jī)了也不影響我們定時(shí)任務(wù)的存儲,只要在容忍時(shí)間內(nèi)重啟服務(wù)器我們的定時(shí)任務(wù)還是可以正常執(zhí)行的。

底層原理:我們只需要搞清楚兩個(gè)問題即可明白該框架的主流程脈絡(luò),至于其他的枝葉邏輯筆者并沒有深究,這兩個(gè)主流程問題就是該框架是如何將任務(wù)存入數(shù)據(jù)庫的?該框架是如何獲取定時(shí)任務(wù)的時(shí)間并判斷給出動作的?

問題1:quartz框架是如何將任務(wù)存儲進(jìn)它的配置數(shù)據(jù)表中的?

筆者還是以源碼跟蹤的方式與讀者們一起研究這里面的主要脈絡(luò):

首先在官方demo中我們能發(fā)現(xiàn)至關(guān)重要的語句,也是查找源碼的起始點(diǎn):

你知道怎么實(shí)現(xiàn)定時(shí)任務(wù)嗎?

?org.quartz.Scheduler類實(shí)現(xiàn)的調(diào)度任務(wù)方法,就是添加計(jì)劃任務(wù)的主要實(shí)現(xiàn)代碼,筆者點(diǎn)進(jìn)源碼發(fā)現(xiàn)以下方法鏈調(diào)用:

你知道怎么實(shí)現(xiàn)定時(shí)任務(wù)嗎??你知道怎么實(shí)現(xiàn)定時(shí)任務(wù)嗎?

你知道怎么實(shí)現(xiàn)定時(shí)任務(wù)嗎?

你知道怎么實(shí)現(xiàn)定時(shí)任務(wù)嗎?

你知道怎么實(shí)現(xiàn)定時(shí)任務(wù)嗎?

你知道怎么實(shí)現(xiàn)定時(shí)任務(wù)嗎?

?(觸發(fā)器同理也能找到類似于更新與插入方法的調(diào)用,我們先揪著一個(gè)走就行)

你知道怎么實(shí)現(xiàn)定時(shí)任務(wù)嗎?

你知道怎么實(shí)現(xiàn)定時(shí)任務(wù)嗎?

?這里其實(shí)已經(jīng)很清晰了,運(yùn)用了JDBC采用動態(tài)注入的方式進(jìn)行數(shù)據(jù)庫的更新與插入操作。

其實(shí)quartz還有其他數(shù)據(jù)庫類型可以使用,但是入庫的主流程脈絡(luò)大差不差,我們首先要知道它就是通過我們crud程序員最擅長的方式進(jìn)行的數(shù)據(jù)庫存儲定時(shí)任務(wù)信息的。

問題2:quartz是如何獲取定時(shí)任務(wù)的時(shí)間并判斷給出動作的?

關(guān)于這個(gè)問題,筆者查看了依賴包的層級結(jié)構(gòu),找到了core中的核心QuartzSchedulerThread類;

你知道怎么實(shí)現(xiàn)定時(shí)任務(wù)嗎??顯然這就是quartz做出動作的核心線程類,關(guān)于它做了什么,我們只需要看線程的run方法即可:

由于此類的run方法行數(shù)比較長,筆者將分開敘述它做了什么:

首先在while中優(yōu)先給出了同步持有對象鎖sigLock,通先查詢halted的原子布爾型變量的判斷,如果停止的值為false則進(jìn)入等待階段等到之后進(jìn)行下一次掃描:

你知道怎么實(shí)現(xiàn)定時(shí)任務(wù)嗎?

?下一步判斷線程池可用線程數(shù),可用數(shù)大于0后獲取將要觸發(fā)的觸發(fā)器鏈表Tiggers:

你知道怎么實(shí)現(xiàn)定時(shí)任務(wù)嗎?

?注意方法 this.qsRsrcs.getJobStore().acquireNextTriggers 是獲取將要觸發(fā)的觸發(fā)器列表:

這里筆者直接將獲取將要觸發(fā)的觸發(fā)器列表方法核心邏輯展示出來供讀者閱讀:?

你知道怎么實(shí)現(xiàn)定時(shí)任務(wù)嗎?

之后就是 判斷時(shí)間有沒有到該觸發(fā)的時(shí)間,因?yàn)橛|發(fā)器列表中的觸發(fā)器都是將要觸發(fā)的,判斷到快要觸發(fā)了。所以quartz這里做了等待,如果trigger的nextFireTime比當(dāng)前時(shí)間大2ms則循環(huán)等待。timeUntilTrigger就是nextFireTime和當(dāng)前時(shí)間之間的差值,它在循環(huán)中不斷更新,直到它的值非常小了,之后才繼續(xù)向下到真正的觸發(fā)代碼:

你知道怎么實(shí)現(xiàn)定時(shí)任務(wù)嗎?

?然后就是觸發(fā)了:

你知道怎么實(shí)現(xiàn)定時(shí)任務(wù)嗎?

?我們繼續(xù)看觸發(fā)方法tiggersFired()的部分核心代碼:

你知道怎么實(shí)現(xiàn)定時(shí)任務(wù)嗎?

?之后的邏輯方法就是數(shù)據(jù)庫操作,記錄操作:

你知道怎么實(shí)現(xiàn)定時(shí)任務(wù)嗎?

?將庫修改之后就要進(jìn)行腳本的執(zhí)行了(這里需要注意的是執(zhí)行是線程的異步操作,并行執(zhí)行,但也有一定的控制邏輯),根據(jù)數(shù)據(jù)庫的觸發(fā)結(jié)果獲取腳本shell:

你知道怎么實(shí)現(xiàn)定時(shí)任務(wù)嗎?

?拿到shell之后就可以到線程池中丟過去執(zhí)行了:

你知道怎么實(shí)現(xiàn)定時(shí)任務(wù)嗎?

你知道怎么實(shí)現(xiàn)定時(shí)任務(wù)嗎?你知道怎么實(shí)現(xiàn)定時(shí)任務(wù)嗎?

?實(shí)現(xiàn)了Runnable接口的shell當(dāng)然可以直接執(zhí)行了,最后我們發(fā)現(xiàn)它對于未獲取到將要觸發(fā)的觸發(fā)器列表時(shí),它會自動等待:

你知道怎么實(shí)現(xiàn)定時(shí)任務(wù)嗎?

?開始獲取鎖等待是為了定期執(zhí)行,后面的獲取鎖等待是為了在等最近可以觸發(fā)的觸發(fā)器列表。

到這里筆者和讀者們大概也明白了quartz的獲取判斷與觸發(fā)邏輯,理解了第二大主流程脈絡(luò),當(dāng)然里面的邏輯肯定不止這些,但是筆者認(rèn)為理解了主流程脈絡(luò)結(jié)合實(shí)踐加深吃入程度即可。

?Quartz框架的實(shí)現(xiàn)demo實(shí)踐

筆者也做了一個(gè)小demo實(shí)現(xiàn)我們的計(jì)劃任務(wù)業(yè)務(wù)需求,當(dāng)然僅僅供讀者們閱讀與學(xué)習(xí),不支持生產(chǎn)使用。

依賴:

        <!-- quartz依賴 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-quartz</artifactId>
            <version>2.5.4</version>
        </dependency>

quartz.sql

#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 定時(shí)觸發(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)
);

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)
);


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)
);


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)
);


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)
);


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)
);


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)
);


CREATE TABLE QRTZ_PAUSED_TRIGGER_GRPS
(
    SCHED_NAME VARCHAR(120) NOT NULL,
    TRIGGER_GROUP  VARCHAR(200) NOT NULL,
    PRIMARY KEY (SCHED_NAME,TRIGGER_GROUP)
);

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)
);


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)
);

CREATE TABLE QRTZ_LOCKS
(
    SCHED_NAME VARCHAR(120) NOT NULL,
    LOCK_NAME  VARCHAR(40) NOT NULL,
    PRIMARY KEY (SCHED_NAME,LOCK_NAME)
);

配置文件application.yml

server:
  port: 9000
spring:
  application:
    name: quartz-service
  datasource:
    type: com.zaxxer.hikari.HikariDataSource
    url: jdbc:mysql://localhost:3306/your quartz db ?useUnicode=true&characterEncoding=utf8
    username: your db username
    password: your db password
    driver-class-name: com.mysql.cj.jdbc.Driver
  quartz:
    # 相關(guān)屬性配置
    properties:
      org:
        quartz:
          # 數(shù)據(jù)源
          dataSource:
            globalJobDataSource:
              # URL必須大寫
              URL: jdbc:mysql://127.0.0.1:3306/your quartz db ?useUnicode=true&characterEncoding=utf-8&useSSL=false
              driver: com.mysql.cj.jdbc.Driver
              maxConnections: 5
              username: your db username
              password: your db password 
              # 必須指定數(shù)據(jù)源類型
              provider: hikaricp
          scheduler:
            instanceName: globalScheduler
            # 實(shí)例id
            #instanceId: AUTO
            type: com.alibaba.druid.pool.DruidDataSource
          jobStore:
            # 數(shù)據(jù)源
            dataSource: globalJobDataSource
            # JobStoreTX將用于獨(dú)立環(huán)境,提交和回滾都將由這個(gè)類處理
            class: org.quartz.impl.jdbcjobstore.JobStoreTX
            # 驅(qū)動配置
            driverDelegateClass: org.quartz.impl.jdbcjobstore.StdJDBCDelegate
            # 表前綴
            tablePrefix: QRTZ_
          # 線程池配置
          threadPool:
            class: org.quartz.simpl.SimpleThreadPool
            # 線程數(shù)
            threadCount: 10
            # 優(yōu)先級
            threadPriority: 5

實(shí)體類

@Data
public class JobInfo {
    private String jobName;
    private String jobGroup;
    private String triggerName;
    private String triggerGroup;
    private String cron;
    private String className;
    private String status;
    private String nextTime;
    private String prevTime;
    private String config;
}
@Data
public class Message {
    private String title;
    private String message;
    private String jobGroup;
    private String jobName;
    private String sendUserName;
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    private String sendDate;
}

?核心任務(wù)處理類

@Component
public class JobHandler {
    @Resource
    private Scheduler scheduler;
    
    public void addJob(JobInfo jobInfo) throws SchedulerException, ClassNotFoundException {
        Assert.notNull(jobInfo, LocalDateTime.now().toString() + "-> 任務(wù)信息為null拒絕生成定時(shí)任務(wù)");
        JobKey jobKey = JobKey.jobKey(jobInfo.getJobName(), jobInfo.getJobGroup());
        if (!scheduler.checkExists(jobKey)) {
            Class<Job> jobClass = (Class<Job>) Class.forName(jobInfo.getClassName());
            JobDetail jobDetail = JobBuilder.newJob(jobClass)
                    .withIdentity(jobKey)
                    .withIdentity(jobInfo.getJobName(), jobInfo.getJobGroup())
                    .withIdentity(jobInfo.getJobName())
                    .build();
            jobDetail.getJobDataMap().put("config", jobInfo.getConfig());
            TriggerKey triggerKey = TriggerKey.triggerKey(jobInfo.getTriggerName(), jobInfo.getTriggerGroup());
            Trigger trigger = TriggerBuilder.newTrigger()
                    .withIdentity(triggerKey)
                    .withSchedule(CronScheduleBuilder.cronSchedule(jobInfo.getCron()))
                    .build();
            scheduler.scheduleJob(jobDetail,trigger);
        } else {
            throw new SchedulerException(jobInfo.getJobName() + "->定時(shí)任務(wù)計(jì)劃庫已存在");
        }
    }

    public void pauseJob(String jobGroup, String jobName) throws SchedulerException {
        JobKey jobKey = JobKey.jobKey(jobName, jobGroup);
        if (scheduler.checkExists(jobKey)) {
            scheduler.pauseJob(jobKey);
        }
    }

    public void resumeJob(String jobGroup, String jobName) throws SchedulerException {
        JobKey jobKey = JobKey.jobKey(jobName, jobGroup);
        if (scheduler.checkExists(jobKey)) {
            scheduler.resumeJob(jobKey);
        }
    }

    public Boolean deleteJob(String jobGroup, String jobName) throws SchedulerException {
        JobKey jobKey = JobKey.jobKey(jobName, jobGroup);
        if (scheduler.checkExists(jobKey)) {
            return scheduler.deleteJob(jobKey);
        }
        return false;
    }
    
    public JobInfo getJobInfo(String jobGroup, String jobName) throws SchedulerException {
        JobKey jobKey = JobKey.jobKey(jobName,jobGroup);
        if (!scheduler.checkExists(jobKey)){
            return null;
        }
        List<? extends Trigger> triggersOfJob = scheduler.getTriggersOfJob(jobKey);
        Assert.notNull(triggersOfJob, LocalDateTime.now().toString()+"->觸發(fā)器信息為空->"+jobGroup+"/"+jobName);
        TriggerKey key = triggersOfJob.get(0).getKey();
        Trigger.TriggerState state = scheduler.getTriggerState(key);
        JobDetail jobDetail = scheduler.getJobDetail(jobKey);
        JobInfo jobInfo = new JobInfo();
        jobInfo.setJobGroup(jobGroup);
        jobInfo.setJobName(jobName);
        jobInfo.setTriggerGroup(key.getGroup());
        jobInfo.setJobName(key.getName());
        jobInfo.setClassName(jobDetail.getJobClass().getName());
        jobInfo.setStatus(state.toString());
        if (Objects.nonNull(jobDetail.getJobDataMap())){
            jobInfo.setConfig(JSONObject.toJSONString(jobDetail.getJobDataMap()));
        }
        CronTrigger trigger = (CronTrigger) triggersOfJob.get(0);
        jobInfo.setCron(trigger.getCronExpression());
        return jobInfo;
    }
}

事件類型

@Component
@DisallowConcurrentExecution
public class PlanRemindJob implements Job {
    @Override
    public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {
        System.out.println("執(zhí)行計(jì)劃任務(wù)" + jobExecutionContext.getJobDetail().getDescription());
    }
}
@Component
@DisallowConcurrentExecution
public class TimeEventJob implements Job {
    @Override
    public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {
        System.out.println("執(zhí)行定時(shí)任務(wù)" + jobExecutionContext.getJobDetail().getDescription());
    }
}

事件類型枚舉

public enum JobType {
    //計(jì)劃任務(wù)與定時(shí)任務(wù)
     PLAN_REMIND_MESSAGE ,
     TIME_EVENT_MESSAGE 
}

業(yè)務(wù)接口

public interface QuartzService {
    Boolean insertMessage(Message message, JobType jobType) throws SchedulerException, ClassNotFoundException;
    JobInfo getJob(Message message);
    Boolean deleteJob(Message message);
    Boolean resumeJob(Message message);
}

?業(yè)務(wù)實(shí)現(xiàn)類

@Service("QuartzService")
public class QuartzServiceImpl implements QuartzService {

    @Resource
    private JobHandler jobHandler;

    @Override
    public synchronized Boolean insertMessage(Message message, JobType jobType) {
        JobInfo jobInfo = JobGenerator.getJobInfo(message, jobType);
        try {
            jobHandler.addJob(jobInfo);
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    @Override
    public JobInfo getJob(Message message) {
        try {
            return jobHandler.getJobInfo(message.getJobGroup(), message.getJobName());
        } catch (SchedulerException e) {
            e.printStackTrace();
            return null;
        }
    }

    @Override
    public synchronized Boolean deleteJob(Message message) {
        try {
            return jobHandler.deleteJob(message.getJobGroup(), message.getJobName());
        } catch (SchedulerException e) {
            e.printStackTrace();
            return false;
        }
    }

    @Override
    public synchronized Boolean resumeJob(Message message) {
        try {
            jobHandler.resumeJob(message.getJobGroup(), message.getJobName());
            return true;
        } catch (SchedulerException e) {
            e.printStackTrace();
            return false;
        }
    }
}

工具類

cron與日期(“yyyy-MM-dd hh:mm:ss”)轉(zhuǎn)換類

public class CronGenerator {
    public static String geCron(Message message){
        String date = message.getSendDate().toString();
        String yyyy = date.substring(0, 4);
        String MM = date.substring(5, 7);
        String dd = date.substring(8, 10);
        String hh = date.substring(11, 13);
        String mm = date.substring(14, 16);
        String ss = date.substring(17, 19);
        return ss+" "+mm+" "+hh+" "+dd+" "+MM+" "+"? "+yyyy;
    }
}

JobInfo實(shí)體轉(zhuǎn)換工具類

public class JobGenerator {
    public static JobInfo getJobInfo(Message message, JobType jobType) {
        String cron = CronGenerator.geCron(message);
        JobInfo jobInfo = new JobInfo();
        jobInfo.setCron(cron);
        if (jobType == JobType.PLAN_REMIND_MESSAGE){
            jobInfo.setClassName("com.hlc.quartzservice.jobType.PlanRemindJob");
            jobInfo.setJobGroup(message.getJobGroup());
            jobInfo.setJobName(message.getJobName());
            jobInfo.setTriggerGroup("PLAN_REMIND_MESSAGE");
            jobInfo.setTriggerName("計(jì)劃任務(wù)觸發(fā)器");
            jobInfo.setConfig(message.getMessage());
        }else if (jobType == JobType.TIME_EVENT_MESSAGE){
            jobInfo.setClassName("com.hlc.quartzservice.jobType.TimeEventJob");
            jobInfo.setJobGroup(message.getJobGroup());
            jobInfo.setJobName(message.getJobName());
            jobInfo.setTriggerGroup("TIME_EVENT_MESSAGE");
            jobInfo.setTriggerName("定時(shí)任務(wù)觸發(fā)器");
            jobInfo.setConfig(message.getMessage());
        }
        return jobInfo;
    }
}

開啟應(yīng)用程序進(jìn)行測試

@SpringBootApplication
public class QuartzServiceApplication {

    public static void main(String[] args) {
        SpringApplication.run(QuartzServiceApplication.class, args);
        
        QuartzService quartzService = SpringContextHolder.getBean(QuartzService.class);
        Message message = new Message();
        message.setMessage("測試");
        message.setSendDate("2023-04-15 14:30:00");
        message.setTitle("消息");
        message.setSendUserName("hlc");
        try {
            quartzService.insertMessage(message, JobType.PLAN_REMIND_MESSAGE);
        } catch (SchedulerException | ClassNotFoundException e) {
            e.printStackTrace();
        }
    }
}

靜態(tài)方法獲取bean的獲取容器上下文工具類(實(shí)現(xiàn)ApplicationContextAware接口)

@Component
@Lazy(value = false)
public class SpringContextHolder implements ApplicationContextAware {
    /**
     * 將上下文靜態(tài)設(shè)置,在初始化組件時(shí)就進(jìn)行靜態(tài)上下文的覆蓋(這個(gè)覆蓋是將遠(yuǎn)spring容器的上下文對象引用加到我們預(yù)定設(shè)置)
     */
    private static ApplicationContext applicationContext = null;

    public static ApplicationContext getApplicationContext() {
        assertContextInjected();
        return applicationContext;
    }

    @SuppressWarnings("unchecked")
    public static <T> T getBean(String name) {
        assertContextInjected();
        return (T) applicationContext.getBean(name);
    }

    public static  <T> T getBean(Class<T> beanType) {
        assertContextInjected();
        return applicationContext.getBean(beanType);
    }

    @Override
    public void setApplicationContext(@NotNull ApplicationContext applicationContext) throws BeansException {
        SpringContextHolder.applicationContext = applicationContext;
    }
    
    public void destroy() {
        applicationContext = null;
    }

    private static void assertContextInjected() {
        Assert.notNull(applicationContext,
                "applicationContext屬性未注入, 請?jiān)赼pplicationContext.xml中定義SpringContextHolder.");
    }

    public static void pushEvent(ApplicationEvent event){
        assertContextInjected();
        applicationContext.publishEvent(event);
    }

}

還有另外一種方法可以測試,就是查庫,看看數(shù)據(jù)庫表中有沒有發(fā)生數(shù)據(jù)變化?

給的么詳細(xì),確定不點(diǎn)點(diǎn)贊,收藏一下嗎?文章來源地址http://www.zghlxwxcb.cn/news/detail-422125.html

到了這里,關(guān)于你知道怎么實(shí)現(xiàn)定時(shí)任務(wù)嗎?的文章就介紹完了。如果您還想了解更多內(nèi)容,請?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!

本文來自互聯(lián)網(wǎng)用戶投稿,該文觀點(diǎn)僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務(wù),不擁有所有權(quán),不承擔(dān)相關(guān)法律責(zé)任。如若轉(zhuǎn)載,請注明出處: 如若內(nèi)容造成侵權(quán)/違法違規(guī)/事實(shí)不符,請點(diǎn)擊違法舉報(bào)進(jìn)行投訴反饋,一經(jīng)查實(shí),立即刪除!

領(lǐng)支付寶紅包贊助服務(wù)器費(fèi)用

相關(guān)文章

  • 使用shedlock實(shí)現(xiàn)分布式定時(shí)任務(wù)鎖【防止task定時(shí)任務(wù)重復(fù)執(zhí)行】

    使用shedlock實(shí)現(xiàn)分布式定時(shí)任務(wù)鎖【防止task定時(shí)任務(wù)重復(fù)執(zhí)行】

    第一步:引入shedlock相關(guān)依賴 ShedLock還可以使用Mongo,Redis,Hazelcast,ZooKeeper等外部存儲進(jìn)行協(xié)調(diào),例如使用redis則引入下面的包 第二步:創(chuàng)建數(shù)據(jù)庫表結(jié)構(gòu),數(shù)據(jù)庫表的腳本如下: 第三步:添加shedlock配置類 (定時(shí)任務(wù)防重復(fù)執(zhí)行的配置類) 第四步:在啟動類上添加啟動注

    2024年02月10日
    瀏覽(33)
  • springboot---定時(shí)任務(wù)實(shí)現(xiàn)

    springboot---定時(shí)任務(wù)實(shí)現(xiàn)

    任意類中創(chuàng)建一個(gè)方法,將該方法用@scheduled注解修飾,然后在項(xiàng)目的主方法上添加@EnableScheduling注解,定時(shí)任務(wù)就會生效。 但是需要注意的是定時(shí)任務(wù)不會一開始就執(zhí)行,會等待設(shè)定的時(shí)間 1.2.1. cron cron表達(dá)式是一個(gè)字符串,字符串以5或6個(gè)空格隔開,分開共6或7個(gè)域,每一個(gè)

    2024年02月11日
    瀏覽(20)
  • Java -- 定時(shí)任務(wù)實(shí)現(xiàn)方式

    Java -- 定時(shí)任務(wù)實(shí)現(xiàn)方式

    在Java開發(fā)中,定時(shí)任務(wù)是一種十分常見的功能. 定時(shí)任務(wù)是在約定時(shí)間內(nèi)執(zhí)行的一段程序 如每天凌晨24點(diǎn)備份同步數(shù)據(jù),又或者電商平臺 30 分鐘后自動取消未支付的訂單,每隔一個(gè)小時(shí)拉取一次數(shù)據(jù)等都需要使用到定時(shí)器 批量處理數(shù)據(jù):批量統(tǒng)計(jì)上個(gè)月的某個(gè)數(shù)據(jù)。 時(shí)間驅(qū)

    2024年02月02日
    瀏覽(16)
  • SpringBoot 實(shí)現(xiàn)定時(shí)任務(wù)

    SpringBoot 實(shí)現(xiàn)定時(shí)任務(wù)

    定時(shí)任務(wù)在實(shí)際項(xiàng)目開發(fā)中很常見,并且定時(shí)任務(wù)可以在各種場景中應(yīng)用,通過自動化操作和任務(wù)的規(guī)?;芾?,提高效率、可靠性和工作質(zhì)量??梢詼p少手動操作,避免疏忽和錯(cuò)誤,并節(jié)省時(shí)間和人力資源的投入 簡單易用: 使用注解驅(qū)動的方式,簡化了定時(shí)任務(wù)的配置和

    2024年02月12日
    瀏覽(32)
  • go 中如何實(shí)現(xiàn)定時(shí)任務(wù)

    go 中如何實(shí)現(xiàn)定時(shí)任務(wù)

    定時(shí)任務(wù)是指按照預(yù)定的時(shí)間間隔或特定時(shí)間點(diǎn)自動執(zhí)行的計(jì)劃任務(wù)或操作。這些任務(wù)通常用于自動化重復(fù)性的工作,以減輕人工操作的負(fù)擔(dān),提高效率。在計(jì)算機(jī)編程和應(yīng)用程序開發(fā)中,定時(shí)任務(wù)是一種常見的編程模式,用于周期性地執(zhí)行某些操作、處理數(shù)據(jù)或觸發(fā)事件。

    2024年02月05日
    瀏覽(20)
  • Spring定時(shí)任務(wù)+webSocket實(shí)現(xiàn)定時(shí)給指定用戶發(fā)送消息

    生命無罪,健康萬歲,我是laity。 我曾七次鄙視自己的靈魂: 第一次,當(dāng)它本可進(jìn)取時(shí),卻故作謙卑; 第二次,當(dāng)它在空虛時(shí),用愛欲來填充; 第三次,在困難和容易之間,它選擇了容易; 第四次,它犯了錯(cuò),卻借由別人也會犯錯(cuò)來寬慰自己; 第五次,它自由軟弱,卻把

    2024年02月07日
    瀏覽(30)
  • 任務(wù)調(diào)度框架-如何實(shí)現(xiàn)定時(shí)任務(wù)+RabbitMQ事務(wù)+手動ACK

    任務(wù)調(diào)度框架-如何實(shí)現(xiàn)定時(shí)任務(wù)+RabbitMQ事務(wù)+手動ACK

    比如: 1.每天早上6點(diǎn)定時(shí)執(zhí)行 2.每月最后一個(gè)工作日,考勤統(tǒng)計(jì) 3.每個(gè)月25號信用卡還款 4.會員生日祝福 5.每隔3秒,自動提醒 10分鐘的超時(shí)訂單的自動取消,每隔30秒或1分鐘查詢一次訂單,拿當(dāng)前的時(shí)間上前推10分鐘 定時(shí)任務(wù),資源會有誤差的存在,如果使用定時(shí)任務(wù) 定時(shí)

    2024年02月08日
    瀏覽(26)
  • 一種輕量級定時(shí)任務(wù)實(shí)現(xiàn)

    現(xiàn)在市面上有各式各樣的分布式定時(shí)任務(wù),每個(gè)都有其獨(dú)特的特點(diǎn),我們這邊的項(xiàng)目因?yàn)橐婚_始使用的是分布式開源調(diào)度框架TBSchedule,但是這個(gè)框架依賴ZK, 由于ZK的不穩(wěn)定性和項(xiàng)目老舊無人維護(hù) ,導(dǎo)致我們的定時(shí)任務(wù)會偶發(fā)出現(xiàn)異常,比如:任務(wù)停止、任務(wù)項(xiàng)丟失、任務(wù)不

    2024年02月14日
    瀏覽(96)
  • C#--使用Quartz實(shí)現(xiàn)定時(shí)任務(wù)

    C#--使用Quartz實(shí)現(xiàn)定時(shí)任務(wù)

    Quartz.NET是一個(gè)開源的作業(yè)調(diào)度框架,非常適合在平時(shí)的工作中,定時(shí)輪詢數(shù)據(jù)庫同步,定時(shí)郵件通知,定時(shí)處理數(shù)據(jù)等。 Quartz.NET允許開發(fā)人員根據(jù)時(shí)間間隔(或天)來調(diào)度作業(yè)。它實(shí)現(xiàn)了作業(yè)和觸發(fā)器的多對多關(guān)系,還能把多個(gè)作業(yè)與不同的觸發(fā)器關(guān)聯(lián)。整合了Quartz.NET的應(yīng)

    2024年02月08日
    瀏覽(24)
  • go 利用channel實(shí)現(xiàn)定時(shí)任務(wù)

    想5秒內(nèi)結(jié)束就注釋掉select{} 在linux上后臺執(zhí)行的話,可以這樣

    2024年04月15日
    瀏覽(17)

覺得文章有用就打賞一下文章作者

支付寶掃一掃打賞

博客贊助

微信掃一掃打賞

請作者喝杯咖啡吧~博客贊助

支付寶掃一掃領(lǐng)取紅包,優(yōu)惠每天領(lǐng)

二維碼1

領(lǐng)取紅包

二維碼2

領(lǐng)紅包