主要介紹quartz如何使用,原理請閱讀下面的參考文獻,整理的已經很詳細。使用quartz集羣的主要目的是避免單點job故障,重要job故障後自動從新執行。html
加入quartz jar 包依賴java
<dependency> <groupId>com.opensymphony.quartz</groupId> <artifactId>com.springsource.org.quartz</artifactId> <version>1.6.2</version> </dependency> |
集成spring配置 ( spring 爲 3.2.x)mysql
<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比較關鍵,在失敗後須要重啓,也能夠配置spring
requestsRecovery 爲 true來實現 任務重跑。 輕輕鬆鬆避免了單點故障和任務異常不可恢復問題。sql
【1】 quartz集羣原理 http://www.cnblogs.com/zhenyuyaodidiao/p/4755649.htmlapp
【2】 美團quartz集羣實踐 http://tech.meituan.com/mt-crm-quartz.htmlide
【3】 quartz官網 http://www.quartz-scheduler.org/documentation/this