SpringBoot之定時任務quartz

前言:對於Quartz(kwɔrts)以前在公司用過,比較尷尬的是真的只是用過,寫個控制器在任務系統裏配置一下cron表達式就完事 https://github.com/songwie/task。從那天起我就對Quartz失去了興趣,後來在使用SpringBoot的時候瞭解到Scheduled(Spring 3.1以後支持),就用Scheduled搭建了一個簡單的任務系統。當時我就在想怎麼弄個到點就能執行的任務,由於用Scheduled註解有很大的侷限性,查閱了好多文檔(我好後悔我當初沒有學好英語,形成如今一直很反感英文文檔,每次都是搜索中文博客(開源中國,推酷,簡書segmentfault,scdn,.....),若是我英語給力,技術也不會這麼差)仍是沒有發現比較好的解決方案,當時正好作衆籌票務APP,好比用戶下單以後30分鐘沒有支付須要將該訂單的庫存回收並改變訂單狀態爲失效。若是輪詢1秒一次的話,這樣會頻繁查詢訂單表,將全部失效時間小於當前時間的而且未支付的全部訂單設置爲失效,這樣即不能作到及時,量比較多的話還會頻繁鎖表,訂單表對於票務網站自己就很高頻的,不論是下訂單,支付過程的狀態變動,仍是查詢訂單狀態。我當時採用了很low的方式,就是查詢訂單的時候,若是失效時間小於或者等於當前時間就update該ID的狀態。對於用戶來講沒有什麼變化,若是10條訂單中只有一個就只會更新一個。問題來了,若是該用戶沒有查詢訂單是否是狀態仍是未支付的狀態呢?因此我寫了一個1分鐘一次的輪詢來解決狀態問題。今天我不是來BB這種方案,其實Quartz除了CronTrigger還有SimpleTrigger。java

1.Quartz的簡單介紹

(百度百科)Quartz是一個徹底由java編寫的開源做業調度框架,是OpenSymphony開源組織在Job scheduling領域又一個開源項目,它能夠與J2EE與J2SE應用程序相結合也能夠單獨使用。Quartz能夠用來建立簡單或爲運行十個,百個,甚至是好幾萬個Jobs這樣複雜的程序。git

2.Quartz的主要接口介紹

Scheduler – 與scheduler交互的主要API,這就是所謂的做業調度器github

Job – 你經過scheduler執行任務,你的任務類須要實現的接口;spring

JobDetail – 定義Job的實例;segmentfault

Trigger – 觸發Job的執行;併發

3.簡單的Scheduled任務

3.1 首先在主程序開啓對定時任務的支持框架

@EnableScheduling

3.2 編寫須要定時跑的代碼ide

@Scheduled(fixedRate=10000)
	public void test(){
		System.out.println("程序跑來了");
	}

經過@Scheduled註解 使用fixedRate時表示多少次執行一次,單位是毫秒網站

其實還有cron表達式屬性,具體設置能夠參考https://my.oschina.net/wangnian/blog/668209ui

4.整合Quartz

     我整合是設置時間到點執行,不是上面的cron表達式那種計劃時間或者循環執行。

4.1添加依賴

<dependency>
            <groupId>org.quartz-scheduler</groupId>
            <artifactId>quartz</artifactId>
            <version>2.2.3</version>
        </dependency>
        
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context-support</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-tx</artifactId>
        </dependency>

4.2 配置job交給spring管理

package com.yudianbank.task.config;

import org.quartz.spi.TriggerFiredBundle;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.AutowireCapableBeanFactory;
import org.springframework.scheduling.quartz.AdaptableJobFactory;
import org.springframework.stereotype.Component;

@Component
public class JobFactory extends AdaptableJobFactory {

    @Autowired
    private AutowireCapableBeanFactory capableBeanFactory;

    @Override
    protected Object createJobInstance(TriggerFiredBundle bundle) throws Exception {
        //調用父類的方法  
        Object jobInstance = super.createJobInstance(bundle);
        //進行注入  
        capableBeanFactory.autowireBean(jobInstance);
        return jobInstance;
    }

}

主要目的就是解決job類 注入其餘service或者使用Spring組件

4.3配置javaconfig bean

@Configuration
public class BeanConfig {

    @Autowired
    JobFactory jobFactory;

    /**
     * 註冊調度器
     *
     * @return
     */
    @Bean
    public SchedulerFactoryBean createSchedulerFactoryBean() {
        SchedulerFactoryBean schedulerFactoryBean = new SchedulerFactoryBean();
        schedulerFactoryBean.setJobFactory(jobFactory);
        return schedulerFactoryBean;
    }

    @Bean
    public JobDetailImpl createJobDetailImpl() {
        return new JobDetailImpl();
    }


}

4.4 任務方法

/**
     * 添加任務
     *
     * @param job             任務類
     * @param date            任務時間
     * @param jobDetailName   任務消息名字
     * @param triggerIdentity 觸發器的惟一名
     * @param description     觸發器的描述
     */
    public synchronized void addJob(Job job, Date date, String jobDetailName, String triggerIdentity, String description, String url, String bodyParameter) {
        //job類的參數
        JobDataMap jobDataMap = new JobDataMap();
        jobDataMap.put("url", url);
        jobDataMap.put("bodyParameter", bodyParameter);
        jobDataMap.put("jobDetailName", jobDetailName);
        //這是job類的任務
        jobDetail.setName(jobDetailName);
        jobDetail.setJobClass(job.getClass());
        jobDetail.setJobDataMap(jobDataMap);
        //做業觸發器
        Trigger trigger = TriggerBuilder.newTrigger().withIdentity(triggerIdentity).withDescription(description)
                // .withSchedule(SimpleScheduleBuilder.repeatSecondlyForever(3))//循環間隔多久
                .startAt(date)//執行時間
                .build();
        //做業調度器
        try {
            Scheduler scheduler = schedulerFactoryBean.getScheduler();
            //Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler();
            scheduler.scheduleJob(jobDetail, trigger);
            scheduler.start();
        } catch (SchedulerException e) {
            logger.error("任務調度器異常:", e);

4.5 編寫任務類(這裏就能夠注入service)

@Service
public class JobTask implements Job {

    static final Logger logger = LoggerFactory.getLogger(JobTask.class);

    @Autowired
    ExecuteTaskService executeTaskService;

    @Override
    public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {
        JobDataMap jobDataMap = jobExecutionContext.getMergedJobDataMap();
        String url = jobDataMap.get("url").toString();
        String bodyParameter = jobDataMap.get("bodyParameter").toString();
        String jobDetailName = jobDataMap.get("jobDetailName").toString();
        try {
            executeTaskService.execute(url, bodyParameter, jobDetailName);
        } catch (Exception ex) {
        }
    }

關於job的狀態數據(即JobDataMap)和併發性,還有一些地方須要注意。在job類上能夠加入一些註解,這些註解會影響job的狀態和併發性。

@DisallowConcurrentExecution:將該註解加到job類上,告訴Quartz不要併發地執行同一個job定義(這裏指特定的job類)的多個實例。請注意這裏的用詞。拿前一小節的例子來講,若是「SalesReportJob」類上有該註解,則同一時刻僅容許執行一個「SalesReportForJoe」實例,但能夠併發地執行「SalesReportForMike」類的一個實例。因此該限制是針對JobDetail的,而不是job類的。可是咱們認爲(在設計Quartz的時候)應該將該註解放在job類上,由於job類的改變常常會致使其行爲發生變化。

@PersistJobDataAfterExecution:將該註解加在job類上,告訴Quartz在成功執行了job類的execute方法後(沒有發生任何異常),更新JobDetail中JobDataMap的數據,使得該job(即JobDetail)在下一次執行的時候,JobDataMap中是更新後的數據,而不是更新前的舊數據。和 @DisallowConcurrentExecution註解同樣,儘管註解是加在job類上的,但其限制做用是針對job實例的,而不是job類的。由job類來承載註解,是由於job類的內容常常會影響其行爲狀態(好比,job類的execute方法須要顯式地「理解」其」狀態「)。

若是你使用了@PersistJobDataAfterExecution註解,咱們強烈建議你同時使用@DisallowConcurrentExecution註解,由於當同一個job(JobDetail)的兩個實例被併發執行時,因爲競爭,JobDataMap中存儲的數據極可能是不肯定的。

調用4.4的任務方法便可   

博客地址:https://my.oschina.net/wangnian

相關文章
相關標籤/搜索