Quartz定时任务框架(二) Quartz详解

目录

Quartz API

Scheduler调度程序、SchedulerFactory调度程序工厂

scheduler调度程序

SchedulerFactory

Job & JobDetail

JobDataMap

Job实例化的过程

job的注解声明和并发

job的其他属性

Trigger触发器

优先级Priority


Quartz API

  •  Scheduler : 和调度程序交互的主要接口
  • Job : 由调度程序执行的一个用来扩展的接口,execute方法里面执行的是job的具体行为
  • JobDetail : 定义jobs的实例
  • Trigger: 定义调度程序上的job的执行时间,用来设置任务什么时间触发
  • JobBuilder: 用来创建或者定义JobDetail实例
  • TriggerBuilder : 用来创建Trigger实例

Scheduler调度程序、SchedulerFactory调度程序工厂

scheduler调度程序

Scheduler维护一个jobDetails和triggers的注册表,当时间一到就会触发job执行

调度程序Scheduler可以通过SchedulerFactory工厂进行创建。一个已经创建的Scheduler可以通过SchedulerFactory获取。

Scheduler创建后只是出于待机状态,调用scheduler.start()方法启动,scheduler.shutdown方法关闭,isShutdown检查状态。

Scheduler调用scheduleJob方法将job加入调度,并在start方法执行后,触发job执行。

SchedulerFactory

SchedulerFactory有两个默认的实现类:DirectSchedulerFactoryStdSchedulerFactory

* DirectSchedulerFactory

DirectSchedulerFactory是一个org.quartz.SchedulerFactory的单例实现。 
这里有一些使用DirectSchedulerFactory的示例代码段: 
示例1:你可以使用createVolatileScheduler方法去创建一个不需要写入数据库的调度程序实例:

//创建一个拥有10个线程的调度程序
DirectSchedulerFactory.getInstance().createVolatileScheduler(10);  
//记得启用该调度程序
DirectSchedulerFactory.getInstance().getScheduler().start();

 另一种,创建方式:

public void createScheduler(String schedulerName, String schedulerInstanceId, ThreadPool threadPool, JobStore jobStore, String rmiRegistryHost, int rmiRegistryPort)

示例代码:

// 创建线程池
SimpleThreadPool threadPool = new SimpleThreadPool(maxThreads, Thread.NORM_PRIORITY); 
threadPool.initialize(); 

// 创建job存储器
JobStore jobStore = new RAMJobStore();

//使用所有参数创建调度程序
DirectSchedulerFactory.getInstance().createScheduler("My Quartz Scheduler", "My Instance", threadPool, jobStore, "localhost", 1099); 

// 不要忘了调用start()方法来启动调度程序
DirectSchedulerFactory.getInstance().getScheduler("My Quartz Scheduler", "My Instance").start();

 也可以使用jdbcjobstore

DBConnectionManager.getInstance().addConnectionProvider("someDatasource", new JNDIConnectionProvider("someDatasourceJNDIName"));

JobStoreTX jdbcJobStore = new JobStoreTX(); jdbcJobStore.setDataSource("someDatasource"); jdbcJobStore.setPostgresStyleBlobs(true); jdbcJobStore.setTablePrefix("QRTZ_"); jdbcJobStore.setInstanceId("My Instance");

* StdSchedulerFactory 

StdSchedulerFactory是org.quartz.SchedulerFactory的实现类,它是基于Quartz属性文件创建Quartz Scheduler 调度程序的。之前的入门案例使用的就是StdSchedulerFactory,因为我们指定了属性文件quartz.properties。

默认情况下是加载当前工作目录下的”quartz.properties”属性文件。如果加载失败,会去加载org/quartz包下的”quartz.properties”属性文件。我们使用JD-GUI反编译工具打开quartz.jar,可以在org/quartz包下找到其默认的属性文件的配置信息

默认的quartz.properties中的配置信息

# Default Properties file for use by StdSchedulerFactory
# to create a Quartz Scheduler Instance, if a different
# properties file is not explicitly specified.
#

org.quartz.scheduler.instanceName: DefaultQuartzScheduler
org.quartz.scheduler.rmi.export: false
org.quartz.scheduler.rmi.proxy: false
org.quartz.scheduler.wrapJobExecutionInUserTransaction: false

org.quartz.threadPool.class: org.quartz.simpl.SimpleThreadPool
org.quartz.threadPool.threadCount: 10
org.quartz.threadPool.threadPriority: 5
org.quartz.threadPool.threadsInheritContextClassLoaderOfInitializingThread: true

org.quartz.jobStore.misfireThreshold: 60000

org.quartz.jobStore.class: org.quartz.simpl.RAMJobStore

Job & JobDetail

 Job是用来定义任务的执行行为,JobDetail则是用来创建并告知scheduler对应job的各种属性,JobDetail使用JobBuilder.newJob 创建,创建的时候必须知道job的实现类型。

任务Job有一个名称name 和组group 来关联。在一个Scheduler中这二者的组合必须是唯一的。

触发器任务计划执行表的执行”机制”。多个触发器可以指向同一个工作,但一个触发器只能指向一个工作。

 JobDetail job = JobBuilder.newJob (HelloJob.class).withIdentity("job1", "group1")
                .build();
public class HelloJob implements Job{
	public HelloJob(){}
	@Override
	public void execute(JobExecutionContext paramJobExecutionContext)
			throws JobExecutionException {
		 System.err.println("["+Thread.currentThread().getName()+"]"+"Hello World!  MyJob is executing.");
	}
}

每次scheduler执行job的execute方法都会创建一个新的实例,job执行完将它进行回收处理

当使用默认的SchedularFactory实现时,Job的实现类必须有一个无参数的构造器

Job的实现类中,定义一些状态变量是没有任何意义的,因为值不能在任务执行期间保留,因为每次执行一次,都会创建新的job实例,执行完毕就会销毁。

JobDataMap

JobDataMap是JobDetail实体的一部分,通过它能够了解Job在执行中的状态,并且用来存储一些和job执行过程中可用的数据

JobDataMap是map的一个实现类,提供了一些方法来存储和检索原始类型的数据

1. 在JobDatail创建的时候可以将数据放到JobDataMap中,在将job加到任务调度之前

Trigger trigger = TriggerBuilder.newTrigger()
                .withIdentity("trigger1", "group1")
                .usingJobData("name", "hello world! ")//usingJobData方法将数据放到jobDataMap中
                .usingJobData("age", 110l)
                .usingJobData("flag", false)
                .startNow()
                .withSchedule(SimpleScheduleBuilder.simpleSchedule()
                        .withIntervalInSeconds(1)
                        .repeatForever())            
                .build();

2. 在Job的执行期间将数据取出

public class HelloJob implements Job{
	public HelloJob(){}
	@Override
	public void execute(JobExecutionContext context)
			throws JobExecutionException {
		 JobDataMap jobDataMap = context.getJobDetail().getJobDataMap();
		 String name = jobDataMap.getString("name");
		 long age = jobDataMap.getLong("age");
		 boolean flag = jobDataMap.getBoolean("flag");
		 System.out.println("name:"+name+",age:"+age+",flag:"+flag);
		 System.err.println("["+Thread.currentThread().getName()+"]"+"Hello World!  MyJob is executing.");
	}
}

存储进去jobdataMap的数据将会被序列化,可能出现序列化的问题。显然标准的java类型存储是安全的,但是如果存放的是序列化自定义的对象,就可能出现序列化问题。

在存放key和字符串的模式中,可以将javadatamap作为数据存储,而不是存放基本类型数据,从而解决消除序列化出现的问题。

另外,Trigger也能和jobdatamap关联,当使用多个Trigger设置一个job时,对于每个trigger都可以通过jobdataMap提供独立的输入。

通过JobExecutionContext能够获取javadatamap,是jobdetail和trigger关联的javadatamap的合并,这这样获取的jobdataMap能够获取到jobDatail和Trigger触发器中的值。

 Trigger trigger = TriggerBuilder.newTrigger()
                .withIdentity("trigger1", "group1")
                .usingJobData("name", "hello world! ")//usingJobData方法将数据放到jobDataMap中
                .usingJobData("age", 110l)
                .usingJobData("flag", false)
                .startNow()
                .withSchedule(SimpleScheduleBuilder.simpleSchedule()
                        .withIntervalInSeconds(1)
                        .repeatForever())            
                .build();
public void execute(JobExecutionContext context)
			throws JobExecutionException {
		JobDataMap jobDataMap = context.getMergedJobDataMap();
	}

Job实例化的过程

可以创建一个job类,然后创建多个jobDetail,每个jobDetail都有自己的设置和jobdatamap,最后加到scheduler调度程序中。

举个例子:你可以创建一个SalesReportJob的job类,作为销售报表使用,在javadatamap中指定销售报表的名称和依据,然后创建这个job的多个jobDetail实例,例如 SalesReportForJoe和SalesReportForMik

重要:当触发器时间到了,就会加载关联的JobDetail,依赖于JobFactory的scheduler的配置进行实例化。JobFactory创建新的job实例,并调用job中set方法将值设置到jobdatamap的对应key上。jobFactory也可以扩展,通过ioc,di完成job实例的构建。

job的注解声明和并发

@DisallowConcurrentExecution: 添加在job类上,告知quartz在多线程环境下不执行job的多个实例。

注意,这个约束是基于jobDetail而不是job类上面的SalesReportJob的job类,定义了多个SalesReportFroJoe和SalesReportForMik,这两个jobDetail实例,这就意味在多线程环境下, 只能执行一个SalesReportForJoe和一个SalesReportForMik定义的job.

@PersistJobDataAfterExecution: 当执行一次execute完毕后,更新jobdatamap中的数据,这样下次执行execute方法,检索的是更新的值,而不是原始的值。

如果使用@PersistJobDataAfterExecution注解,强烈建议配合使用@DisallowConcurrentExecution,避免在多线程环境下, 多个job导致的数据问题。

job的其他属性

  • 持久化 - 如果一个任务不是持久化的,则当没有触发器关联它的时候,Quartz会从scheduler中删除它。
  • 请求恢复 - 如果一个任务请求恢复,一般是该任务执行期间发生了系统崩溃或者其他关闭进程的操作,当服务再次启动的时候,会再次执行该任务。这种情况下,JobExecutionContext.isRecovering()会返回true

Trigger触发器

TriggerBuilder 用于创建触发器Trigger。如果你没有调用withSchedule(..) 方法,会使用默认的schedule 。如果没有使用withIdentity(..) 会自动生成一个触发器名称给你。可以在触发器使用TriggerBuilder进行实例化的同时设置一些属性

jobkey: 当触发器触发的时候,执行的job

starttime:  触发器开始触发的时间

endtime: 触发器结束触发的时间,比如每5天触发一次,endtime是7月1号,那么最后的执行时间是7月5号。

优先级Priority

设置属性优先级,只有starttime相同才有意义。

当一个任务请求恢复,恢复和原始的优先级一致。

参考:http://www.quartz-scheduler.org/documentation/quartz-2.1.x/tutorials/tutorial-lesson-04.html

           https://blog.csdn.net/zixiao217/article/details/53053598

猜你喜欢

转载自blog.csdn.net/Ditto_zhou/article/details/81310786