终于有时间写博客了,这次详细记录下spring 3.X中quartz框架如何持久化调度任务相关知识点,按照目录编排。
1.介绍
1.1quartz概述
Quartz包含三个抽象概念,调度器(谁管理),任务(干什么),触发器(什么时候干).其中几个名词大致介绍下:
@1:Job:接口,实现接口方法定义任务
@2:JobDetail:关联Job实现类和Schedular的类,其构造函数,指定Job实现类,任务在Schedular中的组名和Job名称
@3:Trigger:触发器类,描述触发事件,包含简单的时间规则和复杂的时间规则,SimpleTrigger和CronTrigger
@4:Schedular:Quartz容器,包含Trigger和JobDetail,大致结构如图
持久化的意思
之前的文章所做的demo是将定时任务的信息保存在内存中的,见以下配置
org.quartz.jobStore.class = org.quartz.simpl.RAMJobStore
如果用内存记录定时任务信息,应用重新启动后,定时任务信息将会丢失。
比如,用户A通过系统设置1小时后执行Z操作,设置好后的,因系统重新启动,
新启动的系统将会丢失“1小时后执行Z操作”的定时任务。
如果,我们需要在系统意外(或非意外)重新启动后,仍保留定时任务信息,可以使用数据库存储定时任务信息。
org.quartz.jobStore.class = org.quartz.impl.jdbcjobstore.JobStoreTX
org.quartz.jobStore.class = org.quartz.simpl.RAMJobStore
如果用内存记录定时任务信息,应用重新启动后,定时任务信息将会丢失。
比如,用户A通过系统设置1小时后执行Z操作,设置好后的,因系统重新启动,
新启动的系统将会丢失“1小时后执行Z操作”的定时任务。
如果,我们需要在系统意外(或非意外)重新启动后,仍保留定时任务信息,可以使用数据库存储定时任务信息。
org.quartz.jobStore.class = org.quartz.impl.jdbcjobstore.JobStoreTX
1.2持久化12张表
建立数据存储表
因为需要把quartz的数据保存到数据库,所以要建立相关的数据库。这个可以从下载到的quartz包里面找到对应的sql脚本,目前可以支持MySQL,DB2,Oracle等主流的数据库,自己可以根据项目需要选择合适的脚本运行。
我的项目是mysql的,就在数据中建立了一个quartz的database,然后执行tables_mysql_innodb.sql脚本建表。其中脚本 文件位于:quartz-1.6.6\docs\dbTables
12张表:
pdm结构:
具体的pdm,文章最后将会以附件形式上传。
2.持久化配置
quartz.xml:
- <?xml version="1.0" encoding="utf-8"?>
- <beans xmlns="http://www.springframework.org/schema/beans"
- xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop"
- xmlns:tx="http://www.springframework.org/schema/tx"
- xsi:schemaLocation="http://www.springframework.org/schema/beans
- http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
- http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd
- http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd">
- <!-- quartz持久化存储 -->
- <bean id="quartzScheduler" class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
- <property name="dataSource">
- <ref bean="dataSource"/>
- </property>
- <property name="applicationContextSchedulerContextKey" value="applicationContextKey" />
- <property name="configLocation" value="classpath:/quartz/quartz.properties"/>
- </bean>
- <!--记录调度日志服务 -->
- <bean id="simpleService" class="com.ai.sm.quartz.job.SimpleService">
- </bean>
- <!-- 任务详情 -->
- <bean id="jobDetail" class="org.springframework.scheduling.quartz.JobDetailBean">
- <property name="jobClass">
- <value>com.ai.sm.quartz.job.MyQuartzJobBean</value>
- </property>
- <property name="jobDataAsMap">
- <map>
- <entry key="simpleService">
- <ref bean="simpleService"/>
- </entry>
- </map>
- </property>
- </bean>
- <!-- 封装调度服务 -->
- <bean id="schedulerService" class="com.ai.sm.quartz.smo.SchedulerServiceImpl">
- <property name="scheduler">
- <ref bean="quartzScheduler" />
- </property>
- <property name="jobDetail">
- <ref bean="jobDetail" />
- </property>
- </bean>
- </beans>
1.工厂模式,建立一个SchedulerFactoryBean的实例,其中需要指定dataSource,configLocation,其中最重要的就是引入了quartz.properties这样一个配置文件。
2.定义一个simpleService,主要是记录任务调度日志。
3.定义JobDetailBean的实例,需要指定jobClass,调度任务处理类,这里我需要记录日志,所以property是simpleService。
4.封装一个服务schedulerService,对外提供任务的创建,销毁,启动,停用等API。
quartz.properties:
- #============================================================================
- # Configure Main Scheduler Properties
- #============================================================================
- org.quartz.scheduler.instanceName = DefaultQuartzScheduler
- org.quartz.scheduler.rmi.export = false
- org.quartz.scheduler.rmi.proxy = false
- org.quartz.scheduler.wrapJobExecutionInUserTransaction = false
- #============================================================================
- # Configure ThreadPool 配置数据库连接池
- #============================================================================
- org.quartz.threadPool.class = org.quartz.simpl.SimpleThreadPool
- org.quartz.threadPool.threadCount = 10
- org.quartz.threadPool.threadPriority = 5
- org.quartz.threadPool.threadsInheritContextClassLoaderOfInitializingThread = true
- #============================================================================
- # Configure JobStore 配置做业存储方式
- #============================================================================
- #相当于扫描频率,如果系统基于秒级,应培植成1000,quartz默认为分级(60000-分钟级别)
- org.quartz.jobStore.misfireThreshold = 60000
- #org.quartz.jobStore.class = org.quartz.simpl.RAMJobStore
- #在这里自己控制事务
- org.quartz.jobStore.class = org.quartz.impl.jdbcjobstore.JobStoreTX
- #org.quartz.jobStore.driverDelegateClass=org.quartz.impl.jdbcjobstore.HSQLDBDelegate
- org.quartz.jobStore.driverDelegateClass=org.quartz.impl.jdbcjobstore.StdJDBCDelegate
- #org.quartz.jobStore.useProperties = true
- org.quartz.jobStore.tablePrefix = QRTZ_
- org.quartz.jobStore.isClustered = false
- org.quartz.jobStore.maxMisfiresToHandleAtATime=1
3.任务调度对外API
该API主要是提供任务创建,销毁,启用,停用,并对启用/体用过程记录日志。
simpleService:
- package com.ai.sm.quartz.job;
- import java.io.Serializable;
- /**
- * 记录调度日志信息
- * @author Administrator
- * @see [相关类/方法](可选)
- * @since [产品/模块版本] (可选)
- */
- public class SimpleService implements Serializable{
- private static final long serialVersionUID = 122323233244334343L;
- private TaskSMO taskSMO = null;
- public TaskSMO getTaskSMO() {
- if (taskSMO == null) {
- taskSMO = (TaskSMO) SpringBeanInvoker.getBean("task.taskSMO");
- }
- return taskSMO;
- }
- /**
- *
- * 功能描述: 主要处理调度日志
- * 〈功能详细描述〉
- *
- * @param triggerName
- * @see [相关类/方法](可选)
- * @since [产品/模块版本](可选)
- */
- public void vsopMethod(String triggerName){
- //这里执行定时调度业务
- TriggersExecLogModel triggersExecLog =new TriggersExecLogModel();
- TriggersClassRelModel triggersClassRel = new TriggersClassRelModel();
- triggersClassRel.setRelId(Long.valueOf(triggerName));
- List list =this.getTaskSMO().queryTriggersClassRel(triggersClassRel);
- triggersExecLog.setTriggerName(triggerName);
- triggersExecLog.setStartDt(new Date());
- HttpURLConnection http_conn = null;
- Long logId= new Long(0);
- if(list.size()>0){
- triggersClassRel=(TriggersClassRelModel)list.get(0);
- triggersExecLog.setJobName(triggersClassRel.getTriggerName());
- try{
- if(triggersClassRel.getClassType()==3){
- if(triggersClassRel.getClassPara().startsWith("http")){
- logId=this.taskSMO.queryCountLogId();
- URL http_url = new URL(triggersClassRel.getClassPara());
- http_conn = (HttpURLConnection) http_url.openConnection();
- http_conn.setDoOutput(true);
- http_conn.setRequestMethod("POST");//传输用post方式
- http_conn.setRequestProperty("Content-Type","application/x-www-form-urlencoded");
- http_conn.connect();
- DataOutputStream out = new DataOutputStream(http_conn.getOutputStream());
- String str_buf = new String();
- str_buf="jobId="+logId;
- out.writeBytes(str_buf);
- out.flush();
- out.close();
- int code = http_conn.getResponseCode();
- }else{
- JobMethodType.javaMethod(triggersClassRel.getClassPara());
- }
- }
- if(triggersClassRel.getClassType()==2){
- this.getTaskSMO().queryCall(triggersClassRel.getClassPara());
- }
- triggersExecLog.setLogId(logId);
- triggersExecLog.setEndDt(new Date());
- triggersExecLog.setStateCd("2");
- triggersExecLog.setRelClass(triggersClassRel.getClassPara());
- triggersExecLog.setCreateDt(new Date());
- triggersExecLog.setRemarks("调用成功");
- this.getTaskSMO().insertTriggersExecLog(triggersExecLog);
- }catch(Exception e){
- e.printStackTrace();
- logId=this.taskSMO.queryCountLogId();
- triggersExecLog.setLogId(logId);
- triggersExecLog.setEndDt(new Date());
- triggersExecLog.setStateCd("3");
- triggersExecLog.setCreateDt(new Date());
- triggersExecLog.setRemarks(MsgHandleUtil.getSubStr(MsgHandleUtil.getExceptionString(e), 300));
- this.getTaskSMO().insertTriggersExecLog(triggersExecLog);
- }finally{
- if(http_conn!=null)
- http_conn.disconnect();
- }
- }
- }
- }
- public class MyQuartzJobBean extends QuartzJobBean {
- private SimpleService simpleService;
- public void setSimpleService(SimpleService simpleService) {
- this.simpleService = simpleService;
- }
- @Override
- protected void executeInternal(JobExecutionContext jobexecutioncontext) {
- Trigger trigger = jobexecutioncontext.getTrigger();
- String triggerName = trigger.getName();
- simpleService.vsopMethod(triggerName);
- }
- }
SchedulerServiceImpl:
- package com.ai.sm.quartz.smo;
- import java.text.ParseException;
- import java.util.Date;
- import java.util.Map;
- import java.util.UUID;
- import org.apache.commons.lang.StringUtils;
- import org.apache.commons.lang.math.NumberUtils;
- import org.apache.commons.lang.time.DateUtils;
- import org.quartz.CronExpression;
- import org.quartz.CronTrigger;
- import org.quartz.JobDetail;
- import org.quartz.Scheduler;
- import org.quartz.SchedulerException;
- import org.quartz.SimpleTrigger;
- import org.springframework.beans.factory.annotation.Autowired;
- import org.springframework.beans.factory.annotation.Qualifier;
- import org.springframework.stereotype.Service;
- @Service("schedulerService")
- public class SchedulerServiceImpl implements SchedulerService {
- private Scheduler scheduler;
- private JobDetail jobDetail;
- @Autowired
- public void setJobDetail(@Qualifier("jobDetail") JobDetail jobDetail) {
- this.jobDetail = jobDetail;
- }
- @Autowired
- public void setScheduler(@Qualifier("quartzScheduler") Scheduler scheduler) {
- this.scheduler = scheduler;
- }
- public void schedule(String cronExpression) {
- schedule("", cronExpression);
- }
- public void schedule(String name, String cronExpression) {
- schedule( name, cronExpression,Scheduler.DEFAULT_GROUP);
- }
- public void schedule(String name, String cronExpression,String group) {
- try {
- schedule(name, new CronExpression(cronExpression),group);
- } catch (ParseException e) {
- throw new RuntimeException(e);
- }
- }
- public void schedule(CronExpression cronExpression) {
- schedule(null, cronExpression);
- }
- public void schedule(String name, CronExpression cronExpression) {
- schedule( name, cronExpression,Scheduler.DEFAULT_GROUP) ;
- }
- public void schedule(String name, CronExpression cronExpression,String group) {
- if (name == null || name.trim().equals("")) {
- name = UUID.randomUUID().toString();
- }
- try {
- scheduler.addJob(jobDetail, true);
- CronTrigger cronTrigger = new CronTrigger(name, group, jobDetail.getName(),
- Scheduler.DEFAULT_GROUP);
- cronTrigger.setCronExpression(cronExpression);
- scheduler.scheduleJob(cronTrigger);
- scheduler.rescheduleJob(cronTrigger.getName(), cronTrigger.getGroup(), cronTrigger);
- } catch (SchedulerException e) {
- throw new RuntimeException(e);
- }
- }
- public void schedule(Map<String,String> map) {
- String temp = null;
- SimpleTrigger simpleTrigger = new SimpleTrigger();
- simpleTrigger.setJobName(jobDetail.getName());
- simpleTrigger.setJobGroup(Scheduler.DEFAULT_GROUP);
- simpleTrigger.setRepeatInterval(1000L);
- temp = map.get("triggerName");
- if (StringUtils.isEmpty(StringUtils.trim(temp)) ){
- temp = UUID.randomUUID().toString();
- }else{
- temp +="&"+UUID.randomUUID().toString();
- }
- simpleTrigger.setName(temp);
- temp = map.get("triggerGroup");
- if(StringUtils.isEmpty(temp)){
- temp = Scheduler.DEFAULT_GROUP;
- }
- simpleTrigger.setGroup(temp);
- temp = map.get("startTime");
- if(StringUtils.isNotEmpty(temp)){
- simpleTrigger.setStartTime(this.parseDate(temp));
- }
- temp = map.get("endTime");
- if(StringUtils.isNotEmpty(temp)){
- simpleTrigger.setEndTime(this.parseDate(temp));
- }
- temp = map.get("repeatCount");
- if(StringUtils.isNotEmpty(temp) && NumberUtils.toInt(temp) > 0){
- simpleTrigger.setRepeatCount(NumberUtils.toInt(temp));
- }
- temp = map.get("repeatInterval");
- if(StringUtils.isNotEmpty(temp) && NumberUtils.toLong(temp) > 0){
- simpleTrigger.setRepeatInterval(NumberUtils.toLong(temp)*1000);
- }
- try {
- scheduler.addJob(jobDetail, true);
- scheduler.scheduleJob(simpleTrigger);
- scheduler.rescheduleJob(simpleTrigger.getName(), simpleTrigger.getGroup(), simpleTrigger);
- } catch (SchedulerException e) {
- throw new RuntimeException(e);
- }
- }
- public void pauseTrigger(String triggerName,String group){
- try {
- scheduler.pauseTrigger(triggerName, group);
- } catch (SchedulerException e) {
- throw new RuntimeException(e);
- }
- }
- public void resumeTrigger(String triggerName,String group){
- try {
- //Trigger trigger = scheduler.getTrigger(triggerName, group);
- scheduler.resumeTrigger(triggerName, group);
- } catch (SchedulerException e) {
- throw new RuntimeException(e);
- }
- }
- public boolean removeTrigdger(String triggerName,String group){
- try {
- scheduler.pauseTrigger(triggerName, group);
- return scheduler.unscheduleJob(triggerName, group);
- } catch (SchedulerException e) {
- throw new RuntimeException(e);
- }
- }
- private Date parseDate(String time){
- try {
- return DateUtils.parseDate(time, new String[]{"yyyy-MM-dd HH:mm"});
- } catch (ParseException e) {
- throw new RuntimeException(e);
- }
- }
- }