SpringBoot整合Quartz實現定時任務的動態建立、啟動、暫停、恢復、刪除。

語言: CN / TW / HK

點選上方 "程式設計師小樂"關注, 星標或置頂一起成長

後臺回覆“大禮包”有驚喜禮包!

關注訂閱號「程式設計師小樂」,收看更多精彩內容

每日英文

When you are free from desire, you will be happy, because you will never be disappointed.

當你沒有慾望,你就會快樂,因為你永不會失望。

每日掏心話

懂得進退,才能成就人生;懂得取捨,才能淡定從容;懂得知足,才能怡養心性;懂得刪減,才能輕鬆釋然;懂得變通,才會少走彎路;懂得反思,才會提高自己。

來自:毅大師 | 責編:樂樂

連結:blog.csdn.net/qq_39648029/article/details/108993476

 

後端架構師(ID:study_tech)第 1068 次推文

 

往日回顧:京東單方面辭退38歲 P7 員工,勞動仲裁京東三次敗訴,員工復崗三天又收解聘通知

 

    

   正文   

 

看了好多文章,都只講了基礎的demo用法,也就是簡單的建立執行定時任務,對定時任務的管理卻很少。

我這裡從0開始搭建一個簡單的demo,包括定時任務的各種操作,以及API的一些用法,可以實現大多場景的需求。如:

  1. 普通定時任務的建立、啟動、停止。

  2. 動態建立定時任務,如建立一個訂單,5分鐘後執行某某操作。

 

 

一、整個 Quartz 的程式碼流程基本基本如下:

 

  1. 首先需要建立我們的任務(Job),比如取消訂單、定時傳送簡訊郵件之類的,這是我們的任務主體,也是寫業務邏輯的地方。

  2. 建立任務排程器(Scheduler),這是用來排程任務的,主要用於啟動、停止、暫停、恢復等操作,也就是那幾個api的用法。

  3. 建立任務明細(JobDetail),最開始我們編寫好任務(Job)後,只是寫好業務程式碼,並沒有觸發,這裡需要用JobDetail來和之前建立的任務(Job)關聯起來,便於執行。

  4. 建立觸發器(Trigger),觸發器是來定義任務的規則的,比如幾點執行,幾點結束,幾分鐘執行一次等等。這裡觸發器主要有兩大類(SimpleTrigger和CronTrigger)。

  5. 根據Scheduler來啟動JobDetail與Trigger

 

二、進入正題,引入依賴

 

  <dependency>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-starter-quartz</artifactId>
  </dependency>

 

三、建立Job

 

需實現Job介面,這個介面就一個execute()方法需要重寫,方法內容就是具體的業務邏輯。如果是動態任務呢,比如取消訂單,每次執行都是不同的訂單號。

這個時候就需要在建立任務(JobDetail)或者建立觸發器(Trigger)的那裡傳入引數,然後在這裡通過JobExecutionContext來獲取引數進行處理,

在公眾號程式設計師小樂後臺回覆“offer”,獲取演算法面試題和答案。

import com.dy.utils.DateUtil;
import org.quartz.DisallowConcurrentExecution;
import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import java.util.Date;
 
/**
 * @program: xiudo-ota
 * @description: 測試定時任務
 * @author: zhang yi
 * @create: 2020-10-09 14:38
 */
@DisallowConcurrentExecution//Job中的任務有可能併發執行,例如任務的執行時間過長,而每次觸發的時間間隔太短,則會導致任務會被併發執行。如果是併發執行,就需要一個數據庫鎖去避免一個數據被多次處理。
public class TestJob implements Job {
    @Override
    public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {
        System.err.println(jobExecutionContext.getJobDetail().getJobDataMap().get("name"));
        System.err.println(jobExecutionContext.getJobDetail().getJobDataMap().get("age"));
        System.err.println(jobExecutionContext.getTrigger().getJobDataMap().get("orderNo"));
        System.err.println("定時任務執行,當前時間:"+ DateUtil.formatDateTime(new Date()));
    }
}

 

四、建立任務排程器(Scheduler)

 

這裡採用Spring IOC,所以直接注入完事。如果是普通的,則需通過工廠建立。

工廠:

SchedulerFactory schedulerFactory = new StdSchedulerFactory();
Scheduler scheduler = schedulerFactory.getScheduler();

IOC:

@Autowired
private Scheduler scheduler;

 

五、建立任務明細(JobDetail)

 

/**通過JobBuilder.newJob()方法獲取到當前Job的具體實現(以下均為鏈式呼叫)
 * 這裡是固定Job建立,所以程式碼寫死XXX.class
 * 如果是動態的,根據不同的類來建立Job,則 ((Job)Class.forName("com.zy.job.TestJob").newInstance()).getClass()
 * 即是 JobBuilder.newJob(((Job)Class.forName("com.zy.job.TestJob").newInstance()).getClass())
 * */
JobDetail jobDetail = JobBuilder.newJob(TestJob.class)
        /**給當前JobDetail新增引數,K V形式*/
        .usingJobData("name","zy")
        /**給當前JobDetail新增引數,K V形式,鏈式呼叫,可以傳入多個引數,在Job實現類中,可以通過jobExecutionContext.getJobDetail().getJobDataMap().get("age")獲取值*/
        .usingJobData("age",23)
        /**新增認證資訊,有3種重寫的方法,我這裡是其中一種,可以檢視原始碼看其餘2種*/
        .withIdentity("我是name","我是group")
        .build();//執行

 

六、建立觸發器(Trigger)

 

這裡主要分為兩大類SimpleTrigger、CronTrigger。

SimpleTrigger:是根據它自帶的api方法設定規則,比如每隔5秒執行一次、每隔1小時執行一次。

Trigger trigger = TriggerBuilder.newTrigger()
        /**給當前JobDetail新增引數,K V形式,鏈式呼叫,可以傳入多個引數,在Job實現類中,可以通過jobExecutionContext.getTrigger().getJobDataMap().get("orderNo")獲取值*/
        .usingJobData("orderNo", "123456")
        /**新增認證資訊,有3種重寫的方法,我這裡是其中一種,可以檢視原始碼看其餘2種*/
        .withIdentity("我是name","我是group")
        /**立即生效*/
//      .startNow()
        /**開始執行時間*/
        .startAt(start)
        /**結束執行時間,不寫永久執行*/
        .endAt(start)
        /**新增執行規則,SimpleTrigger、CronTrigger的區別主要就在這裡*/
        .withSchedule(
                SimpleScheduleBuilder.simpleSchedule()
                /**每隔3s執行一次,api方法有好多規則自行檢視*/
                .withIntervalInSeconds(3)
                /**一直執行,如果不寫,定時任務就執行一次*/
                .repeatForever()
        )
        .build();//執行

CronTrigger:這就比較常用了,是基於Cron表示式來實現的。

CronTrigger  trigger = TriggerBuilder.newTrigger()
        /**給當前JobDetail新增引數,K V形式,鏈式呼叫,可以傳入多個引數,在Job實現類中,可以通過jobExecutionContext.getTrigger().getJobDataMap().get("orderNo")獲取值*/
        .usingJobData("orderNo", "123456")
        /**新增認證資訊,有3種重寫的方法,我這裡是其中一種,可以檢視原始碼看其餘2種*/
        .withIdentity("我是name","我是group")
        /**立即生效*/
//      .startNow()
        /**開始執行時間*/
        .startAt(start)
        /**結束執行時間,不寫永久執行*/
        .endAt(start)
        /**新增執行規則,SimpleTrigger、CronTrigger的區別主要就在這裡,我這裡是demo,寫了個每2分鐘執行一次*/
        .withSchedule(CronScheduleBuilder.cronSchedule("0 0/2 * * * ?"))
        .build();//執行

注意:.startNow( )和.startAt( )這裡有個坑,這兩個方法是對同一個成員變數進行修改的 也就是說startAt和startNow同時呼叫的時候任務開始的時間是按後面呼叫的方法為主的,誰寫在後面用誰。



 

七、啟動任務

 

/**新增定時任務*/
scheduler.scheduleJob(jobDetail, trigger);
if (!scheduler.isShutdown()) {
    /**啟動*/
    scheduler.start();
}

以上,任務的建立啟動都完事了,後面就是任務的暫停、恢復、刪除。比較簡單,大致原理就是我們在建立任務明細(JobDetail)和建立觸發器(Trigger)時,會呼叫.withIdentity(key,group)來傳入認證資訊,後續就是根據這些認證資訊來管理任務(通過api方法)

 

八、任務的暫停

 

scheduler.pauseTrigger(TriggerKey.triggerKey("我是剛才寫的name","我是剛才寫的group"));

 

九、任務的恢復

 

scheduler.resumeTrigger(TriggerKey.triggerKey("我是剛才寫的name","我是剛才寫的group"));

 

根據你寫的方式來獲取。

在公眾號程式設計師小樂後臺回覆“Java”,獲取Java面試題和答案。

 

十、任務的刪除

 

scheduler.pauseTrigger(TriggerKey.triggerKey("我是剛才寫的name","我是剛才寫的group"));//暫停觸發器
scheduler.unscheduleJob(TriggerKey.triggerKey("我是剛才寫的name","我是剛才寫的group"));//移除觸發器
scheduler.deleteJob(JobKey.jobKey("我是剛才寫的name","我是剛才寫的group"));//刪除Job

最後附上基本程式碼,Job實現在上面:

    @Autowired
    private Scheduler scheduler;
 
    @PostMapping("/Quartz")
    @ApiOperation(value = "定時任務_建立", notes = "建立")
    @ResponseBody
    public Object quartz(@RequestParam("orderNo")  String orderNo) throws Exception {
        Date start=new Date(System.currentTimeMillis() + 7 * 1000);//當前時間7秒之後
 
        /**通過JobBuilder.newJob()方法獲取到當前Job的具體實現(以下均為鏈式呼叫)
         * 這裡是固定Job建立,所以程式碼寫死XXX.class
         * 如果是動態的,根據不同的類來建立Job,則 ((Job)Class.forName("com.zy.job.TestJob").newInstance()).getClass()
         * 即是 JobBuilder.newJob(((Job)Class.forName("com.zy.job.TestJob").newInstance()).getClass())
         * */
        JobDetail jobDetail = JobBuilder.newJob(TestJob.class)
                /**給當前JobDetail新增引數,K V形式*/
                .usingJobData("name","zy")
                /**給當前JobDetail新增引數,K V形式,鏈式呼叫,可以傳入多個引數,在Job實現類中,可以通過jobExecutionContext.getJobDetail().getJobDataMap().get("age")獲取值*/
                .usingJobData("age",23)
                /**新增認證資訊,有3種重寫的方法,我這裡是其中一種,可以檢視原始碼看其餘2種*/
                .withIdentity(orderNo)
                .build();//執行
 
 
        Trigger trigger = TriggerBuilder.newTrigger()
                /**給當前JobDetail新增引數,K V形式,鏈式呼叫,可以傳入多個引數,在Job實現類中,可以通過jobExecutionContext.getTrigger().getJobDataMap().get("orderNo")獲取值*/
                .usingJobData("orderNo", orderNo)
                /**新增認證資訊,有3種重寫的方法,我這裡是其中一種,可以檢視原始碼看其餘2種*/
                .withIdentity(orderNo)
                /**立即生效*/
//      .startNow()
                /**開始執行時間*/
                .startAt(start)
                /**結束執行時間*/
//        .endAt(start)
                /**新增執行規則,SimpleTrigger、CronTrigger的區別主要就在這裡*/
                .withSchedule(
                        SimpleScheduleBuilder.simpleSchedule()
                                /**每隔1s執行一次*/
                                .withIntervalInSeconds(3)
                                /**一直執行,*/
                                .repeatForever()
                )
                .build();//執行
 
//CronTrigger  trigger = TriggerBuilder.newTrigger()
//        /**給當前JobDetail新增引數,K V形式,鏈式呼叫,可以傳入多個引數,在Job實現類中,可以通過jobExecutionContext.getTrigger().getJobDataMap().get("orderNo")獲取值*/
//        .usingJobData("orderNo", orderNo)
//        /**新增認證資訊,有3種重寫的方法,我這裡是其中一種,可以檢視原始碼看其餘2種*/
//        .withIdentity(orderNo)
//        /**開始執行時間*/
//        .startAt(start)
//        /**結束執行時間*/
//        .endAt(start)
//        /**新增執行規則,SimpleTrigger、CronTrigger的區別主要就在這裡*/
//        .withSchedule(CronScheduleBuilder.cronSchedule("* 30 10 ? * 1/5 2018"))
//        .build();//執行
 
 
        /**新增定時任務*/
        scheduler.scheduleJob(jobDetail, trigger);
        if (!scheduler.isShutdown()) {
            /**啟動*/
            scheduler.start();
        }
        System.err.println("--------定時任務啟動成功 "+new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date())+" ------------");
        return "ok";
    }
 
    @PostMapping("/shutdown")
    @ApiOperation(value = "定時任務_停止", notes = "停止")
    @ResponseBody
    public Object shutdown(@RequestParam("orderNo")  String orderNo) throws IOException, SchedulerException {
        scheduler.pauseTrigger(TriggerKey.triggerKey(orderNo));//暫停Trigger
        return "";
    }
 
    @PostMapping("/resume")
    @ApiOperation(value = "定時任務_恢復", notes = "恢復")
    @ResponseBody
    public Object resume(@RequestParam("orderNo")  String orderNo) throws IOException, SchedulerException {
        scheduler.resumeTrigger(TriggerKey.triggerKey(orderNo));//恢復Trigger
        return "ok";
    }
 
    @PostMapping("/del")
    @ApiOperation(value = "定時任務_刪除", notes = "刪除")
    @ResponseBody
    public Object del(@RequestParam("orderNo")  String orderNo) throws IOException, SchedulerException {
        scheduler.pauseTrigger(TriggerKey.triggerKey(orderNo));//暫停觸發器
        scheduler.unscheduleJob(TriggerKey.triggerKey(orderNo));//移除觸發器
        scheduler.deleteJob(JobKey.jobKey(orderNo));//刪除Job
        return "ok";
    }

 

完事。。。。。。,如果想讓定時任務在啟動專案後自動啟動,則需要持久化任務,可以把基本資訊儲存在資料庫,專案啟動時啟動完,或者做分散式任務。

 

PS:歡迎在留言區留    下你的觀點,一起討論提高。如果今天的文章讓你有新的啟發,歡迎轉發分享給更多人。

歡迎加入後端架構師交流群,在後臺回覆“學習”即可。

猜你還想看

阿里、騰訊、百度、華為、京東最新面試題彙集

任正非在榮耀送別會上的講話:一旦“離婚”就不要藕斷絲連,要做華為全球最強的競爭對手

網際網路公司忽悠員工的黑話,套路太深了。。。

實戰:上億資料如何秒查

嘿,你在看嗎