主要介绍quartz如何使用,原理请阅读下面的参考文献,整理的已经很详细。使用quartz集群的主要目的是避免单点job故障,重要job故障后自动重新执行。
加入quartz jar 包依赖
<dependency>
<groupId>com.opensymphony.quartz</groupId>
<artifactId>com.springsource.org.quartz</artifactId>
<version>1.6.2</version>
</dependency>
|
集成spring配置 ( spring 为 3.2.x)
<bean id="schedulerFactoryBean" class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
<property name="configLocation" value="classpath:quartz.properties"/>
<!-- 设置自动启动 -->
<property name="autoStartup" value="true"/>
<!-- This name is persisted as SCHED_NAME in db. for local testing could change to unique name to avoid collision with dev server -->
<property name="schedulerName" value="quartzScheduler"/>
<!--可选,QuartzScheduler 启动时更新己存在的Job,这样就不用每次修改targetObject后删除qrtz_job_details表对应记录了 -->
<property name="overwriteExistingJobs" value="true"/>
<!--必须的,QuartzScheduler 延时启动,应用启动完后 QuartzScheduler 再启动-->
<property name="startupDelay" value="30"/>
<property name="applicationContextSchedulerContextKey" value="applicationContext"/>
<!-- 注册触发器 -->
<property name="triggers">
<list>
<ref local="demoJobTrigger"/>
</list>
</property>
</bean>
<bean id="demoJobDetail" class="org.springframework.scheduling.quartz.JobDetailBean">
<property name="name" value="demoJobDetailName"/>
<property name="group" value="demoJobDetailGroup"/>
<property name="jobClass" value="org.ACRusher.DemoJobClass"/>
<!-- requestsRecovery属性为true,则当Quartz服务被中止后,再次启动任务时会尝试恢复执行之前未完成的所有任务-->
<property name="requestsRecovery" value="false"/>
<!-- 标识job是持久的,删除所有触发器的时候不被删除 -->
<property name="durability" value="true"/>
<!--是否是暂存的,若是 ,re-starting quartz 后不存在-->
<property name="volatility" value="false"/>
</bean>
<bean id="demoJobTrigger" class="org.springframework.scheduling.quartz.CronTriggerBean">
<property name="name" value="demoJobTriggerName"/>
<property name="group" value="demoJobTriggerGroup"/>
<property name="jobDetail" ref="demoJobDetail"/>
<property name="cronExpression" value="0 0 1 * * ? *"/>
</bean>
|
# Using Spring datasource in quartzJobsConfig.xml
# Spring uses LocalDataSourceJobStore extension of JobStoreCMT
# org.quartz.jobStore.useProperties=true
org.quartz.jobStore.tablePrefix=QRTZ_
org.quartz.jobStore.isClustered=true
# 10 seconds
org.quartz.jobStore.clusterCheckinInterval=10000
org.quartz.scheduler.skipUpdateCheck=true
# Change this to match your DB vendor
org.quartz.jobStore.class = org.quartz.impl.jdbcjobstore.JobStoreTX
org.quartz.jobStore.driverDelegateClass=org.quartz.impl.jdbcjobstore.StdJDBCDelegate
# Needed to manage cluster instances
org.quartz.scheduler.instanceId=AUTO
org.quartz.scheduler.instanceName=DEMO_JOB_SCHEDULER
org.quartz.scheduler.rmi.export=false
org.quartz.scheduler.rmi.proxy=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.dataSource=myDS
org.quartz.dataSource.myDS.driver=com.mysql.jdbc.Driver
org.quartz.dataSource.myDS.URL=${org.quartz.dataSource.myDS.URL}
org.quartz.dataSource.myDS.user=${org.quartz.dataSource.myDS.user}
org.quartz.dataSource.myDS.password=${org.quartz.dataSource.myDS.password}
org.quartz.dataSource.myDS.maxConnections=5
org.quartz.jobStore.misfireThreshold=120000
|
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.scheduling.quartz.QuartzJobBean;
import java.io.Serializable;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReentrantLock;
/**
* cluster job 抽象类
* <p/>
* Created by ACRusher on 2016/4/7.
*/
public abstract class AbstractClusterJob extends QuartzJobBean implements Serializable {
protected static final Logger logger = LoggerFactory.getLogger("quartzJob");
private static final long serialVersionUID = 3158445449885064827L;
protected ApplicationContext applicationContext;
//由父类自动注入。 注意,QuartzJobBean 只会注入spring的 context。
public void setApplicationContext(ApplicationContext applicationContext) {
this.applicationContext = applicationContext;
}
@Override
protected void executeInternal(JobExecutionContext context) throws JobExecutionException {
long start = System.currentTimeMillis();
logger.warn(">>> Cluster Job Frame | jobClass:{} started.",getClass().getSimpleName());
try {
beforeJob();
run(context);
} catch (Exception e) {
logger.error("jobClass:{} | jobContext:{} ", new Object[]{getClass().getSimpleName(), context, e});
} finally {
logger.warn(">>> Cluster Job Frame | jobClass:{} finished. costTime:{}ms ", getClass().getSimpleName(), System.currentTimeMillis() - start);
}
}
//job业务逻辑
protected abstract void run(JobExecutionContext context);
//job执行前注入必要的bean
protected void beforeJob() throws InvocationTargetException, IllegalAccessException {
//init injection , etc
Method[] methods=getClass().getDeclaredMethods();
for(Method m : methods){
m.setAccessible(true);
if(m.getName().startsWith("set")){
String name=m.getName().substring(3);
if(name.length()>1){
name=Character.toLowerCase(name.charAt(0))+name.substring(1);
}else {
name=String.valueOf(Character.toLowerCase(name.charAt(0)));
}
try {
Object bean = applicationContext.getBean(name);
if (bean != null && !bean.equals("applicationContext")) {
m.invoke(this, bean);
}
}catch (Exception e){
logger.error("",e);
}
}
}
}
}
|
import org.quartz.JobExecutionContext;
/**
* Created by ACRusher on 2016/4/7.
*/
public class DemoJobClass extends AbstractClusterJob {
private static final long serialVersionUID = 6945414743611829056L;
private UserManager userManager;
@Override
protected void run(JobExecutionContext context) {
// some biz code...
}
//由父类自动注入
public void setUserManager(UserManager userManager) {
this.userManager = userManager;
}
}
|
CREATE TABLE `QRTZ_JOB_DETAILS` (`JOB_NAME` VARCHAR(200) NOT NULL,`JOB_GROUP` VARCHAR(200) NOT NULL,`DESCRIPTION` VARCHAR(250) DEFAULT NULL,`JOB_CLASS_NAME` VARCHAR(250) NOT NULL,`IS_DURABLE` VARCHAR(1) NOT NULL,`IS_VOLATILE` VARCHAR(1) NOT NULL,`IS_STATEFUL` VARCHAR(1) NOT NULL,`REQUESTS_RECOVERY` VARCHAR(1) NOT NULL,`JOB_DATA` BLOB, PRIMARY KEY (`JOB_NAME`,`JOB_GROUP`) ) ENGINE=INNODB DEFAULT CHARSET=utf8; CREATE TABLE `QRTZ_JOB_LISTENERS` ( `JOB_NAME` varchar(200) NOT NULL,`JOB_GROUP` varchar(200) NOT NULL,`JOB_LISTENER` varchar(200) NOT NULL,PRIMARY KEY (`JOB_NAME`,`JOB_GROUP`,`JOB_LISTENER`),KEY `JOB_NAME` (`JOB_NAME`,`JOB_GROUP`),CONSTRAINT `QRTZ_JOB_LISTENERS_ibfk_1` FOREIGN KEY (`JOB_NAME`, `JOB_GROUP`) REFERENCES `QRTZ_JOB_DETAILS` (`JOB_NAME`, `JOB_GROUP`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8 CREATE TABLE `QRTZ_LOCKS` (`LOCK_NAME` varchar(40) NOT NULL,PRIMARY KEY (`LOCK_NAME`)) ENGINE=InnoDB DEFAULT CHARSET=utf8 CREATE TABLE `QRTZ_PAUSED_TRIGGER_GRPS` (`TRIGGER_GROUP` varchar(200) NOT NULL, PRIMARY KEY (`TRIGGER_GROUP`)) ENGINE=InnoDB DEFAULT CHARSET=utf8 CREATE TABLE `QRTZ_SCHEDULER_STATE` (`INSTANCE_NAME` varchar(200) NOT NULL,`LAST_CHECKIN_TIME` bigint(13) NOT NULL,`CHECKIN_INTERVAL` bigint(13) NOT NULL,PRIMARY KEY (`INSTANCE_NAME`)) ENGINE=InnoDB DEFAULT CHARSET=utf8 CREATE TABLE `QRTZ_TRIGGERS` (`TRIGGER_NAME` varchar(200) NOT NULL,`TRIGGER_GROUP` varchar(200) NOT NULL,`JOB_NAME` varchar(200) NOT NULL,`JOB_GROUP` varchar(200) NOT NULL,`IS_VOLATILE` varchar(1) NOT NULL,`DESCRIPTION` varchar(250) DEFAULT NULL,`NEXT_FIRE_TIME` bigint(13) DEFAULT NULL,`PREV_FIRE_TIME` bigint(13) DEFAULT NULL,`PRIORITY` int(11) DEFAULT NULL,`TRIGGER_STATE` varchar(16) NOT NULL,`TRIGGER_TYPE` varchar(8) NOT NULL,`START_TIME` bigint(13) NOT NULL,`END_TIME` bigint(13) DEFAULT NULL,`CALENDAR_NAME` varchar(200) DEFAULT NULL,`MISFIRE_INSTR` smallint(2) DEFAULT NULL,`JOB_DATA` blob,PRIMARY KEY (`TRIGGER_NAME`,`TRIGGER_GROUP`),KEY `JOB_NAME` (`JOB_NAME`,`JOB_GROUP`),CONSTRAINT `QRTZ_TRIGGERS_ibfk_1` FOREIGN KEY (`JOB_NAME`, `JOB_GROUP`) REFERENCES `QRTZ_JOB_DETAILS` (`JOB_NAME`, `JOB_GROUP`)) ENGINE=InnoDB DEFAULT CHARSET=utf8 CREATE TABLE `QRTZ_TRIGGER_LISTENERS` (`TRIGGER_NAME` varchar(200) NOT NULL,`TRIGGER_GROUP` varchar(200) NOT NULL,`TRIGGER_LISTENER` varchar(200) NOT NULL,PRIMARY KEY (`TRIGGER_NAME`,`TRIGGER_GROUP`,`TRIGGER_LISTENER`),KEY `TRIGGER_NAME` (`TRIGGER_NAME`,`TRIGGER_GROUP`), CONSTRAINT `QRTZ_TRIGGER_LISTENERS_ibfk_1` FOREIGN KEY (`TRIGGER_NAME`, `TRIGGER_GROUP`) REFERENCES `QRTZ_TRIGGERS` (`TRIGGER_NAME`, `TRIGGER_GROUP`)) ENGINE=InnoDB DEFAULT CHARSET=utf8 CREATE TABLE `QRTZ_SIMPLE_TRIGGERS` (`TRIGGER_NAME` varchar(200) NOT NULL,`TRIGGER_GROUP` varchar(200) NOT NULL,`REPEAT_COUNT` bigint(7) NOT NULL,`REPEAT_INTERVAL` bigint(12) NOT NULL,`TIMES_TRIGGERED` bigint(10) NOT NULL,PRIMARY KEY (`TRIGGER_NAME`,`TRIGGER_GROUP`),KEY `TRIGGER_NAME` (`TRIGGER_NAME`,`TRIGGER_GROUP`),CONSTRAINT `QRTZ_SIMPLE_TRIGGERS_ibfk_1` FOREIGN KEY (`TRIGGER_NAME`, `TRIGGER_GROUP`) REFERENCES `QRTZ_TRIGGERS` (`TRIGGER_NAME`, `TRIGGER_GROUP`)) ENGINE=InnoDB DEFAULT CHARSET=utf8 CREATE TABLE `QRTZ_BLOB_TRIGGERS` (`TRIGGER_NAME` varchar(200) NOT NULL,`TRIGGER_GROUP` varchar(200) NOT NULL,`BLOB_DATA` blob,PRIMARY KEY (`TRIGGER_NAME`,`TRIGGER_GROUP`),KEY `TRIGGER_NAME` (`TRIGGER_NAME`,`TRIGGER_GROUP`), CONSTRAINT `QRTZ_BLOB_TRIGGERS_ibfk_1` FOREIGN KEY (`TRIGGER_NAME`, `TRIGGER_GROUP`) REFERENCES `QRTZ_TRIGGERS` (`TRIGGER_NAME`, `TRIGGER_GROUP`)) ENGINE=InnoDB DEFAULT CHARSET=utf8 CREATE TABLE `QRTZ_CALENDARS` (`CALENDAR_NAME` varchar(200) NOT NULL,`CALENDAR` blob NOT NULL, PRIMARY KEY (`CALENDAR_NAME`)) ENGINE=InnoDB DEFAULT CHARSET=utf8 CREATE TABLE `QRTZ_CRON_TRIGGERS` (`TRIGGER_NAME` varchar(200) NOT NULL,`TRIGGER_GROUP` varchar(200) NOT NULL,`CRON_EXPRESSION` varchar(120) NOT NULL,`TIME_ZONE_ID` varchar(80) DEFAULT NULL,PRIMARY KEY (`TRIGGER_NAME`,`TRIGGER_GROUP`),KEY `TRIGGER_NAME` (`TRIGGER_NAME`,`TRIGGER_GROUP`),CONSTRAINT `QRTZ_CRON_TRIGGERS_ibfk_1` FOREIGN KEY (`TRIGGER_NAME`, `TRIGGER_GROUP`) REFERENCES `QRTZ_TRIGGERS` (`TRIGGER_NAME`, `TRIGGER_GROUP`)) ENGINE=InnoDB DEFAULT CHARSET=utf8 CREATE TABLE `QRTZ_FIRED_TRIGGERS` (`ENTRY_ID` varchar(95) NOT NULL,`TRIGGER_NAME` varchar(200) NOT NULL,`TRIGGER_GROUP` varchar(200) NOT NULL,`IS_VOLATILE` varchar(1) NOT NULL,`INSTANCE_NAME` varchar(200) NOT NULL,`FIRED_TIME` bigint(13) NOT NULL,`PRIORITY` int(11) NOT NULL,`STATE` varchar(16) NOT NULL,`JOB_NAME` varchar(200) DEFAULT NULL,`JOB_GROUP` varchar(200) DEFAULT NULL,`IS_STATEFUL` varchar(1) DEFAULT NULL,`REQUESTS_RECOVERY` varchar(1) DEFAULT NULL, PRIMARY KEY (`ENTRY_ID`)) ENGINE=InnoDB DEFAULT CHARSET=utf8 INSERT INTO QRTZ_LOCKS VALUES ( ‘CALENDAR_ACCESS‘ ) INSERT INTO QRTZ_LOCKS VALUES ( ‘JOB_ACCESS‘ ) INSERT INTO QRTZ_LOCKS VALUES ( ‘MISFIRE_ACCESS‘ ) INSERT INTO QRTZ_LOCKS VALUES ( ‘STATE_ACCESS‘ ) INSERT INTO QRTZ_LOCKS VALUES ( ‘TRIGGER_ACCESS‘ ) |
到此一切配置结束,剩下的就是具体的业务JobBean的开发了。当部署在多台机器上时,每个Job会动态分配在某一台机器执行。如果Job比较关键,在失败后需要重启,也可以配置
requestsRecovery 为 true来实现 任务重跑。 轻轻松松避免了单点故障和任务异常不可恢复问题。
【1】 quartz集群原理 http://www.cnblogs.com/zhenyuyaodidiao/p/4755649.html
【2】 美团quartz集群实践 http://tech.meituan.com/mt-crm-quartz.html
【3】 quartz官网 http://www.quartz-scheduler.org/documentation/
原文:http://www.cnblogs.com/ACRusher/p/5362935.html