xxx
---系统调度机制与开发入手介绍
日期 | 描述 | 版本 | 作者 |
| 文档创建与内容编写 | V1.0 |
|
|
|
|
|
调度机制Quartz介绍
本文只对Quartz做简单介绍。Quartz是一个开源的框架,相关的网络资料资源丰富,需要详细了解可自行去查找查看。
使用Quartz框架开发简单的调度任务,只需关注调度器、任务和触发器这3个核心的概念。它们分别是任务JobDetail、触发器Trigger、调度器Scheduler;了解三者之间关系即可实施开发。
●Job:是一个接口,只有一个方法voidexecute(JobExecutionContext context),开发者实现该接口定义运行任务,JobExecutionContext类提供了调度上下文的各种信息。Job运行时的信息保存在JobDataMap实例中;
●JobDetail:Quartz在每次执行Job时,都重新创建一个Job实例,所以它不直接接受一个Job的实例,相反它接收一个Job实现类,以便运行时通过newInstance()的反射机制实例化Job。因此需要通过一个类来描述Job的实现类及其它相关的静态信息,如Job名字、描述、关联监听器等信息,JobDetail承担了这一角色。
●Trigger:是一个类,描述触发Job执行的时间触发规则。主要有SimpleTrigger和CronTrigger这两个子类。当仅需触发一次或者以固定时间间隔周期执行,SimpleTrigger是最适合的选择;而CronTrigger则可以通过Cron表达式定义出各种复杂时间规则的调度方案:如每早晨9:00执行,周一、周三、周五下午5:00执行等;
●Calendar:org.quartz.Calendar和java.util.Calendar不同,它是一些日历特定时间点的集合(可以简单地将org.quartz.Calendar看作java.util.Calendar的集合——java.util.Calendar代表一个日历时间点,无特殊说明后面的Calendar即指org.quartz.Calendar)。一个Trigger可以和多个Calendar关联,以便排除或包含某些时间点。
●Scheduler:代表一个Quartz的独立运行容器,Trigger和JobDetail可以注册到Scheduler中,两者在Scheduler中拥有各自的组及名称,组及名称是Scheduler查找定位容器中某一对象的依据,Trigger的组及名称必须唯一,JobDetail的组和名称也必须唯一(但可以和Trigger的组和名称相同,因为它们是不同类型的)。Scheduler定义了多个接口方法,允许外部通过组及名称访问和控制容器中Trigger和JobDetail。
Scheduler可以将Trigger绑定到某一JobDetail中,这样当Trigger触发时,对应的Job就被执行。一个Job可以对应多个Trigger,但一个Trigger只能对应一个Job。可以通过SchedulerFactory创建一个Scheduler实例。Scheduler拥有一个SchedulerContext,它类似于ServletContext,保存着Scheduler上下文信息,Job和Trigger都可以访问SchedulerContext内的信息。SchedulerContext内部通过一个Map,以键值对的方式维护这些上下文数据,SchedulerContext为保存和获取数据提供了多个put()和getXxx()的方法。可以通过Scheduler# getContext()获取对应的SchedulerContext实例;
●ThreadPool:Scheduler使用一个线程池作为任务运行的基础设施,任务通过共享线程池中的线程提高运行效率。
集成Quartz
本文Quartz介绍说明了调度器、任务和触发器的关系,调度器负责将任务和触发器进行绑定。在Spring集成中,只需在配置文件中将它们关系绑定即可。
驾驶舱中使用的MVC框架为Spring,而在Spring中使用Quartz有两种方式实现:
第一种是任务类继承QuartzJobBean;
第二种则是在配置文件里定义任务类和要执行的方法,类和方法仍然是普通JavaBean类。
由于驾驶舱采取集群部署,调度需要解决集群的问题,Quartz具备任务持久化功能,且可以解决集群调度问题。持久化任务的Java类(含其属性类)必须是实现序列化接口Serializable类,否则将抛出持久化异常。第二种集成方式虽然更好解耦代码,但可能会容易增加后期增加其它调度的问题,因此排除使用,而选择使用第一种方式集成,即继承任务类QuartzJobBean。
要使Quartz任务其作用,需要进行配置,一般在xml中要有配置JobDetail、Trigger、Scheduler三个Bean配置。由于持久化需要,以上三个Bean配置建议使用JobDetailFactoryBean、CronTriggerFactoryBean、SchedulerFactoryBean等*FactoryBean,以免持久化或其它集成问题出现。
以下为第一种方式配置样例(详见quartz-config.xml文件):
<!-- 主数据账户同步调度(调用SSO提供的主数据接口来实施同步)-->
<bean name="synAccountFromMainDataJob"class="org.springframework.scheduling.quartz.JobDetailFactoryBean">
<!-- 表示任务完成之后是否依然保留到数据库,默认false -->
<propertyname="durability" value="true" />
<property name="jobClass"value="com.cmb.app.driverhome.sso.quartz.SynAccountFromMainDataJobTask"/>
</bean>
<!-- 主数据账户同步调度(调用SSO提供的主数据接口来实施同步)每天凌晨4点(0 0 4 * * ?) 执行-->
<bean id="synAccountFromMainDataTrigger" class="org.springframework.scheduling.quartz.CronTriggerFactoryBean">
<propertyname="jobDetail">
<ref bean="synAccountFromMainDataJob"/>
</property>
<propertyname="cronExpression" value="0 0/5 * * * ?" />
</bean>
<beanid="sysGlobalScheduler" lazy-init="false"autowire="no"class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
<propertyname="triggers">
<list>
<!-- 在此注释,则不作调度处理-->
<ref bean="synAccountFromMainDataTrigger"/>
</list>
</property>
<propertyname="applicationContextSchedulerContextKey"value="applicationContext" />
<propertyname="dataSource">
<refbean="dataSource" />
</property>
<propertyname="transactionManager">
<refbean="transactionManager" />
</property>
<propertyname="configLocation" value="classpath:quartz.properties"/>
</bean>
由于任务持久化需要,配置必须要有DB连接。在样例中,SchedulerFactoryBean 的dataSource 配置了连接,而configLocation属性指定quartz.properties配置文件必须为集群配置:
org.quartz.jobStore.misfireThreshold = 60000
#JDBC持久化到DB的配置
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.maxMisfiresToHandleAtATime=10
org.quartz.jobStore.isClustered= true
org.quartz.jobStore.clusterCheckinInterval = 20000
系统调度开发入手
驾驶舱定义调度任务的基类:com.cmb.app.driverhome.common.quartz. BaseScheduleTask,其继承org.springframework.scheduling.quartz.QuartzJobBean,主要为提供开发继承使用。
驾驶舱系统中开发任何的调度任务,只需继承BaseScheduleTask并重写executeInternal方法,然后在quartz-config.xml文件按集成方式说明进行配置,可参照已有任务进行配置即可实现任务调度功能。
【需要特别说明】:
配置的调度任务在工程首次启动后即被持久化到DB表(qrtz_开头的表),再次启动的时候,工程会直接读取DB表的任务去进行调度;因此对xml中trigger的表达式配置的修改可能不会其作用。解决方法为采取的方式是手工在DB表(qrtz_cron_triggers和qrtz_triggers)中修改,或将它们从DB表(qrtz_cron_triggers和qrtz_triggers)中删除,工程重启时会再自动持久化添加调度任务到DB表。