zoukankan      html  css  js  c++  java
  • 【Quartz】初识与基本使用

    个人学习笔记分享,当前能力有限,请勿贬低,菜鸟互学,大佬绕道

    如有勘误,欢迎指出和讨论,本文后期也会进行修正和补充


    1.介绍

    Quartz是OpenSymphony开源组织在Job scheduling领域又一个开源项目,完全由Java开发,可以用来执行定时任务,类似于java.util.Timer。但是相较于Timer, Quartz增加了很多功能:

    • 持久性作业 - 就是保持调度定时的状态;
    • 作业管理 - 对调度作业进行有效的管理;

    使用quartz的调度器统一管理定时任务,可以动态的添加、删除、暂停等

    推荐几个博客,写的真的挺好

    2.使用步骤

    2.1.spring框架使用方法

    仅适用于spring基础框架,因为需要在web.xml里配置路径,然而springboot框架基本不用这个了。。。

    配置方法很简单,但是建议使用springboot方法进行配置,与时俱进嘛

    而且spring下的动态定时器巨麻烦,这里只做静态定时器的介绍,动态的使用springboot吧

    1. 添加quartz依赖

      <!-- 定时器依赖 -->
      <!-- https://mvnrepository.com/artifact/org.quartz-scheduler/quartz -->
      <dependency>
          <groupId>org.quartz-scheduler</groupId>
          <artifactId>quartz</artifactId>
          <version>2.3.2</version>
      </dependency>
      
    2. 定义任务目标类,添加需要执行的任务内容

      里面定义一个需要执行的方法即可

      public class MyTask{
          /**
           * 计数器
           */
          private static Integer count = 0;
      
          /**
           * 执行内容
           *
           * @param jobExecutionContext 上下文
           */
          public void test() {
              synchronized (count) {
                  System.out.println(new Date().toString() + ": " + count);
                  count++;
              }
          }
      }
      
    3. 定义spring配置类spring-quartz.xml(当然名字可以自己随便取)

      1. 示例化任务目标类

        <bean id="myTask" class="com.yezi_tool.demo_basic.test.MyTask"/>
        
      2. 定义工作任务job,设置目标类、目标方法和是否允许并发

        <bean id="testJob" class="org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean">
            <!-- 定时器的类  -->
            <property name="targetObject" ref="myTask"></property>
            <!-- 需要定时执行的方法  -->
            <property name="targetMethod" value="test"></property>
            <!-- 是否允许并发  -->
            <property name="concurrent" value="false"></property>
        </bean>
        
      3. 定义触发器trigger,定义工作任务job和执行时间(cron表达式可以在https://qqe2.com/cron 这里调试)

        <!-- 2.定义触发器Trigger并与Job绑定 -->
        <bean id="testJobTrigger" class="org.springframework.scheduling.quartz.CronTriggerFactoryBean">
            <!-- 指定工作任务 -->
            <property name="jobDetail" ref="testJob"/>
            <!-- 根据需要设置定时执行的时间 -->
            <property name="cronExpression" value="*/1 * * * * ? " />
        </bean>
        
      4. 定义调度器,注册trigger

        <bean name="quartzScheduler" lazy-init="false" class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
            <property name="triggers">
                <list>
                    <ref bean="testJobTrigger"/>
                </list>
            </property>
        </bean>
        

      完整xml

      <?xml version="1.0" encoding="UTF-8"?>
      <beans xmlns="http://www.springframework.org/schema/beans"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
      
          <bean id="myTask" class="com.yezi_tool.demo_basic.test.MyTask"/>
          <!-- 1.定义工作任务job -->
          <bean id="testJob" class="org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean">
              <!-- 指定任务类  -->
              <property name="targetObject" ref="myTask"></property>
              <!-- 需要定时执行的方法  -->
              <property name="targetMethod" value="test"></property>
              <!-- 是否允许并发  -->
              <property name="concurrent" value="false"></property>
          </bean>
          <!-- 2.定义触发器Trigger并与Job绑定 -->
          <bean id="testJobTrigger" class="org.springframework.scheduling.quartz.CronTriggerFactoryBean">
              <!-- 指定工作任务 -->
              <property name="jobDetail" ref="testJob"/>
              <!-- 根据需要设置定时执行的时间 -->
              <property name="cronExpression" value="*/1 * * * * ? " />
          </bean>
      
          <!-- 3.定义调度器,并将trigger注册进去 -->
          <bean name="quartzScheduler" lazy-init="false" class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
              <property name="triggers">
                  <list>
                      <ref bean="testJobTrigger"/>
                  </list>
              </property>
          </bean>
      </beans>
      
    4. 将xml的注入到项目

      • 基于spring的web项目,可以将所有配置文件添加到sprint-context.xml,然后将sprint-context.xml一起添加到web.xml

        1. 添加到spring-context.xml,其余内容都可有可无,不做赘述

          <import resource="classpath:spring-quartz.xml"/>
          
        2. 添加到web.xml

          <context-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>classpath:spring/spring-context.xml</param-value>
          </context-param>
          
      • 基于springboot,直接在quartz自定义配置类,或者项目启动类上添加引入注解即可

        //@ImportResource("classpath:*.xml")//引入所有xml
        @ImportResource("classpath:spring-quartz.xml")//引入quartz
        

    2.2.基于springboot的集成方法

    2.2.1.静态定时器

    静态定时器即主要使用代码控制,一旦项目部署将无法对定时器做出调整(增删改),适用于强制执行而不需要灵活变通的任务

    1. 添加quartz依赖

      <!-- 定时器依赖 -->
      <!-- https://mvnrepository.com/artifact/org.quartz-scheduler/quartz -->
      <dependency>
          <groupId>org.quartz-scheduler</groupId>
          <artifactId>quartz</artifactId>
          <version>2.3.2</version>
      </dependency>
      
    2. 创建需要执行的任务类MyTask,必须继承并实现QuartzJobBean

      public class MyTask extends QuartzJobBean {
          /**
           * 计数器
           */
          private static Integer count = 0;
      
          /**
           * 执行内容
           *
           * @param jobExecutionContext 上下文
           */
          @Override
          protected void executeInternal(JobExecutionContext jobExecutionContext) {
              JobDetail jobDetail = jobExecutionContext.getJobDetail();
              Trigger trigger = jobExecutionContext.getTrigger();
              synchronized (count) {
                  System.out.println(""
                          + new Date()    //打印时间
                          + " :" + count  //打印计时器
                          + "  jobDetailKey : " + jobDetail.getKey().toString()   //打印jobDetail的key
                          + "  jobDetailData : " + jobDetail.getJobDataMap().getString("jobName") //打印来自jobDetail的参数
                          + "  triggerKey : " + trigger.getKey().toString()   //打印trigger的key
                          + "  triggerData : " + trigger.getJobDataMap().getString("triggerName") //打印来自trigger的参数
                  );
                  count++;
              }
          }
      }
      
    3. 创建quartz配置文件QuartzConfig.java

      1. 定义业务组件myJob1(),配置需要执行的任务类、业务自定义身份、转递给任务的参数等等
      2. 定义触发器myTrigger1(),配置关联的业务组件、触发器自定义身份、转递给任务的参数等等
      3. 别忘了使用@Configuration标记配置文件类、@Bean标记业务组件和触发器
      @Configuration
      public class QuartzConfig {
          @Bean
          public JobDetail myJob1() {
              JobDetail jobDetail = JobBuilder.newJob(MyTask.class)
                      .withIdentity("myJob1", "myJobGroup1")
                      //JobDataMap可以给任务execute传递参数,且可传递多个
                      .usingJobData("jobName", "myJob1")
                      .storeDurably()
                      .build();
              return jobDetail;
          }
      
          @Bean
          public Trigger myTrigger1() {
              Trigger trigger = TriggerBuilder.newTrigger()
                      .forJob(myJob1())
                      .withIdentity("myTrigger1", "myTriggerGroup1")
                      //JobDataMap可以给任务execute传递参数,且可传递多个
                      .usingJobData("triggerName", "myTrigger1")
                      .startNow()
                      //.withSchedule(SimpleScheduleBuilder.simpleSchedule().withIntervalInSeconds(5).repeatForever())
                      .withSchedule(CronScheduleBuilder.cronSchedule("*/3 * * * * ?"))
                      .build();
              return trigger;
          }
      
          @Bean
          public Trigger myTrigger2() {
              Trigger trigger = TriggerBuilder.newTrigger()
                      .forJob(myJob1())
                      .withIdentity("myTrigger2", "myTriggerGroup1")
                      .usingJobData("triggerName", "myTrigger2")
                      .startNow()
                      .withSchedule(SimpleScheduleBuilder.simpleSchedule().withIntervalInSeconds(5).repeatForever())
      //                .withSchedule(CronScheduleBuilder.cronSchedule("*/3 * * * * ?"))
                      .build();
              return trigger;
          }
      }
      

    执行结果如下

    image-20200908094332929

    • myTrigger1和myTrigger2均使用业务组件myJob1
    • myTrigger1每隔3秒触发一次
    • myTrigger2每隔5秒触发一次

    2.2.2.动态定时器

    动态定时器即可以对定时器动态进行调整,通常由前端页面和数据库配合,从而灵活调整定时器

    因为需要动态管理,那么数据必须保证持久化,否则重新启动项目就什么都不剩了,那肯定不得行

    添加quartz依赖

            <!-- 定时器依赖 -->
            <!-- https://mvnrepository.com/artifact/org.quartz-scheduler/quartz -->
            <dependency>
                <groupId>org.quartz-scheduler</groupId>
                <artifactId>quartz</artifactId>
                <version>2.3.2</version>
            </dependency>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-quartz</artifactId>
            </dependency>
    

    持久化配置文件

    写入配置文件即可,仅编写quartz相关部分,其余按照自己需求即可,不影响

    推荐下面两篇博文,介绍的很详细,此处只做简单实现

    spring:
      quartz:
        #相关属性配置
        properties:
          org:
            quartz:
              scheduler:
                instanceName: quartzScheduler
                instanceId: AUTO
              jobStore:
                class: org.quartz.impl.jdbcjobstore.JobStoreTX
                driverDelegateClass: org.quartz.impl.jdbcjobstore.StdJDBCDelegate
                tablePrefix: QRTZ_
                isClustered: false
                clusterCheckinInterval: 10000
                useProperties: false
                misfireThreshold : 5000
              threadPool:
                class: org.quartz.simpl.SimpleThreadPool
                threadCount: 10
                threadPriority: 5
                threadsInheritContextClassLoaderOfInitializingThread: true
        #数据库方式
        job-store-type: JDBC
        #初始化表结构
        jdbc:
          initialize-schema: NEVER
    

    建表

    表需要依照quartz官方给出的实例,可以到http://www.quartz-scheduler.org/downloads/下载后解压,将目录srcorgquartzimpljdbcjobstore ables_mysql_innodb.sql里的语句在数据库直接执行即可

    嫌卡或者懒得可以在这个地址获取2.3.0版本https://gitee.com/echo_ye/assets/blob/master/doc/tables_mysql_innodb.sql

    数据封装类和查询方法

    数据封装类需要包括前后端传递的所有参数,查询则需要向quartz相关的表中查询

    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
    <mapper namespace="com.yezi_tool.demo_basic.mapper.JobMapper">
        <select id="listJob" resultType="com.yezi_tool.demo_basic.entity.QuartzJob">
            SELECT
              job.JOB_NAME as jobName,
              job.JOB_GROUP as jobGroup,
              job.DESCRIPTION as description,
              job.JOB_CLASS_NAME as jobClassName,
              cron.CRON_EXPRESSION as cronExpression,
              tri.TRIGGER_NAME as triggerName,
              tri.TRIGGER_STATE as triggerState,
              job.JOB_NAME as oldJobName,
              job.JOB_GROUP as oldJobGroup
            FROM qrtz_job_details AS job
            LEFT JOIN qrtz_triggers AS tri ON job.JOB_NAME = tri.JOB_NAME
            LEFT JOIN qrtz_cron_triggers AS cron ON cron.TRIGGER_NAME = tri.TRIGGER_NAME
            WHERE tri.TRIGGER_TYPE = 'CRON'
        </select>
    </mapper>
    
    package com.yezi_tool.demo_basic.mapper;
    
    import com.yezi_tool.demo_basic.entity.QuartzJob;
    import org.apache.ibatis.annotations.Mapper;
    
    import java.util.List;
    
    @Mapper
    public interface JobMapper {
        List<QuartzJob> listJob();
    }
    
    
    package com.yezi_tool.demo_basic.entity;
    
    import lombok.Data;
    
    import java.util.List;
    import java.util.Map;
    
    @Data
    public class QuartzJob {
        private String jobName;//任务名称
        private String jobGroup;//任务分组
        private String description;//任务描述
        private String jobClassName;//执行类
        private String cronExpression;//执行时间
    
        private String triggerState;//任务状态
    
        private List<Map<String,Object>> dataMap;//参数
        
        private List<Map<String, Object>> jobDataParam;//备用数据域
    
    
        public QuartzJob() {
            super();
        }
    
        public QuartzJob(String jobName, String jobGroup, String description, String jobClassName, String cronExpression) {
            super();
            this.jobName = jobName;
            this.jobGroup = jobGroup;
            this.description = description;
            this.jobClassName = jobClassName;
            this.cronExpression = cronExpression;
        }
    }
    

    执行的任务类

    与静态的任务类相同,可以建立多个,但是最好保证在同一个目录下,方便实例化的时候确认路径

    本案例的任务类放置在目录com.yezi_tool.demo_basic.test

    服务层方法

    控制层为主要代码,在这里对定时任务进行增删改查等操作

    因为懒就不写接口实现了。。。实际使用中请自己补上

    重点为创建定时任务的方法。步骤如下

    1. 检查任务是否已存在,若是,则删除任务
    2. 根据任务类的名字(路径固定),实例化任务类task
    3. 创建定时任务job,设置目标任务类、job名、分组名、传递给任务的数据、任务描述等参数
    4. 创建触发器trigger,设置触发器的名字、分组、触发时间、传递给任务的数据等参数
    5. 通过调度程序schedule将job和trigger进行绑定并启动

    也可在trigger中绑定job,那么schedule直接启动trigger即可

    @Service
    public class QuartzService {
    
        public String QUARTZ_TASK_PATH_HEAD = "com.yezi_tool.demo_basic.test" + ".";
    
        private final Scheduler scheduler;
    
        private final JobMapper jobMapper;
    
        public QuartzService(Scheduler scheduler, JobMapper jobMapper) {
            this.scheduler = scheduler;
            this.jobMapper = jobMapper;
        }
    
        public List<QuartzJob> list(String jobName) {
            List<QuartzJob> list = jobMapper.listJob();
            list.forEach(m -> {
                //只查询类名,无视路径
                m.setJobClassName(m.getJobClassName().replace(QUARTZ_TASK_PATH_HEAD, ""));
            });
            return list;
        }
    
        /**
         * 保存定时任务
         *
         * @param quartzJob 定时任务类
         * @throws Exception 抛出异常
         */
        public void save(QuartzJob quartzJob) throws Exception {
            try {
                //组装参数
                JobKey jobKey = new JobKey(quartzJob.getJobName(), quartzJob.getJobGroup());
                JobDataMap jobDataMap = new JobDataMap();
                if (quartzJob.getDataMap() != null) {
                    for (Map<String, Object> map : quartzJob.getDataMap())
                        jobDataMap.putAll(map);
                }
    
                //删除旧的job
                if (scheduler.checkExists(jobKey)) {
                    scheduler.deleteJob(jobKey);
                }
    
                //构建新的job
                Class clazz = Class.forName(QUARTZ_TASK_PATH_HEAD + quartzJob.getJobClassName());   //实例化目标任务类
                clazz.getDeclaredConstructor().newInstance();
                JobDetail jobDetail = JobBuilder
                        .newJob(clazz)//设置目标任务类
                        .withIdentity(quartzJob.getJobName(), quartzJob.getJobGroup())//设置job名和分组名
                        .usingJobData("jobName", quartzJob.getJobName())//传递的参数,可以自定义,可传递多组,以map形式传递
    //                    .usingJobData(new JobDataMap(quartzJob.getDataMap()))
                        .withDescription(quartzJob.getDescription())//设置描述,可为空
                        .build();
    
                //构建新的触发器
                Trigger trigger = TriggerBuilder
                        .newTrigger()
    //                    .forJob(jobDetail)//目标job,这里不设置,如果设置了将会自动触发
                        .withIdentity(quartzJob.getJobName() + "Trigger", quartzJob.getJobGroup())//设置job名和分组名
                        .usingJobData("triggerName", quartzJob.getJobName() + "Trigger")//传递的参数,可以自定义,可传递多组,以map形式传递
                        .usingJobData(jobDataMap)
                        .withSchedule(CronScheduleBuilder
                                .cronSchedule(quartzJob.getCronExpression().trim())
                                .withMisfireHandlingInstructionDoNothing()
                        )
                        .startNow()//现在就启动
    //                    .startAt(new Date())//启动时间,可以自定义,与startNow冲突
                        .build();
    
                //通过schedule触发
                scheduler.scheduleJob(jobDetail, trigger);
    
            } catch (SchedulerException e) {
                throw new BaseException("保存定时任务失败");
            } catch (ClassNotFoundException e) {
                throw new BaseException("任务目标类不存在");
            }
        }
    
    
        /**
         * 启动任务
         *
         * @param jobName  任务名
         * @param jobGroup 任务分组
         * @param dataMap  数据,可为空
         * @throws Exception 相关异常
         */
        public void trigger(String jobName, String jobGroup, List<Map<String, Object>> dataMap) throws Exception {
            //组装参数
            JobKey jobKey = new JobKey(jobName, jobGroup);
            JobDataMap jobDataMap = new JobDataMap();
            if (dataMap != null) {
                for (Map<String, Object> map : dataMap)
                    jobDataMap.putAll(map);
            }
            //启动触发器
            try {
                scheduler.triggerJob(jobKey, jobDataMap);
            } catch (SchedulerException e) {
                throw new BaseException("启动定时任务失败");
            }
        }
    
        /**
         * 暂停任务
         *
         * @param jobName  任务名
         * @param jobGroup 任务分组
         * @throws Exception 相关异常
         */
        public void pause(String jobName, String jobGroup) throws Exception {
            //组装参数
    //        JobKey jobKey = new JobKey(jobName, jobGroup);
            TriggerKey triggerKey = TriggerKey.triggerKey(jobName + "Trigger", jobGroup);
    
            //暂停触发器
            try {
    //            scheduler.pauseJob(jobKey);
                scheduler.pauseTrigger(triggerKey);
            } catch (SchedulerException e) {
                throw new BaseException("暂停定时任务失败");
            }
        }
    
        /**
         * 继续任务
         *
         * @param jobName  任务名
         * @param jobGroup 任务分组
         * @throws Exception 相关异常
         */
        public void resume(String jobName, String jobGroup) throws Exception {
            //组装参数
    //        JobKey jobKey = new JobKey(jobName, jobGroup);
            TriggerKey triggerKey = TriggerKey.triggerKey(jobName + "Trigger", jobGroup);
    
            //继续触发器
            try {
    //            scheduler.resumeJob(jobKey);
                scheduler.resumeTrigger(triggerKey);
            } catch (SchedulerException e) {
                throw new BaseException("启动定时任务失败");
            }
        }
    
        /**
         * 取消任务
         *
         * @param jobName  任务名
         * @param jobGroup 任务分组
         * @throws Exception 相关异常
         */
        public void cancel(String jobName, String jobGroup) throws Exception {
            try {
                TriggerKey triggerKey = TriggerKey.triggerKey(jobName + "Trigger", jobGroup);
                JobKey jobKey = JobKey.jobKey(jobName, jobGroup);
                //暂停
                scheduler.pauseTrigger(triggerKey);
                //解除绑定
                scheduler.unscheduleJob(triggerKey);
                //删除触发器
                scheduler.deleteJob(jobKey);
            } catch (SchedulerException e) {
                throw new BaseException("取消定时任务失败");
            }
        }
    
        /**
         * 取消全部任务
         *
         * @throws Exception 相关异常
         */
        public void cancelAll() throws Exception {
            List<QuartzJob> jobList = jobMapper.listJob();
            for (QuartzJob quartzJob : jobList) {
                cancel(quartzJob.getJobName(), quartzJob.getJobGroup());
            }
        }
    
        /**
         * 暂停全部任务
         *
         * @throws Exception 相关异常
         */
        public void pauseAll() throws Exception {
            try {
                scheduler.pauseAll();
            } catch (SchedulerException e) {
                throw new BaseException("暂停定时任务失败");
            }
        }
    
        /**
         * 恢复所有任务
         *
         * @throws Exception
         */
        public void resumeAll() throws Exception {
            try {
                scheduler.resumeAll();
            } catch (SchedulerException e) {
                throw new BaseException("启动定时任务失败");
            }
        }
    
    }
    
    

    控制层

    直接调用服务层方法就可以了,没啥技术含量

    这里只调用部分功能用于测试,请按照实际需求调整

    @Controller
    @RequestMapping("/quartz")
    @Slf4j
    public class QuartzController {
        private QuartzService quartzService;
    
        public QuartzController(QuartzService quartzService) {
            this.quartzService = quartzService;
        }
    
        @GetMapping("/list")
        @ResponseBody
        public ReturnMsg list(String jobName) {
            log.info("查询列表");
            return ReturnMsg.success(quartzService.list(jobName));
        }
    
        @PostMapping("/save")
        @ResponseBody
        public ReturnMsg save(@RequestBody QuartzJob quartzJob) throws Exception {
            log.info("保存");
            quartzService.save(quartzJob);
            return ReturnMsg.success();
        }
    
        @PostMapping("/pauseAll")
        @ResponseBody
        public ReturnMsg pauseAll() throws Exception {
            log.info("全部暂停");
            quartzService.pauseAll();
            return ReturnMsg.success();
        }
    
        @PostMapping("/resumeAll")
        @ResponseBody
        public ReturnMsg resumeAll() throws Exception {
            log.info("恢复全部");
            quartzService.resumeAll();
            return ReturnMsg.success();
        }
    
        @PostMapping("/cancelAll")
        @ResponseBody
        public ReturnMsg cancelAll() throws Exception {
            log.info("删除全部");
            quartzService.cancelAll();
            return ReturnMsg.success();
        }
    }
    
    

    测试结果

    save的参数如下,其余请求为空参数

    {
    "jobName": "job1",
    "jobGroup": "group1",
    "description": "demoJob",
    "jobClassName": "MyTask",
    "cronExpression": "*/3 * * * * ?"
    }
    

    2.2.3.补充

    • job与trigger的关系

      job表示任务,trigger表示触发器

      实际上两者是1-N的关系,一个任务可以被多个trigger持有,而trigger只能绑定一个job,但为了方便管理建议按1-1处理

      因此对于job的操作(暂停、恢复)会同步至与之关联的trigger,部分操作如下

      • 暂停/恢复job时,暂停/恢复与之相关的所有trigger
      • 删除job时,解除所有trigger与自身的关联,解除失败时报错

      而对于trigger的操作并不会同步到job

    • 执行的时间

      先上图

      image-20200909153729345

      可以看到trigger中自己生成了一个字段nextFireTime,即下一次执行时间

      到了nextFireTime时间则会执行一次任务,并将该字段更新

    • 执行失效的处理方案

      配置文件中有一条属性misfireThreshold = 5000,即失效阈值为5秒钟,表示一个任务若5秒内没有执行完成,则表示执行失效

      在对Trigger.withSchedule的时候对ScheduleBuilder设置处理方案

      • CronScheduleBuilder有4种方案,设置方法如下

        • 不设置:默认,智能处理。具体多智能我也不知道,但是这种不确定性显然是比较危险的,慎用
        • withMisfireHandlingInstructionIgnoreMisfires():忽略失效策略。服务恢复(包括项目启动)后,将一次性执行多次直至错过的任务全部补充。适用于部分场景。
        • withMisfireHandlingInstructionDoNothing():啥也不做。服务恢复后,将直接把nextFireTime更新至当前时间之后的下一次的执行时间,那么之前漏掉的任务将会被抛弃掉。适用于大部分场景。
        • withMisfireHandlingInstructionFireAndProceed():立即执行一次。服务恢复后立即执行一次,即将下次执行时间修改为现在,执行任务并从现在计算下一次时间。适用于部分场景。
      • SimpleScheduleBuilder有5种方案

      • 不设置:默认,智能处理,慎用

        • withMisfireHandlingInstructionIgnoreMisfires():忽略失效策略。服务恢复后,将一次性执行多次直至错过的任务全部补充。适用于部分场景。
        • withMisfireHandlingInstructionFireNow():立即执行一次,通常用于不重复执行的任务。即恢复后立即执行一次,对于不重复的任务相当于重试,对于重复的任务会立即执行一次,仅保留剩余次数而直接遗忘掉开始时间和总次数
      • withMisfireHandlingInstructionNextWithExistingCount():从当前时间之后的下次执行时间继续,错过的次数保留,不扣除

        • withMisfireHandlingInstructionNextWithRemainingCount():从当前时间之后的下次执行时间继续,错过的次数扣除
      • withMisfireHandlingInstructionNowWithExistingCount():从现在继续,错过的次数保留,不扣除。遗忘开始时间和总次数

        • withMisfireHandlingInstructionNowWithRemainingCount():从现在继续,错过的次数扣除。遗遗忘开始时间和总次数

      剩余两种ScheduleBuilder不多做描述,有兴趣的可以直接查源码,写的还是比较详细的

      若未设置misfireThreshold属性,以上方案均不会生效,依旧为智能处理,且大概率为忽略失效策略

    • pauseTrigger与pauseJob与pauseAll(resume同理)

      • pauseTrigger,即暂停触发器,那么自然任务也就不会再被触发
      • pauseJob,即暂停job,会将与自己关联的所有trigger一同暂停,resume同理
      • pauseAll,即全部暂停,实际上是暂停所有的group,并在数据库做记录,resume同理

    2.2.4.可能遇到的坑

    • resume或重启项目后任务快速执行多次

      原因就是前面补充的,错过时间的处理方案,默认值是智能处理,而智能成啥样并不知道。。。建议主动设置为需要的模式

      如修改trigger构建方法如下

               //构建新的触发器
               Trigger trigger = TriggerBuilder
                       .newTrigger()
      //                    .forJob(jobDetail)//目标job,这里不设置,如果设置了将会自动触发
                       .withIdentity(quartzJob.getJobName() + "Trigger", quartzJob.getJobGroup())//设置job名和分组名
                       .usingJobData("triggerName", quartzJob.getJobName() + "Trigger")//传递的参数,可以自定义,可传递多组,以map形式传递
                       .usingJobData(jobDataMap)
                       .withSchedule(CronScheduleBuilder
                               .cronSchedule(quartzJob.getCronExpression().trim())
                               .withMisfireHandlingInstructionDoNothing()
                       )
                       .startNow()//现在就启动
      //                    .startAt(new Date())//启动时间,可以自定义,与startNow冲突
                       .build();
      
    • pauseAll之后删除,然后再建立一样的任务,任务默认为暂停状态,而不是自动启动

      pauseAll会暂停所有的triggerGroup并存储到表qrtz_paused_trigger_grps,而删除任务时并不会删除这张表里的内容(但会删除trigger)

      那么当从新建立一个一毛一样的任务的时候,quartz就会将其识别为pause状态,因而并不会启动

      至于解决办法。。。个人建议批量暂停不要用pauseAll实现,完全可以查出所有任务,然后挨个pauseJob


    3.demo地址

    https://gitee.com/echo_ye/demo_basic/tree/scheduleDemo

    不同定时器启用方法在README.MD中查看,一共6种方法,如有纰漏请联系我

    仅实现了部分功能作为样例,请按照需求自己扩展哦,有疑问或者建议欢迎联系我~


    BB两句

    quartz之前只在spring用过。。。本来以为就一个小组件,打算几个定时器整理在一起得了。。。结果越学越多。。不得不分离出来作为单独的文章

    当然整理出来的依然只有一小部分,后面有机会再扩充整理一遍

    而且讲道理quartz的注释是写的真的挺好,我这个英语渣渣跟着debug看源码也问题不大



    作者:Echo_Ye

    WX:Echo_YeZ

    EMAIL :echo_yezi@qq.com

    个人站点:在搭了在搭了。。。(右键 - 新建文件夹)

  • 相关阅读:
    ajax提交Form
    MySQL新建用户,授权,删除用户,修改密码总结
    php 数组操作类(整合 给意见)
    PHP基于数组的分页函数(核心函数array_slice())
    php生成table表格
    百度地图定位
    python-redis-订阅和发布
    宿主机-免密登录Docker容器
    docker-文件系统出错处理
    python-redis集合模式
  • 原文地址:https://www.cnblogs.com/silent-bug/p/13647343.html
Copyright © 2011-2022 走看看