Detailed design of scheduled tasks
Timing task is implemented by QuartZ, and QuartZ document reference https://www.w3cschool.cn/quartz_doc/
QuartZ configuration
- Create a new QuartZ data table in the database. The corresponding SQL table is in the QuartZ.sql file in the current directory
- Add the quartz.properties file, which provides the related configuration of QuartZ internally
# Fixed prefix org.quartz # It is mainly divided into scheduler, threadPool, jobStore, plugin, etc org.quartz.scheduler.instanceName = DefaultQuartzScheduler org.quartz.scheduler.rmi.export = false org.quartz.scheduler.rmi.proxy = false org.quartz.scheduler.wrapJobExecutionInUserTransaction = false # When instantiating ThreadPool, the thread class used is SimpleThreadPool org.quartz.threadPool.class = org.quartz.simpl.SimpleThreadPool # threadCount and threadPriority will be injected into the ThreadPool instance as setter s # Number of concurrent org.quartz.threadPool.threadCount = 5 # priority org.quartz.threadPool.threadPriority = 5 org.quartz.threadPool.threadsInheritContextClassLoaderOfInitializingThread = true org.quartz.jobStore.misfireThreshold = 60000 # org.quartz.jobStore.driverDelegateClass = org.quartz.impl.jdbcjobstore.StdJDBCDelegate #Persistence org.quartz.jobStore.class = org.quartz.impl.jdbcjobstore.JobStoreTX org.quartz.jobStore.tablePrefix = QRTZ_ org.quartz.jobStore.dataSource = shop_mrg_admin_new org.quartz.dataSource.shop_mrg_admin_new.driver = @datasource.driver@ org.quartz.dataSource.shop_mrg_admin_new.URL = @datasource.url@ org.quartz.dataSource.shop_mrg_admin_new.user = @datasource.username@ org.quartz.dataSource.shop_mrg_admin_new.password = @datasource.password@ org.quartz.dataSource.shop_mrg_admin_new.maxConnections = 10 # trigger execution log display org.quartz.plugin.triggHistory.class =org.quartz.plugins.history.LoggingTriggerHistoryPlugin org.quartz.plugin.triggHistory.triggerFiredMessage =Trigger {1}.{0} fired job {6}.{5} at: {4, date, HH:mm:ss MM/dd/yyyy} org.quartz.plugin.triggHistory.triggerCompleteMessage =Trigger {1}.{0} completed firing job {6}.{5} at {4, date, HH:mm:ss MM/dd/yyyy}.
- Add QuartZ configuration JobFactory, quartzconfiguration
import org.quartz.spi.TriggerFiredBundle; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.config.AutowireCapableBeanFactory; import org.springframework.scheduling.quartz.AdaptableJobFactory; import org.springframework.stereotype.Component; @Component public class JobFactory extends AdaptableJobFactory { @Autowired private AutowireCapableBeanFactory capableBeanFactory; protected Object createJobInstance(TriggerFiredBundle bundle) throws Exception{ Object jobInstance = super.createJobInstance(bundle); capableBeanFactory.autowireBean(jobInstance); return jobInstance; } }
import org.quartz.Scheduler; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.config.PropertiesFactoryBean; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.core.io.ClassPathResource; import org.springframework.scheduling.quartz.SchedulerFactoryBean; import java.io.IOException; import java.util.Properties; @Configuration public class QuartzConfigration { @Autowired private JobFactory jobFactory; //Get factory bean @Bean public SchedulerFactoryBean schedulerFactoryBean() { SchedulerFactoryBean schedulerFactoryBean = new SchedulerFactoryBean(); try { schedulerFactoryBean.setQuartzProperties(quartzProperties()); //Update existing job s at startup schedulerFactoryBean.setOverwriteExistingJobs(true); //Auto start schedulerFactoryBean.setAutoStartup(true); schedulerFactoryBean.setJobFactory(jobFactory); } catch (IOException e) { e.printStackTrace(); } return schedulerFactoryBean; } //Specify quartz.properties @Bean public Properties quartzProperties() throws IOException { PropertiesFactoryBean propertiesFactoryBean = new PropertiesFactoryBean(); propertiesFactoryBean.setLocation(new ClassPathResource("/quartz.properties")); propertiesFactoryBean.afterPropertiesSet(); return propertiesFactoryBean.getObject(); } //Create schedule @Bean(name = "scheduler") public Scheduler scheduler() { return schedulerFactoryBean().getScheduler(); } }
- Create a new job information table t ﹣ job ﹣ triggers in the database, and provide the basic operation of adding, deleting, modifying and querying the table (in the next operation, you need to use the query operation of t ﹣ job ﹣ triggers table). The sql of t ﹣ job ﹣ triggers is as follows:
CREATE TABLE `t_job_triggers` ( `job_id` int(11) NOT NULL AUTO_INCREMENT COMMENT 'Primary key', `cron` varchar(100) COLLATE utf8_bin NOT NULL COMMENT 'cron expression', `status` int(11) NOT NULL COMMENT 'job Status 0=Not enabled, 1=Enable', `job_name` varchar(100) COLLATE utf8_bin NOT NULL COMMENT 'Scheduled task class name', `job_group` varchar(100) COLLATE utf8_bin DEFAULT NULL COMMENT 'For and job_name Determine the solution together job Uniqueness of', `job_decs` varchar(100) COLLATE utf8_bin DEFAULT NULL COMMENT 'Task description', `job_param` varchar(300) COLLATE utf8_bin DEFAULT NULL COMMENT 'Customizable additional parameters', PRIMARY KEY (`job_id`) USING BTREE ) ENGINE=InnoDB AUTO_INCREMENT=110 DEFAULT CHARSET=utf8 COLLATE=utf8_bin;
- Add a spring Scheduled to get the job data in the t job triggers. An example is as follows:
import com.gkcx.fls.dao.TJobTriggers; import com.gkcx.fls.mappers.TJobTriggersMapper; import org.quartz.*; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Service; import java.util.List; @Service public class ScheduleTriggerService { private static final Logger logger = LoggerFactory.getLogger(ScheduleTriggerService.class); @Autowired private Scheduler scheduler; @Autowired private TJobTriggersMapper jobTriggersMapper;//Table Mapper //Update tasks in quartz regularly every other minute @Scheduled(cron = "${scheduler.cron.trigger}") public void refreshTrigger() { try { //Query all scheduled tasks in the database List<TJobTriggers> jobList = jobTriggersMapper.selectAll(); if (jobList != null) { for (TJobTriggers jobTrigger : jobList) { TriggerKey triggerKey = TriggerKey.triggerKey(jobTrigger.getJobName(), jobTrigger.getJobGroup()); CronTrigger cronTrigger = (CronTrigger) scheduler.getTrigger(triggerKey); //This task has not been added to quartz if (cronTrigger == null) { if (jobTrigger.getStatus() == 0) { //Task is not enabled continue; } //Create a JobDetail (the full path of the task saved in the job [name] database, where you can dynamically inject the task into the JobDetail) JobDetail jobDetail = JobBuilder.newJob((Class<? extends Job>) Class.forName(jobTrigger.getJobName())) .withIdentity(jobTrigger.getJobName(), jobTrigger.getJobGroup()).requestRecovery(true).build(); //Expression scheduling builder CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(jobTrigger .getCron()); //Build a new trigger with a new cronExpression expression cronTrigger = TriggerBuilder.newTrigger().withIdentity(jobTrigger.getJobName(), jobTrigger.getJobGroup()).withSchedule(scheduleBuilder).build(); //Inject trigger and jobDetail into the scheduler scheduler.scheduleJob(jobDetail, cronTrigger); } else { //This task is already in quartz //cronTrigger already exists. Judge whether to delete it first. If not, judge whether the time changes again if (jobTrigger.getStatus() == 0) { //If disabled, remove this task from quartz JobKey jobKey = JobKey.jobKey(jobTrigger.getJobName(), jobTrigger.getJobGroup()); scheduler.deleteJob(jobKey); continue; } String searchCron = jobTrigger.getCron(); //Get cron expression in database String currentCron = cronTrigger.getCronExpression(); if (!searchCron.equals(currentCron)) { //It indicates that the task has changed and the corresponding records in quartz need to be updated //Expression scheduling builder CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(searchCron); //Rebuild trigger with new cronExpression expression cronTrigger = cronTrigger.getTriggerBuilder().withIdentity(triggerKey) .withSchedule(scheduleBuilder).build(); //Reset job execution by new trigger scheduler.rescheduleJob(triggerKey, cronTrigger); } } } } } catch (Exception e) { logger.info("Scheduled task refresh Quartz in job Abnormal!"); e.printStackTrace(); } } }
Developer documentation
Timing task source file location: you can create a new schedule package, for example: com.gkcx.fls.schedule
New scheduled task method
- Create a new class in com.gkcx.fls.schedule.job and implement the org.quartz.Job interface. Refer to the following example (TestTask.java):
import org.quartz.CronTrigger; import org.quartz.Job; import org.quartz.JobExecutionContext; import org.quartz.JobExecutionException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Component; @Component public class TestTask implements Job { private static final Logger logger = LoggerFactory.getLogger(TestTask.class); @Override public void execute(JobExecutionContext context) throws JobExecutionException { logger.info("TestTask Start executing...."); try{ CronTrigger trigger = (CronTrigger) context.getTrigger(); logger.info("cron:{}", trigger.getCronExpression()); logger.info("jobName:{}", trigger.getKey().getName()); logger.info("jobGroup:{}", trigger.getKey().getGroup()); }catch (Exception e){ e.printStackTrace(); }finally { logger.info("TestTask End of execution...."); } } }
- Add the new class information to the t job triggers in the database. Pay attention to the correctness of cron expression and job name, otherwise it will affect the operation of the scheduled task. Refer to the following example:
INSERT INTO t_job_triggers (job_id, cron, status, job_name, job_group, job_decs, job_param) VALUES(100, '0 21 10 * * ?', 0, 'com.gkcx.fls.schedule.job.TestTask', 'test', 'For testing', NULL);
be careful:
The table beginning with QRTZ in the database is the table required for QUARTZ persistence. Please do not modify it. When you migrate scheduled tasks to the production environment, you need to add tables that start with QRTZ to the production database.