Quartz - 任務調度框架(能夠分佈式)

​ Quartz is a richly featured, open source job scheduling library that can be integrated within virtually any Java application - from the smallest stand-alone application to the largest e-commerce system.java

Quartz 是一個功能豐富,開源的任務調度的庫, 能夠和任何Java應用整合 .git

項目地址 : github.com/quartz-sche…github

快速開始地址 : github.com/quartz-sche…spring

Demo地址 : github.com/quartz-sche…編程

1. 快速開始

public class StdSchedulerTest {
    public static void main(String[] args) {
        try {
            //1. StdSchedulerFactory工廠機制加載一個Scheduler
            Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler();

            // 2. JobBuilder 定義一個JobDetail , 工做信息
            JobDetail job = JobBuilder.newJob(HelloJob.class)
                    .withIdentity("job1", "group1")
                    .build();

            // 3. TriggerBuilder定義一個trigger , 觸發器 , 告訴你這個Job什麼時候觸發
            Trigger trigger = TriggerBuilder.newTrigger()
                    .withIdentity("trigger1", "group1")
                    .startNow()
                    .withSchedule(SimpleScheduleBuilder.simpleSchedule()
                            .withIntervalInMilliseconds(1000)
                            .repeatForever())
                    .build();
            
            // 4.告訴Scheduler , 我這個工做須要這個trigger
            scheduler.scheduleJob(job, trigger);

            //5. 啓動
            scheduler.start();

            // 這裏咱們先阻塞着.. 
            System.in.read();
            // 6. 關閉
            scheduler.shutdown();
        } catch (SchedulerException | IOException se) {
            se.printStackTrace();
        }
    }
}
複製代碼

其中 com.example.springquartz.HelloJob 須要實現org.quartz.Job 此接口安全

public class HelloJob implements Job {
    @Override
    public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {
        JobKey key = jobExecutionContext.getJobDetail().getKey();
        System.out.printf("group %s , name : %s ,\t", key.getGroup(), key.getName());
        System.out.printf("Thread : %s - %s\n", Thread.currentThread().getName(), "Hello Job !");
    }
}
複製代碼

啓動 : 日誌信息 : 會告訴你任務什麼時候調用的.服務器

11:12:09.682 [main] INFO org.quartz.impl.StdSchedulerFactory - Quartz scheduler 'DefaultQuartzScheduler' initialized from default resource file in Quartz package: 'quartz.properties'
11:12:09.682 [main] INFO org.quartz.impl.StdSchedulerFactory - Quartz scheduler version: 2.3.0
11:12:09.697 [main] INFO org.quartz.core.QuartzScheduler - Scheduler DefaultQuartzScheduler_$_NON_CLUSTERED started.
11:12:09.697 [DefaultQuartzScheduler_QuartzSchedulerThread] DEBUG org.quartz.core.QuartzSchedulerThread - batch acquisition of 1 triggers
11:12:09.697 [DefaultQuartzScheduler_QuartzSchedulerThread] DEBUG org.quartz.simpl.PropertySettingJobFactory - Producing instance of Job 'group1.job1', class=com.example.springquartz.HelloJob
11:12:09.713 [DefaultQuartzScheduler_QuartzSchedulerThread] DEBUG org.quartz.core.QuartzSchedulerThread - batch acquisition of 1 triggers
11:12:09.713 [DefaultQuartzScheduler_Worker-1] DEBUG org.quartz.core.JobRunShell - Calling execute on job group1.job1
group  group1 , name : job1 ,	Thread : DefaultQuartzScheduler_Worker-1 - Hello Job !
複製代碼

2. JobData 同步問題

能夠經過 org.quartz.JobBuilder#usingJobData(java.lang.String, java.lang.Long) 給Job賦值參數. 對於咱們這個使用的若是是靜態的數據, 好比說數據一層不變, 或者說任務先後關係不是依賴的. 能夠不考慮同步問題.多線程

JobDetail job = JobBuilder.newJob(HelloJob.class)
        .withIdentity("job1", "group1")
        .usingJobData(HelloJob.START_TIME, System.currentTimeMillis())
        .build();
複製代碼

若是選擇了同步, 請選擇在你的Job中這倆註解 , 保持其同步關係.app

@PersistJobDataAfterExecution // 這個但願刷新JobData,因此多線程環境下共享是安全
@DisallowConcurrentExecution // 這個保證其同步執行, 其實就是任務挨着任務.
複製代碼

咱們對比使用一下. 不使用同步框架

public class HelloJob implements Job {
    public static final String START_TIME = "START_TIME";

    @Override
    public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {
        long start = jobExecutionContext.getMergedJobDataMap().getLong(START_TIME);
        long flag = System.currentTimeMillis()-start;
        System.out.printf("Thread-%d start : %s - %dms\n", tag, Thread.currentThread().getName(), System.currentTimeMillis() - start);
        sleep(2000);
        System.out.printf("Thread-%d end : %s - %dms\n", tag, Thread.currentThread().getName(), System.currentTimeMillis() - start);
    }
}
複製代碼

輸出 : 因此他們並不會由於任務延時致使同步執行, 這些任務之間都是不互相依賴的.

Thread-15 start : DefaultQuartzScheduler_Worker-1 - 15ms
Thread-1002 start : DefaultQuartzScheduler_Worker-2 - 1002ms
Thread-2001 start : DefaultQuartzScheduler_Worker-3 - 2001ms
Thread-15  end  : DefaultQuartzScheduler_Worker-1 - 2016ms
Thread-1002  end  : DefaultQuartzScheduler_Worker-2 - 3010ms
Thread-2001  end  : DefaultQuartzScheduler_Worker-3 - 4002ms
複製代碼

可是, 當咱們加入了

@DisallowConcurrentExecution
@PersistJobDataAfterExecution
public class HelloJob implements Job {
    public static final String START_TIME = "START_TIME";
    @Override
    public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {
        long start = jobExecutionContext.getMergedJobDataMap().getLong(START_TIME);
        long flag = System.currentTimeMillis()-start;
        System.out.printf("Thread-%d start : %s - %dms\n", flag, Thread.currentThread().getName(), System.currentTimeMillis() - start);
        sleep(2000);
        System.out.printf("Thread-%d end : %s - %dms\n", flag, Thread.currentThread().getName(), System.currentTimeMillis() - start);
    }
}
複製代碼

輸出 : 因此同步執行 ,

Thread-27 start : DefaultQuartzScheduler_Worker-1 - 28ms
Thread-27  end  : DefaultQuartzScheduler_Worker-1 - 2032ms
Thread-2033 start : DefaultQuartzScheduler_Worker-2 - 2033ms
Thread-2033  end  : DefaultQuartzScheduler_Worker-2 - 4034ms
複製代碼

總結一下 , 他的功能性, 以及特性都十分的強. 能夠保證其同步也能夠不一樣步, 這裏就要對比一下 . ScheduledExecutorService -> java.util.concurrent.ScheduledThreadPoolExecutor -> java.util.concurrent.ScheduledExecutorService 他的任務調度所有是同步執行的. 第二個任務必須等待第一個任務執行完畢才行. 我後面給你們展現

他的顆粒度掌握的很是的好 .

4. Trigger

1. CronScheduleBuilder - 支持 Cron

​ 計劃任務,是任務在約定的時間執行已經計劃好的工做,這是表面的意思。在Linux中,咱們常常用到 cron 服務器來完成這項工做。cron服務器能夠根據配置文件約定的時間來執行特定的任務。

怎麼使用? , 固然是觸發器裏面了 , 咱們任務調度是靠的Triggers

// 這裏是每2S執行一次

Trigger trigger = TriggerBuilder.newTrigger()
        .startNow()
        .withSchedule(CronScheduleBuilder.cronSchedule("0/2 * * * * ?"))
        .build();
複製代碼

輸出:

DefaultQuartzScheduler_Worker-1 : Echo com.example.springquartz.EchoJob@5f02d7f6
DefaultQuartzScheduler_Worker-2 : Echo com.example.springquartz.EchoJob@1d653213
DefaultQuartzScheduler_Worker-3 : Echo com.example.springquartz.EchoJob@7d2e9543
複製代碼

其實發現Quartz框架對於每個任務對象來講,他不是單例的, 每次都會依靠反射生成一個

2. SimpleScheduleBuilder

​ 簡單的編程實現

SimpleScheduleBuilder.simpleSchedule()
    // 2S 一次
                    .withIntervalInMilliseconds(2000)
    // 永遠執行下去
                    .repeatForever()
    // 0 表明執行一次, 1表明執行兩次
                // .withRepeatCount(1)
複製代碼

3. JobExecutionException 控制處理異常

JobExecutionException e = new JobExecutionException("exception");

//e.refireImmediately(); // 失敗了能夠重複執行 , This will force quartz to run this job over and over and over and over again. 
e.setUnscheduleAllTriggers(true); // 失敗了立馬中止trigger ,This will force quartz to shutdown this job so that it does not run again.
複製代碼

這個異常須要放到 Job中拋出去, 只能拋出這個異常才能控制是否執行

4. Trigger優先級 Priority

​ Set the Trigger's priority. When more than one Trigger have the same fire time, the scheduler will fire the one with the highest priority first.

倆任務一塊觸發, 優先觸發優先級高的任務 .

TriggerBuilder.newTrigger()
                    .usingJobData(EchoJob.FAVORITE_COLOR,"RED")
                    .forJob("JOB")
                    .startNow()
        			// 設置優先級
                    .withPriority(0)
                    .withSchedule(CronScheduleBuilder.cronSchedule("0/2 * * * * ?"))
                    .build();
複製代碼

5. 誤觸發

​ Instructs the Scheduler that upon a mis-fire situation, the SimpleTrigger wants to be fired now by Scheduler.

有幾種, 我也不知道他這個有啥用 , 誤觸發.

相關文章
相關標籤/搜索