[Summary of Previous Situation] This spring-boot-starter-quartz cluster has been put into practice because a project needs a timed task cluster.The springboot version is 2.1.6.RELEASE; the quartz version is 2.3.1. If there are two timer task nodes here, their code is exactly the same.
1.jar package dependency
<properties> <java.version>1.8</java.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-quartz</artifactId> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <scope>runtime</scope> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-jdbc</artifactId> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <optional>true</optional> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> </dependencies>
Here you choose to store the data for the timer task to avoid direct data storage in memory, data loss due to application restart, and cluster control.
Bitter, Project Configuration
spring: server: port: 8080 servlet: context-path: /lovin datasource: url: jdbc:mysql://127.0.0.1:3306/training?serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8&useSSL=true username: root password: root driver-class-name: com.mysql.cj.jdbc.Driver quartz: job-store-type: jdbc #Database Mode jdbc: initialize-schema: never #Do not initialize table structure properties: org: quartz: scheduler: instanceId: AUTO #Default hostname and timestamp generate instance ID, which can be any string, but must be the only corresponding qrtz_scheduler_state INSTANCE_NAME field for all dispatchers #instanceName: clusteredScheduler #quartzScheduler jobStore: class: org.quartz.impl.jdbcjobstore.JobStoreTX #Persistence Configuration driverDelegateClass: org.quartz.impl.jdbcjobstore.StdJDBCDelegate #We only make database-specific proxies for databases useProperties: false #Indicates that JDBC JobStore stores all values in JobDataMaps as strings, so more complex objects can be stored as name-value pairs rather than serialized in BLOB columns.In the long run, this is safer because you avoid serializing non-String classes to BLOB class versions. tablePrefix: qrtz_ #Database Table Prefix misfireThreshold: 60000 #The number of milliseconds the dispatcher will "tolerate" a Trigger to pass its next startup time before being considered a "fire".The default value (if you do not enter this property in the configuration) is 60000 (60 seconds). clusterCheckinInterval: 5000 #Set the frequency (in milliseconds) of this instance'checkin'* with other instances of the cluster.Affects the speed of detecting failed instances. isClustered: true #Turn on Clustering threadPool: #Connection Pool class: org.quartz.simpl.SimpleThreadPool threadCount: 10 threadPriority: 5 threadsInheritContextClassLoaderOfInitializingThread: true
Note here that the port numbers of the two nodes should be inconsistent to avoid conflicts
3. Implement a Job
@Slf4j public class Job extends QuartzJobBean { @Override protected void executeInternal(JobExecutionContext jobExecutionContext) throws JobExecutionException { // Get parameters JobDataMap jobDataMap = jobExecutionContext.getJobDetail().getJobDataMap(); // Business Logic... log.info("------springbootquartzonejob implement"+jobDataMap.get("name").toString()+"###############"+jobExecutionContext.getTrigger()); }
The log output is used to facilitate the observation of task execution
Random, encapsulated timed task operation
@Service public class QuartzService { @Autowired private Scheduler scheduler; @PostConstruct public void startScheduler() { try { scheduler.start(); } catch (SchedulerException e) { e.printStackTrace(); } } /** * Add a job * * @param jobClass * Task Implementation Class * @param jobName * Task Name * @param jobGroupName * Task Group Name * @param jobTime * Time expression (this is a task every few seconds) * @param jobTimes * Number of runs (<0: unlimited) * @param jobData * parameter */ public void addJob(Class<? extends QuartzJobBean> jobClass, String jobName, String jobGroupName, int jobTime, int jobTimes, Map jobData) { try { // Task name and group make up task key JobDetail jobDetail = JobBuilder.newJob(jobClass).withIdentity(jobName, jobGroupName) .build(); // Set job Parameters if(jobData!= null && jobData.size()>0){ jobDetail.getJobDataMap().putAll(jobData); } // Use the simpleTrigger rule Trigger trigger = null; if (jobTimes < 0) { trigger = TriggerBuilder.newTrigger().withIdentity(jobName, jobGroupName) .withSchedule(SimpleScheduleBuilder.repeatSecondlyForever(1).withIntervalInSeconds(jobTime)) .startNow().build(); } else { trigger = TriggerBuilder .newTrigger().withIdentity(jobName, jobGroupName).withSchedule(SimpleScheduleBuilder .repeatSecondlyForever(1).withIntervalInSeconds(jobTime).withRepeatCount(jobTimes)) .startNow().build(); } scheduler.scheduleJob(jobDetail, trigger); } catch (SchedulerException e) { e.printStackTrace(); } } /** * Add a job * * @param jobClass * Task Implementation Class * @param jobName * Task Name (Recommended Unique) * @param jobGroupName * Task Group Name * @param jobTime * Time expressions (such as: 0/5 * * * *?) * @param jobData * parameter */ public void addJob(Class<? extends QuartzJobBean> jobClass, String jobName, String jobGroupName, String jobTime, Map jobData) { try { // Create a jobDetail instance, bind the Job implementation class // Indicates the name of the job, the name of the group it belongs to, and the bound job class // Task name and group make up task key JobDetail jobDetail = JobBuilder.newJob(jobClass).withIdentity(jobName, jobGroupName) .build(); // Set job Parameters if(jobData!= null && jobData.size()>0){ jobDetail.getJobDataMap().putAll(jobData); } // Define Schedule Trigger Rules // Using the cornTrigger rule // Trigger key Trigger trigger = TriggerBuilder.newTrigger().withIdentity(jobName, jobGroupName) .startAt(DateBuilder.futureDate(1, IntervalUnit.SECOND)) .withSchedule(CronScheduleBuilder.cronSchedule(jobTime)).startNow().build(); // Register jobs and triggers in task scheduling scheduler.scheduleJob(jobDetail, trigger); } catch (Exception e) { e.printStackTrace(); } } /** * Modify a job's time expression * * @param jobName * @param jobGroupName * @param jobTime */ public void updateJob(String jobName, String jobGroupName, String jobTime) { try { TriggerKey triggerKey = TriggerKey.triggerKey(jobName, jobGroupName); CronTrigger trigger = (CronTrigger) scheduler.getTrigger(triggerKey); trigger = trigger.getTriggerBuilder().withIdentity(triggerKey) .withSchedule(CronScheduleBuilder.cronSchedule(jobTime)).build(); // Restart Trigger scheduler.rescheduleJob(triggerKey, trigger); } catch (SchedulerException e) { e.printStackTrace(); } } /** * Delete Task One job * * @param jobName * Task Name * @param jobGroupName * Task Group Name */ public void deleteJob(String jobName, String jobGroupName) { try { scheduler.deleteJob(new JobKey(jobName, jobGroupName)); } catch (Exception e) { e.printStackTrace(); } } /** * Pause a job * * @param jobName * @param jobGroupName */ public void pauseJob(String jobName, String jobGroupName) { try { JobKey jobKey = JobKey.jobKey(jobName, jobGroupName); scheduler.pauseJob(jobKey); } catch (SchedulerException e) { e.printStackTrace(); } } /** * Restore a job * * @param jobName * @param jobGroupName */ public void resumeJob(String jobName, String jobGroupName) { try { JobKey jobKey = JobKey.jobKey(jobName, jobGroupName); scheduler.resumeJob(jobKey); } catch (SchedulerException e) { e.printStackTrace(); } } /** * Execute a job immediately * * @param jobName * @param jobGroupName */ public void runAJobNow(String jobName, String jobGroupName) { try { JobKey jobKey = JobKey.jobKey(jobName, jobGroupName); scheduler.triggerJob(jobKey); } catch (SchedulerException e) { e.printStackTrace(); } } /** * Get a list of all scheduled tasks * * @return */ public List<Map<String, Object>> queryAllJob() { List<Map<String, Object>> jobList = null; try { GroupMatcher<JobKey> matcher = GroupMatcher.anyJobGroup(); Set<JobKey> jobKeys = scheduler.getJobKeys(matcher); jobList = new ArrayList<Map<String, Object>>(); for (JobKey jobKey : jobKeys) { List<? extends Trigger> triggers = scheduler.getTriggersOfJob(jobKey); for (Trigger trigger : triggers) { Map<String, Object> map = new HashMap<>(); map.put("jobName", jobKey.getName()); map.put("jobGroupName", jobKey.getGroup()); map.put("description", "trigger:" + trigger.getKey()); Trigger.TriggerState triggerState = scheduler.getTriggerState(trigger.getKey()); map.put("jobStatus", triggerState.name()); if (trigger instanceof CronTrigger) { CronTrigger cronTrigger = (CronTrigger) trigger; String cronExpression = cronTrigger.getCronExpression(); map.put("jobTime", cronExpression); } jobList.add(map); } } } catch (SchedulerException e) { e.printStackTrace(); } return jobList; } /** * Get all running job s * * @return */ public List<Map<String, Object>> queryRunJob() { List<Map<String, Object>> jobList = null; try { List<JobExecutionContext> executingJobs = scheduler.getCurrentlyExecutingJobs(); jobList = new ArrayList<Map<String, Object>>(executingJobs.size()); for (JobExecutionContext executingJob : executingJobs) { Map<String, Object> map = new HashMap<String, Object>(); JobDetail jobDetail = executingJob.getJobDetail(); JobKey jobKey = jobDetail.getKey(); Trigger trigger = executingJob.getTrigger(); map.put("jobName", jobKey.getName()); map.put("jobGroupName", jobKey.getGroup()); map.put("description", "trigger:" + trigger.getKey()); Trigger.TriggerState triggerState = scheduler.getTriggerState(trigger.getKey()); map.put("jobStatus", triggerState.name()); if (trigger instanceof CronTrigger) { CronTrigger cronTrigger = (CronTrigger) trigger; String cronExpression = cronTrigger.getCronExpression(); map.put("jobTime", cronExpression); } jobList.add(map); } } catch (SchedulerException e) { e.printStackTrace(); } return jobList; }
Land, Initialization Tasks
This is not intended to give users a web interface to configure timer tasks, so use CommandLineRunner to initialize tasks when they are initialized.Simply implement the run() method of the CommandLineRunner.
@Override public void run(String... args) throws Exception { HashMap<String,Object> map = new HashMap<>(); map.put("name",1); quartzService.deleteJob("job", "test"); quartzService.addJob(Job.class, "job", "test", "0 * * * * ?", map); map.put("name",2); quartzService.deleteJob("job2", "test"); quartzService.addJob(Job.class, "job2", "test", "10 * * * * ?", map); map.put("name",3); quartzService.deleteJob("job3", "test2"); quartzService.addJob(Job.class, "job3", "test2", "15 * * * * ?", map); }
, Test Verification
Start two applications in Summer Hou, and then observe the execution of tasks, as well as killing a service while running, to observe the execution of timed tasks.
[Write later] Here is the connection address for the script you need: Script download address And there's another self-fulfilling demo