This is the 28th day of my participation in the August Challenge

Quartz is another open source project in the field of Job Scheduling by the OpenSymphony open source organization, developed entirely in Java and used to perform scheduled tasks.

Quartz is integrated with SpringBoot.

<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>

<! -- mysql -->
<dependency>
  <groupId>mysql</groupId>
  <artifactId>mysql-connector-java</artifactId>
  <scope>runtime</scope>
</dependency>

<! -- jdbc -->
<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
Copy the code

JobStore Job storage

RAMJobStore and JDBCJobStore

If JDBC storage is used, specify the database, such as mysql and datasource, such as JDBC or Mybatis

<dependency>
  <groupId>com.baomidou</groupId>
  <artifactId>mybatis-plus-boot-starter</artifactId>
  <version>3.3.2 rainfall distribution on 10-12</version>
</dependency>
Copy the code

Application. Yml configuration

spring:
  server:
    port: 8080
    servlet:
      context-path: /lovin
  datasource:
    url: JDBC: mysql: / / 127.0.0.1:3306 / quartz? serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8&useSSL=true
    username: root
    password: 123456
    driver-class-name: com.mysql.cj.jdbc.Driver
  quartz:
    job-store-type: jdbc # database mode
    jdbc:
      initialize-schema: never Never always initialize the table structure
    properties:
      org:
        quartz:
          scheduler:
            instanceId: AUTO # Default host name and timestamp generated instance ID, which can be any string but must be unique for all schedulers corresponding to the qrTZ_scheduler_state INSTANCE_NAME field
            #instanceName: clusteredScheduler #quartzScheduler
          jobStore:
            class: org.quartz.impl.jdbcjobstore.JobStoreTX # Persistent configuration
            driverDelegateClass: org.quartz.impl.jdbcjobstore.StdJDBCDelegate We only made database-specific agents for the database
            useProperties: false # to indicate that JDBCJobStore treats all values in JobDataMaps as strings, so more complex objects can be stored as name-value pairs rather than in their serialized form in BLOB columns. This is safer in the long run, because you avoid the class version problem of serializing non-String classes to bloBs.
            tablePrefix: QRTZ_  # database table prefix
            misfireThreshold: 60000 # The scheduler will "tolerate" the number of milliseconds that a Triggers will pass its next start time before it is considered a "fire". The default (if you did not enter this property in the configuration) is 60000 (60 seconds).
            clusterCheckinInterval: 5000 Set the frequency (in milliseconds) of this instance "checking in" * with other instances of the cluster. Affects the speed at which failed instances are detected.
            isClustered: true Enable clustering
          threadPool: # connection pool
            class: org.quartz.simpl.SimpleThreadPool
            threadCount: 10
            threadPriority: 5
            threadsInheritContextClassLoaderOfInitializingThread: true
Copy the code

Quartz. JDBC. Initialize -schema Changing to always will initialize the table structure and change to never after generating the table

Note the case of tablePrefix: QRTZ_ and check the table generated by the database to set it.

The name of the table instructions
qrtz_blob_triggers Trigger is stored as a Blob type (used when Quartz users create their own custom Trigger type in JDBC and JobStore doesn’t know how to store instances)
qrtz_calendars Calendar information stored as Blob for Quartz, which can be configured with a Calendar to specify a time range
qrtz_cron_triggers Stores Cron triggers, including Cron expressions and time zone information.
qrtz_fired_triggers Stores status information about triggered triggers and execution information about associated jobs
qrtz_job_details Stores detailed information about each configured Job
qrtz_locks Store the program’s non-watchable locking information (if pessimistic locking is used)
qrtz_paused_trigger_graps Stores information about the paused Trigger group
qrtz_scheduler_state Stores a small amount of state information about Scheduler and other Scheduler instances (if used in a cluster)
qrtz_simple_triggers Stores simple triggers, including repetitions, intervals, and times touched
qrtz_triggers Stores information about the configured Trigger
qrzt_simprop_triggers

Write task executor

Inherits from QuartzJobBean, add @Component to IOC container, can be used through class without adding

@Slf4j
@Component
public class HelloJob extends QuartzJobBean {

  @Override
  protected void executeInternal (JobExecutionContext context) throws JobExecutionException {
    log.info("HelloJob "+System.currentTimeMillis()); }}Copy the code

Perform job services

The Spring-boot-starter-Quartz integration injects Scheduler and starts the Scheduler via PostConstruct.

@Service
public class QuartzService {

  @Autowired
  private Scheduler scheduler;

  @PostConstruct
  public void startScheduler (a) {
    try {
      scheduler.start();
    } catch(SchedulerException e) { e.printStackTrace(); }}/** * add a job **@paramJobClass task implementation class *@paramJobName Task name *@paramJobGroupName Task group name *@paramJobTime Time expression (this is how many seconds a task is every) *@paramJobTimes Number of times (<0: indicates unlimited times) *@param* / jobData parameters
  public void addJob (Class<? extends QuartzJobBean> jobClass, String jobName, String jobGroupName, int jobTime,
      int jobTimes, Map jobData) {
    try {
      // The task name and group form the 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();
      }
      // Register jobDetail and trigger with the scheduler
      scheduler.scheduleJob(jobDetail, trigger);
    } catch(SchedulerException e) { e.printStackTrace(); }}/** * add a job **@paramJobClass task implementation class *@paramJobName Task name (unique recommended) *@paramJobGroupName Task group name *@paramJobTime Time expression (e.g. 0/5 * * * *?) *@param* / jobData parameters
  public void addJob (Class<? extends QuartzJobBean> jobClass, String jobName, String jobGroupName, String jobTime, Map jobData) {
    try {
      // Create jobDetail instance and bind the Job implementation class
      // Specify the name of the job, the name of the group to which it belongs, and the binding of the job class
      // The task name and group form the 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 scheduling trigger rules
      // Use cornTrigger rules
      // 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 with the task scheduler
      scheduler.scheduleJob(jobDetail, trigger);
    } catch(Exception e) { e.printStackTrace(); }}/** * Change the time expression of a job **@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 the trigger
      scheduler.rescheduleJob(triggerKey, trigger);
    } catch(SchedulerException e) { e.printStackTrace(); }}/** * Delete a job **@paramJobName Task name *@paramJobGroupName Task group name */
  public void deleteJob (String jobName, String jobGroupName) {
    try {
      scheduler.deleteJob(new JobKey(jobName, jobGroupName));
    } catch(Exception e) { e.printStackTrace(); }}/** * Suspends 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 the list of all planned 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 jobs **@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();
    }
    returnjobList; }}Copy the code

Quartz controller

Control to interact with the front-end page. When the front-end page interacts, service is called to control the job.

@API (tags = "Quartz controller ")
@RestController
public class QuartzController {


  @Autowired
  private QuartzService quartzService;

  @apiOperation (" Add task ")
  @PostMapping("/addJob")
  public Object addJob (@RequestBody JobRequestDto requestDto){
    Object bean = SpringUtil.getBean(requestDto.getBeanName());
    if(! (beaninstanceof QuartzJobBean)){
      return "bean type is not QuartzJobBean"; } QuartzJobBean jobBean = (QuartzJobBean) bean; quartzService.addJob(jobBean.getClass(),requestDto.getJobName(),requestDto.getJobGroupName(),requestDto.getCron(),reques tDto.getParams());return "success";
  }

  / /... updateJob deleteJob pauseJob resumeJob runAJobNow queryAllJob queryRunJob
}
Copy the code
@Data
public class JobRequestDto {

  String beanName;

  String jobName;

  String jobGroupName;

  String cron;

  Map<String,Object> params;
}
Copy the code

Start the service and send the request

{
  "beanName": "helloJob"."cron": "0/5 * * * *?"."jobGroupName": "paw-demo"."jobName": "helloJob"."params": {}}Copy the code

You see console output that HelloJob has been started

The 2021-08-12 14:20:30. 17620-029 the INFO [eduler_Worker] com. Paw. Quartz. Jobs. HelloJob: HelloJob 1628749230029 14:20:35 2021-08-12. 17620-035 the INFO [eduler_Worker - 8] com. Paw. Quartz. Jobs. HelloJob: HelloJob 1628749235035 14:20:40 2021-08-12. 17620-030 the INFO [eduler_Worker - 9] com. Paw. Quartz. Jobs. HelloJob: HelloJob 1628749240030 14:20:45 2021-08-12. 17620-031 INFO. [10] duler_Worker - com paw. Quartz. Jobs. HelloJob: HelloJob 1628749245031Copy the code

Viewing a Database

Relevant data, for example, QRTZ_JOB_DETAILS, has been saved

SCHED_NAME	JOB_NAME	JOB_GROUP	DESCRIPTION	JOB_CLASS_NAME	IS_DURABLE	IS_NONCONCURRENT	IS_UPDATE_DATA	REQUESTS_RECOVERY	JOB_DATA
quartzScheduler	helloJob	paw-demo		com.paw.quartz.jobs.HelloJob	0	0	0	0	
Copy the code

Stop the service and start again

Quartz gets the configuration from the database via JDBC and starts the task.

conclusion

Springboot provides quartz integration with Spring-boot-starter-Quartz. Jobs can be stored in the database in the jdbcStore mode so that service restart tasks are not lost. The API interface is used to control jobs to achieve interactive page control. Users only need to inherit QuartzJobBean to implement the business logic, and then submit the business class bean to the job through the page to realize the timing service.