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); Map map=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(); } }
总结
以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,如果有疑问大家可以留言交流,谢谢大家对毛票票的支持。