spring boot + quartz集群搭建的完整步骤
quartz集群能力:
- quartz集群分为水平集群和垂直集群,水平集群即将定时任务节点部署在不同的服务器,水平集群最大的问题就是时钟同步问题,
quartz集群强烈要求时钟同步,若时钟不能同步,则会导致集群中各个节点状态紊乱,造成不可预知的后果,请自行搜索服务器时钟同步,
若能保证时钟同步,水平集群能保证服务的可靠性,其中一个节点挂掉或其中一个服务器宕机,其他节点依然正常服务;垂直集群则是集群各节点部署在同一台服务器,
时钟同步自然不是问题,但存在单点故障问题,服务器宕机会严重影响服务的可用性。因此,要结合实际情况来考虑集群方案 - 由于集群中强烈要求时钟同步,因此不管是垂直集群还是水平集群,本地开发决不能连接线上环境(本地也是集群模式),这样的话势必会破坏集群,但本地若是非集群模式,
则可以依情况来连接线上环境。 - quartz集群和redis这样的集群实现方式不一样,redis集群需要节点之间通信,各节点需要知道其他节点的状况,而quartz集群的实现
方式在于11张表,集群节点相互之间不通信,而是通过定时任务持久化加锁的方式来实现集群。 - 破坏集群后果一般是死锁或者状态紊乱每个节点都不可用或其中某些节点能用部分或全部的定时任务
以上是个人的一些见解,下面开始本文的正文,本文主要介绍了关于springboot+quartz集群搭建的相关内容,分享出来供大家参考学习,下面话不多说了,来一起看看详细的介绍吧。
步骤如下:
springbootbean配置:
@Configuration
publicclassQuartzConfig{
@Value("${quartz.scheduler.instanceName}")
privateStringquartzInstanceName;
@Value("${org.quartz.dataSource.myDS.driver}")
privateStringmyDSDriver;
@Value("${org.quartz.dataSource.myDS.URL}")
privateStringmyDSURL;
@Value("${org.quartz.dataSource.myDS.user}")
privateStringmyDSUser;
@Value("${org.quartz.dataSource.myDS.password}")
privateStringmyDSPassword;
@Value("${org.quartz.dataSource.myDS.maxConnections}")
privateStringmyDSMaxConnections;
/**
*设置属性
*@return
*@throwsIOException
*/
privatePropertiesquartzProperties()throwsIOException{
Propertiesprop=newProperties();
prop.put("quartz.scheduler.instanceName",quartzInstanceName);
prop.put("org.quartz.scheduler.instanceId","AUTO");
prop.put("org.quartz.scheduler.skipUpdateCheck","true");
prop.put("org.quartz.scheduler.jmx.export","true");
prop.put("org.quartz.jobStore.class","org.quartz.impl.jdbcjobstore.JobStoreTX");
prop.put("org.quartz.jobStore.driverDelegateClass","org.quartz.impl.jdbcjobstore.StdJDBCDelegate");
prop.put("org.quartz.jobStore.dataSource","quartzDataSource");
prop.put("org.quartz.jobStore.tablePrefix","QRTZ_");
prop.put("org.quartz.jobStore.isClustered","true");
prop.put("org.quartz.jobStore.clusterCheckinInterval","20000");
prop.put("org.quartz.jobStore.dataSource","myDS");
prop.put("org.quartz.jobStore.maxMisfiresToHandleAtATime","1");
prop.put("org.quartz.jobStore.misfireThreshold","120000");
prop.put("org.quartz.jobStore.txIsolationLevelSerializable","true");
prop.put("org.quartz.jobStore.selectWithLockSQL","SELECT*FROM{0}LOCKSWHERELOCK_NAME=?FORUPDATE");
prop.put("org.quartz.threadPool.class","org.quartz.simpl.SimpleThreadPool");
prop.put("org.quartz.threadPool.threadCount","10");
prop.put("org.quartz.threadPool.threadPriority","5");
prop.put("org.quartz.threadPool.threadsInheritContextClassLoaderOfInitializingThread","true");
prop.put("org.quartz.dataSource.myDS.driver",myDSDriver);
prop.put("org.quartz.dataSource.myDS.URL",myDSURL);
prop.put("org.quartz.dataSource.myDS.user",myDSUser);
prop.put("org.quartz.dataSource.myDS.password",myDSPassword);
System.out.println("myDSMaxConnections:"+myDSMaxConnections);
prop.put("org.quartz.dataSource.myDS.maxConnections",myDSMaxConnections);
prop.put("org.quartz.plugin.triggHistory.class","org.quartz.plugins.history.LoggingJobHistoryPlugin");
prop.put("org.quartz.plugin.shutdownhook.class","org.quartz.plugins.management.ShutdownHookPlugin");
prop.put("org.quartz.plugin.shutdownhook.cleanShutdown","true");
returnprop;
}
@Bean
publicSchedulerFactoryBeanschedulerFactoryBean(@Qualifier("dialogJobTrigger")TriggercronJobTrigger)throwsIOException{
SchedulerFactoryBeanfactory=newSchedulerFactoryBean();
//thisallowstoupdatetriggersinDBwhenupdatingsettingsinconfigfile:
//用于quartz集群,QuartzScheduler启动时更新己存在的Job,这样就不用每次修改targetObject后删除qrtz_job_details表对应记录了
factory.setOverwriteExistingJobs(true);
//用于quartz集群,加载quartz数据源
//factory.setDataSource(dataSource);
//QuartzScheduler延时启动,应用启动完10秒后QuartzScheduler再启动
factory.setStartupDelay(10);
//用于quartz集群,加载quartz数据源配置
factory.setQuartzProperties(quartzProperties());
factory.setAutoStartup(true);
factory.setApplicationContextSchedulerContextKey("applicationContext");
//注册触发器
factory.setTriggers(cronJobTrigger);//直接使用配置文件
//factory.setConfigLocation(newFileSystemResource(this.getClass().getResource("/quartz.properties").getPath()));
returnfactory;
}
/**
*加载job
*@return
*/
@Bean
publicJobDetailFactoryBeanupdateDialogStatusJobDetail(){
returncreateJobDetail(InvokingJobDetailDetailFactory.class,"updateDialogStatusGroup","dialogJob");
}
/**
*加载触发器
*@paramjobDetail
*@return
*/
@Bean(name="dialogJobTrigger")
publicCronTriggerFactoryBeandialogStatusJobTrigger(@Qualifier("updateDialogStatusJobDetail")JobDetailjobDetail){
returndialogStatusTrigger(jobDetail,"000/1**?");
}
/**
*创建job工厂
*@paramjobClass
*@paramgroupName
*@paramtargetObject
*@return
*/
privatestaticJobDetailFactoryBeancreateJobDetail(Class>jobClass,StringgroupName,StringtargetObject){
JobDetailFactoryBeanfactoryBean=newJobDetailFactoryBean();
factoryBean.setJobClass(jobClass);
factoryBean.setDurability(true);
factoryBean.setRequestsRecovery(true);
factoryBean.setGroup(groupName);
Mapmap=newHashMap<>();
map.put("targetObject",targetObject);
map.put("targetMethod","execute");
factoryBean.setJobDataAsMap(map);
returnfactoryBean;
}
/**
*创建触发器工厂
*@paramjobDetail
*@paramcronExpression
*@return
*/
privatestaticCronTriggerFactoryBeandialogStatusTrigger(JobDetailjobDetail,StringcronExpression){
CronTriggerFactoryBeanfactoryBean=newCronTriggerFactoryBean();
factoryBean.setJobDetail(jobDetail);
factoryBean.setCronExpression(cronExpression);
returnfactoryBean;
}
}
InvokingJobDetailDetailFactory对象:
publicclassInvokingJobDetailDetailFactoryextendsQuartzJobBean{
//计划任务所在类
privateStringtargetObject;
//具体需要执行的计划任务
privateStringtargetMethod;
privateApplicationContextctx;
@Override
protectedvoidexecuteInternal(JobExecutionContextcontext)throwsJobExecutionException{
try{
ObjectotargetObject=ctx.getBean(targetObject);
Methodm=null;
try{
m=otargetObject.getClass().getMethod(targetMethod);
m.invoke(otargetObject);
}catch(SecurityExceptione){
e.printStackTrace();
}catch(NoSuchMethodExceptione){
e.printStackTrace();
}
}catch(Exceptione){
thrownewJobExecutionException(e);
}
}
publicvoidsetApplicationContext(ApplicationContextapplicationContext){
this.ctx=applicationContext;
}
publicvoidsetTargetObject(StringtargetObject){
this.targetObject=targetObject;
}
publicvoidsetTargetMethod(StringtargetMethod){
this.targetMethod=targetMethod;
}
}
备注:set方法不能少,setApplicationContext中的applicationContext与factory.setApplicationContextSchedulerContextKey("applicationContext")填入的值有关,其原理由InvokingJobDetailDetailFactory父类中的BeanWrapper实现。
sql脚本:--
--表的结构`qrtz_blob_triggers` -- CREATETABLEIFNOTEXISTS`qrtz_blob_triggers`( `SCHED_NAME`varchar(120)NOTNULL, `TRIGGER_NAME`varchar(120)NOTNULL, `TRIGGER_GROUP`varchar(120)NOTNULL, `BLOB_DATA`blob )ENGINE=InnoDBDEFAULTCHARSET=utf8mb4; ---------------------------------------------------------- -- --表的结构`qrtz_calendars` -- CREATETABLEIFNOTEXISTS`qrtz_calendars`( `SCHED_NAME`varchar(120)NOTNULL, `CALENDAR_NAME`varchar(120)NOTNULL, `CALENDAR`blobNOTNULL )ENGINE=InnoDBDEFAULTCHARSET=utf8mb4; ---------------------------------------------------------- -- --表的结构`qrtz_cron_triggers` -- CREATETABLEIFNOTEXISTS`qrtz_cron_triggers`( `SCHED_NAME`varchar(120)NOTNULL, `TRIGGER_NAME`varchar(120)NOTNULL, `TRIGGER_GROUP`varchar(120)NOTNULL, `CRON_EXPRESSION`varchar(120)NOTNULL, `TIME_ZONE_ID`varchar(80)DEFAULTNULL )ENGINE=InnoDBDEFAULTCHARSET=utf8mb4; ---------------------------------------------------------- -- --表的结构`qrtz_fired_triggers` -- CREATETABLEIFNOTEXISTS`qrtz_fired_triggers`( `SCHED_NAME`varchar(120)NOTNULL, `ENTRY_ID`varchar(95)NOTNULL, `TRIGGER_NAME`varchar(120)NOTNULL, `TRIGGER_GROUP`varchar(120)NOTNULL, `INSTANCE_NAME`varchar(120)NOTNULL, `FIRED_TIME`bigint(13)NOTNULL, `SCHED_TIME`bigint(13)NOTNULL, `PRIORITY`int(11)NOTNULL, `STATE`varchar(16)NOTNULL, `JOB_NAME`varchar(120)DEFAULTNULL, `JOB_GROUP`varchar(120)DEFAULTNULL, `IS_NONCONCURRENT`varchar(1)DEFAULTNULL, `REQUESTS_RECOVERY`varchar(1)DEFAULTNULL )ENGINE=InnoDBDEFAULTCHARSET=utf8mb4; ---------------------------------------------------------- -- --表的结构`qrtz_job_details` -- CREATETABLEIFNOTEXISTS`qrtz_job_details`( `SCHED_NAME`varchar(120)NOTNULL, `JOB_NAME`varchar(120)NOTNULL, `JOB_GROUP`varchar(120)NOTNULL, `DESCRIPTION`varchar(250)DEFAULTNULL, `JOB_CLASS_NAME`varchar(250)NOTNULL, `IS_DURABLE`varchar(1)NOTNULL, `IS_NONCONCURRENT`varchar(1)NOTNULL, `IS_UPDATE_DATA`varchar(1)NOTNULL, `REQUESTS_RECOVERY`varchar(1)NOTNULL, `JOB_DATA`blob )ENGINE=InnoDBDEFAULTCHARSET=utf8mb4; ---------------------------------------------------------- -- --表的结构`qrtz_locks` -- CREATETABLEIFNOTEXISTS`qrtz_locks`( `SCHED_NAME`varchar(120)NOTNULL, `LOCK_NAME`varchar(40)NOTNULL )ENGINE=InnoDBDEFAULTCHARSET=utf8mb4; ---------------------------------------------------------- -- --表的结构`qrtz_paused_trigger_grps` -- CREATETABLEIFNOTEXISTS`qrtz_paused_trigger_grps`( `SCHED_NAME`varchar(120)NOTNULL, `TRIGGER_GROUP`varchar(120)NOTNULL )ENGINE=InnoDBDEFAULTCHARSET=utf8mb4; ---------------------------------------------------------- -- --表的结构`qrtz_scheduler_state` -- CREATETABLEIFNOTEXISTS`qrtz_scheduler_state`( `SCHED_NAME`varchar(120)NOTNULL, `INSTANCE_NAME`varchar(120)NOTNULL, `LAST_CHECKIN_TIME`bigint(13)NOTNULL, `CHECKIN_INTERVAL`bigint(13)NOTNULL )ENGINE=InnoDBDEFAULTCHARSET=utf8mb4; ---------------------------------------------------------- -- --表的结构`qrtz_simple_triggers` -- CREATETABLEIFNOTEXISTS`qrtz_simple_triggers`( `SCHED_NAME`varchar(120)NOTNULL, `TRIGGER_NAME`varchar(120)NOTNULL, `TRIGGER_GROUP`varchar(120)NOTNULL, `REPEAT_COUNT`bigint(7)NOTNULL, `REPEAT_INTERVAL`bigint(12)NOTNULL, `TIMES_TRIGGERED`bigint(10)NOTNULL )ENGINE=InnoDBDEFAULTCHARSET=utf8mb4; ---------------------------------------------------------- -- --表的结构`qrtz_simprop_triggers` -- CREATETABLEIFNOTEXISTS`qrtz_simprop_triggers`( `SCHED_NAME`varchar(120)NOTNULL, `TRIGGER_NAME`varchar(120)NOTNULL, `TRIGGER_GROUP`varchar(120)NOTNULL, `STR_PROP_1`varchar(512)DEFAULTNULL, `STR_PROP_2`varchar(512)DEFAULTNULL, `STR_PROP_3`varchar(512)DEFAULTNULL, `INT_PROP_1`int(11)DEFAULTNULL, `INT_PROP_2`int(11)DEFAULTNULL, `LONG_PROP_1`bigint(20)DEFAULTNULL, `LONG_PROP_2`bigint(20)DEFAULTNULL, `DEC_PROP_1`decimal(13,4)DEFAULTNULL, `DEC_PROP_2`decimal(13,4)DEFAULTNULL, `BOOL_PROP_1`varchar(1)DEFAULTNULL, `BOOL_PROP_2`varchar(1)DEFAULTNULL )ENGINE=InnoDBDEFAULTCHARSET=utf8mb4; ---------------------------------------------------------- -- --表的结构`qrtz_triggers` -- CREATETABLEIFNOTEXISTS`qrtz_triggers`( `SCHED_NAME`varchar(120)NOTNULL, `TRIGGER_NAME`varchar(120)NOTNULL, `TRIGGER_GROUP`varchar(120)NOTNULL, `JOB_NAME`varchar(120)NOTNULL, `JOB_GROUP`varchar(120)NOTNULL, `DESCRIPTION`varchar(250)DEFAULTNULL, `NEXT_FIRE_TIME`bigint(13)DEFAULTNULL, `PREV_FIRE_TIME`bigint(13)DEFAULTNULL, `PRIORITY`int(11)DEFAULTNULL, `TRIGGER_STATE`varchar(16)NOTNULL, `TRIGGER_TYPE`varchar(8)NOTNULL, `START_TIME`bigint(13)NOTNULL, `END_TIME`bigint(13)DEFAULTNULL, `CALENDAR_NAME`varchar(200)DEFAULTNULL, `MISFIRE_INSTR`smallint(2)DEFAULTNULL, `JOB_DATA`blob )ENGINE=InnoDBDEFAULTCHARSET=utf8mb4; -- --Indexesfordumpedtables -- -- --Indexesfortable`qrtz_blob_triggers` -- ALTERTABLE`qrtz_blob_triggers` ADDPRIMARYKEY(`SCHED_NAME`,`TRIGGER_NAME`,`TRIGGER_GROUP`), ADDKEY`SCHED_NAME`(`SCHED_NAME`,`TRIGGER_NAME`,`TRIGGER_GROUP`); -- --Indexesfortable`qrtz_calendars` -- ALTERTABLE`qrtz_calendars` ADDPRIMARYKEY(`SCHED_NAME`,`CALENDAR_NAME`); -- --Indexesfortable`qrtz_cron_triggers` -- ALTERTABLE`qrtz_cron_triggers` ADDPRIMARYKEY(`SCHED_NAME`,`TRIGGER_NAME`,`TRIGGER_GROUP`); -- --Indexesfortable`qrtz_fired_triggers` -- ALTERTABLE`qrtz_fired_triggers` ADDPRIMARYKEY(`SCHED_NAME`,`ENTRY_ID`), ADDKEY`IDX_QRTZ_FT_TRIG_INST_NAME`(`SCHED_NAME`,`INSTANCE_NAME`), ADDKEY`IDX_QRTZ_FT_INST_JOB_REQ_RCVRY`(`SCHED_NAME`,`INSTANCE_NAME`,`REQUESTS_RECOVERY`), ADDKEY`IDX_QRTZ_FT_J_G`(`SCHED_NAME`,`JOB_NAME`,`JOB_GROUP`), ADDKEY`IDX_QRTZ_FT_JG`(`SCHED_NAME`,`JOB_GROUP`), ADDKEY`IDX_QRTZ_FT_T_G`(`SCHED_NAME`,`TRIGGER_NAME`,`TRIGGER_GROUP`), ADDKEY`IDX_QRTZ_FT_TG`(`SCHED_NAME`,`TRIGGER_GROUP`); -- --Indexesfortable`qrtz_job_details` -- ALTERTABLE`qrtz_job_details` ADDPRIMARYKEY(`SCHED_NAME`,`JOB_NAME`,`JOB_GROUP`), ADDKEY`IDX_QRTZ_J_REQ_RECOVERY`(`SCHED_NAME`,`REQUESTS_RECOVERY`), ADDKEY`IDX_QRTZ_J_GRP`(`SCHED_NAME`,`JOB_GROUP`); -- --Indexesfortable`qrtz_locks` -- ALTERTABLE`qrtz_locks` ADDPRIMARYKEY(`SCHED_NAME`,`LOCK_NAME`); -- --Indexesfortable`qrtz_paused_trigger_grps` -- ALTERTABLE`qrtz_paused_trigger_grps` ADDPRIMARYKEY(`SCHED_NAME`,`TRIGGER_GROUP`); -- --Indexesfortable`qrtz_scheduler_state` -- ALTERTABLE`qrtz_scheduler_state` ADDPRIMARYKEY(`SCHED_NAME`,`INSTANCE_NAME`); -- --Indexesfortable`qrtz_simple_triggers` -- ALTERTABLE`qrtz_simple_triggers` ADDPRIMARYKEY(`SCHED_NAME`,`TRIGGER_NAME`,`TRIGGER_GROUP`); -- --Indexesfortable`qrtz_simprop_triggers` -- ALTERTABLE`qrtz_simprop_triggers` ADDPRIMARYKEY(`SCHED_NAME`,`TRIGGER_NAME`,`TRIGGER_GROUP`); -- --Indexesfortable`qrtz_triggers` -- ALTERTABLE`qrtz_triggers` ADDPRIMARYKEY(`SCHED_NAME`,`TRIGGER_NAME`,`TRIGGER_GROUP`), ADDKEY`IDX_QRTZ_T_J`(`SCHED_NAME`,`JOB_NAME`,`JOB_GROUP`), ADDKEY`IDX_QRTZ_T_JG`(`SCHED_NAME`,`JOB_GROUP`), ADDKEY`IDX_QRTZ_T_C`(`SCHED_NAME`,`CALENDAR_NAME`(191)), ADDKEY`IDX_QRTZ_T_G`(`SCHED_NAME`,`TRIGGER_GROUP`), ADDKEY`IDX_QRTZ_T_STATE`(`SCHED_NAME`,`TRIGGER_STATE`), ADDKEY`IDX_QRTZ_T_N_STATE`(`SCHED_NAME`,`TRIGGER_NAME`,`TRIGGER_GROUP`,`TRIGGER_STATE`), ADDKEY`IDX_QRTZ_T_N_G_STATE`(`SCHED_NAME`,`TRIGGER_GROUP`,`TRIGGER_STATE`), ADDKEY`IDX_QRTZ_T_NEXT_FIRE_TIME`(`SCHED_NAME`,`NEXT_FIRE_TIME`), ADDKEY`IDX_QRTZ_T_NFT_ST`(`SCHED_NAME`,`TRIGGER_STATE`,`NEXT_FIRE_TIME`), ADDKEY`IDX_QRTZ_T_NFT_MISFIRE`(`SCHED_NAME`,`MISFIRE_INSTR`,`NEXT_FIRE_TIME`), ADDKEY`IDX_QRTZ_T_NFT_ST_MISFIRE`(`SCHED_NAME`,`MISFIRE_INSTR`,`NEXT_FIRE_TIME`,`TRIGGER_STATE`), ADDKEY`IDX_QRTZ_T_NFT_ST_MISFIRE_GRP`(`SCHED_NAME`,`MISFIRE_INSTR`,`NEXT_FIRE_TIME`,`TRIGGER_GROUP`,`TRIGGER_STATE`); -- --限制导出的表 -- -- --限制表`qrtz_blob_triggers` -- ALTERTABLE`qrtz_blob_triggers` ADDCONSTRAINT`qrtz_blob_triggers_ibfk_1`FOREIGNKEY(`SCHED_NAME`,`TRIGGER_NAME`,`TRIGGER_GROUP`)REFERENCES`qrtz_triggers`(`SCHED_NAME`,`TRIGGER_NAME`,`TRIGGER_GROUP`); -- --限制表`qrtz_cron_triggers` -- ALTERTABLE`qrtz_cron_triggers` ADDCONSTRAINT`qrtz_cron_triggers_ibfk_1`FOREIGNKEY(`SCHED_NAME`,`TRIGGER_NAME`,`TRIGGER_GROUP`)REFERENCES`qrtz_triggers`(`SCHED_NAME`,`TRIGGER_NAME`,`TRIGGER_GROUP`); -- --限制表`qrtz_simple_triggers` -- ALTERTABLE`qrtz_simple_triggers` ADDCONSTRAINT`qrtz_simple_triggers_ibfk_1`FOREIGNKEY(`SCHED_NAME`,`TRIGGER_NAME`,`TRIGGER_GROUP`)REFERENCES`qrtz_triggers`(`SCHED_NAME`,`TRIGGER_NAME`,`TRIGGER_GROUP`); -- --限制表`qrtz_simprop_triggers` -- ALTERTABLE`qrtz_simprop_triggers` ADDCONSTRAINT`qrtz_simprop_triggers_ibfk_1`FOREIGNKEY(`SCHED_NAME`,`TRIGGER_NAME`,`TRIGGER_GROUP`)REFERENCES`qrtz_triggers`(`SCHED_NAME`,`TRIGGER_NAME`,`TRIGGER_GROUP`); -- --限制表`qrtz_triggers` -- ALTERTABLE`qrtz_triggers` ADDCONSTRAINT`qrtz_triggers_ibfk_1`FOREIGNKEY(`SCHED_NAME`,`JOB_NAME`,`JOB_GROUP`)REFERENCES`qrtz_job_details`(`SCHED_NAME`,`JOB_NAME`,`JOB_GROUP`);
quartz集群实现原理,利用数据库记录job行为,并通过锁机制,使job在同一次中仅运行一次。
JobBean示例
//需要交由spring管理
@Service("dialogJob")
publicclassDialogJob{
@Autowired
privateQuestionServicequestionService;
//方法名在quartz定义
publicvoidexecute()throwsException{
//具体执行业务
questionService.XXXXX();
}
}
总结
以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,如果有疑问大家可以留言交流,谢谢大家对毛票票的支持。