Spring中任务调度

2019-11-16  本文已影响0人  Snipers_onk

任务调度

任务调度即在特定的时间点执行指定的操作。任务调度本身设计多线程并发,运行时间规则制定及解析,运行现场保持及恢复,线程池维护等。

quartz是任务调度的成熟解决方案,功能强大使用简单。Spring提供了集成quartz的功能,也为JDK Timer 和 Excutor提供了支持。

在Spring中提供了一系列FactoryBean,可以很轻松的创建任务调度的实例;Spring还提供了几个工具类,可以将某个具体的Bean的方法作为被调度的任务;Spring还提供了支持线程池的执行调度器,它提供了一个抽象层,屏蔽了Java 1.3、JAVA 1.4、JAVA 1.5及Java EE之间的差异。

Quartz

Quartz允许开发人员灵活的定义触发器的调度时间表,并可对触发器和任务进行关联。Quartz提供了调度运行环境的持久化机制,可以保存并恢复调度现场。Quartz提供了监听器,各种插件,线程池等功能。(以下代码基于Quartz 1.8.6)

基础结构

Quartz对任务调度进行了高度的抽象,提出了调度器,任务和触发器三个核心概念,并在org.quartz这个包中通过接口和类对核心概念进行描述。

Job

Job 是一个接口,内部只有一个方法。通过实现该接口定义需要执行的任务。JobExcutionContext提供了调度上下文的各种信息。Job运行时的信息保存在JobDataMap实例中。

public interface Job {
    void execute(JobExecutionContext context)
        throws JobExecutionException;
}
JobDetail

Quartz每次执行任务时,都根据接收的Job的实现类Class,通过反射创建一个Job实例,因此需要一个类对Job实现类和其他配置进行描述,如Job名称,描述,关联监听器等(类似于Spring中的BeanDefinition)。

public interface JobDetail extends Serializable, Cloneable {
    public JobKey getKey();
    public String getDescription();
    public Class<? extends Job> getJobClass();
    public JobDataMap getJobDataMap();
    ...
}
JobDataMap

JobDataMap中可以包含不限量的(序列化的)数据对象,在job实例执行的时候,可以使用其中的数据;JobDataMap是Java Map接口的一个实现,额外增加了一些便于存取基本类型的数据的方法。
将job加入到scheduler之前,在构建JobDetail时,可以将数据放入JobDataMap。
方式有两种

直接在构建JobDetail时通过JobBuilder的usingJobData方法将数据放入JobDataMap中。方法有两种:直接添加数据或者构造JobDataMap

//构造JobDataMap
JobDataMap jobDataMap = new JobDataMap();
jobDataMap.put("jobData2", "hello 2");

JobDetail job = JobBuilder.newJob(SimpleJob.class)
    .withIdentity("helloJob","hello")
    .usingJobData("jobData1","hello 1") //添加数据
    .usingJobData(jobDataMap)
    .build();

也可以在job类中,为JobDataMap中存储的数据的key增加set方法,那么Quartz的默认JobFactory实现在job被实例化的时候会自动调用这些set方法,这样就不需要在execute()方法中显式地从map中取数据了。

public class SimpleJob implements Job {

    private String jobData1;
    private String jobData2;

    public void setJobData1(String jobData1) {
        this.jobData1 = jobData1;
    }
    public void setJobData2(String jobData2) {
        this.jobData2 = jobData2;
    }

    @Override
    public void execute(JobExecutionContext context) throws JobExecutionException {
//        System.out.println(context.getJobDetail().getJobDataMap().get("jobData1"));
//        System.out.println(context.getJobDetail().getJobDataMap().get("jobData2"));
        System.out.println(jobData1);
        System.out.println(jobData2);
        System.out.println("hello,quartz!"+ context.getJobDetail().getKey()+":::"+ context.getTrigger().getKey());
    }
}
Trigger

Trigger接口描述触发Job执行的时间触发规则。主要由两个子类接口:仅需要触发一次或以固定时间间隔执行时,适合选择SimpleTriggerCronTrigger适合复杂的调度方案,通过Cron表达式定义时间规则。

Calendar

org.quartz.Calendarjava.util.Calendar不同,它是一些日历特定时间点的集合。一个Trigger可以和多个Calendar关联,以便排除或包含某些时间点。例如每周一上午9点执行任务,如果遇到法定节假日不执行。针对不同的时间段类型,Quartz提供了不同的实现类,如AnnualCalendar,HolidayCalendar,MonthlyCalendar等。

Scheduler

代表一个Quartz独立运行的容器,Trigger和JobDetail可以注册到Scheduler中,二者在Scheduler中各自拥有组和名称,组成了key。key是Scheduler查找定位容器中某一对象的依据,所以必须唯一(Trigger和JobDetail的key可以相同,因为类型不一样,处在不同的集合中)。

Scheduler定义了多个接口方法,允许外部通过组及名称对容器中的Trigger和JobDetail进行访问控制。

Scheduler可以将Trigger绑定到某一个JobDetail,这样当Trigger触发时,对应Job会被执行,一个Job可以对应多个Trigger,但是一个Trigger只能对应一个Job。

Scheduler实例通过SchedulerFactory创建,Scheduler拥有一个SchedulerContext,SchedulerContext内部维护一个Map,以键值对形式保存上下文信息。job和Trigger都可以访问SchedulerContext内的信息。

Scheduler的生命周期

Scheduler的生命期, 从SchedulerFactory创建它时开始,到Scheduler调用shutdown()方法时结束;Scheduler被创建后,可以增加、删除和列举Job和Trigger,以及执行其它与调度相关的操作(如暂停Trigger)。但是,Scheduler只有在调用start()方法后,才会真正地触发trigger(即执行job)

JobBuilder

用于定义/构建JobDetail实例,用于定义作业的实例。

ThreadPool

Scheduler使用一个线程池作为任务运行的基础设施,任务通过共享线程池中的线程来提高运行效率。

SimpleTrigger

Demo
public class SimpleJob implements Job {
    @Override
    public void execute(JobExecutionContext context) throws JobExecutionException {
        System.out.println("hello,quartz!"+ context.getJobDetail().getKey()+":::"+ context.getTrigger().getKey());
    }
}
public static void main(String[] args) throws Exception{
    //构建SchedulerFactory实例
    SchedulerFactory schedFact = new StdSchedulerFactory();

    //获取Scheduler实例
    Scheduler scheduler = schedFact.getScheduler();

    //构建JobDetail实例
    JobDetail job = JobBuilder.newJob(SimpleJob.class)
        .withIdentity("helloJob","hello")
        .build();

    //构建Trigger实例
    SimpleTrigger trigger = TriggerBuilder.newTrigger()
        .withIdentity("helloTrigger","hello")
        .startNow()         .withSchedule(SimpleScheduleBuilder.simpleSchedule().withIntervalInSeconds(5).repeatForever())
        .build();

    //将JobDetail实例和Trigger实例加入到调度容器
    scheduler.scheduleJob(job,trigger);

    //启动容器
    scheduler.start();

}
Misfire策略

CronTrigger

CronTrigger能提供比SimpleTrigger更具有实际意义的调度方案,调度规则基于Cron表达式。

Demo
TriggerBuilder.newTrigger()
    .withSchedule(CronScheduleBuilder.cronSchedule("0 0/30 * * * ? "))
    .forJob("jobA", "groupA")
    .build();
Misfire策略
Cron表达式

Cron([krɑn]):代表100万年,是英文中最大的时间单位。

Cron表达式对特殊字符的大小写不敏感。

时间 强制 允许值 允许的特殊字符
Seconds yes 0-59 ,_*/
Minutes yes 0-59 ,_*/
Hours yes 0-23 ,_*/
Day Of month yes 1-31 ,_*?/LW
Month yes 1-12 or JAX-DEC ,_*/
Day of Week yes 1-7 or SUN-SAT ,_*?/L#
Year no empty,1970-2099 ,_*/

Spring中使用Quartz

在Spring中使用Quartz比较简单,导入相关依赖,进行配置即可。(后续补充)

<bean name="quartzScheduler"
      class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
    <property name="dataSource">
        <ref bean="myDataSource" /> //在这里指定数据源,配置中会失效
    </property>
    <!-- 指定Spring容器 -->
    <property name="applicationContextSchedulerContextKey" value="applicationContextKey" />
    <property name="configLocation" value="classpath:quartz.properties" />
</bean>
#==============================================================
#Configure Main Scheduler Properties
#==============================================================
org.quartz.scheduler.instanceName = quartzScheduler
org.quartz.scheduler.instanceId = AUTO

#==============================================================
#Configure JobStore
#==============================================================
org.quartz.jobStore.class = org.quartz.impl.jdbcjobstore.JobStoreTX
org.quartz.jobStore.driverDelegateClass = org.quartz.impl.jdbcjobstore.StdJDBCDelegate
org.quartz.jobStore.tablePrefix = QRTZ_
org.quartz.jobStore.isClustered = true
org.quartz.jobStore.clusterCheckinInterval = 20000
#org.quartz.jobStore.dataSource = myDS

#==============================================================
#Configure DataSource
#==============================================================
#org.quartz.dataSource.myDS.driver = com.mysql.jdbc.Driver
#org.quartz.dataSource.myDS.URL =jdbc:mysql://localhost:3306/quartz_db?useUnicode=true&characterEncoding=UTF-8
#org.quartz.dataSource.myDS.user = root
#org.quartz.dataSource.myDS.password = 123456
#org.quartz.dataSource.myDS.maxConnections = 30

#==============================================================
#Configure ThreadPool
#==============================================================
org.quartz.threadPool.class = org.quartz.simpl.SimpleThreadPool
org.quartz.threadPool.threadCount = 100
org.quartz.threadPool.threadPriority = 5
org.quartz.threadPool.threadsInheritContextClassLoaderOfInitializingThread = true

上一篇 下一篇

猜你喜欢

热点阅读