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.

Quartz 是一个功能丰富,开源的任务调度的库, 可以和任何Java应用整合 .

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

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

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中这俩注解 , 保持其同步关系.

@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.

有几种, 我也不知道他这个有啥用 , 误触发.

猜你喜欢

转载自juejin.im/post/5e314b426fb9a02fef3a6fa4