Quartz定时任务

Quartz定时任务

我们经常需要在某个特定的时间做业务处理,如发生生日祝福,除夕拜年短信等,那么就需要使用定时任务框架来解决;

一.quartz 介绍

Quartz是一个功能丰富的开源任务调度库,用于在Java应用程序中进行任务调度。它提供了一种灵活而强大的方式来定义和安排任务的执行时间,支持周期性任务、延迟任务、固定间隔任务等。

  • **灵活的任务调度:**Quartz可以根据各种调度规则定义任务的执行时间,如固定延迟、固定间隔、Cron表达式等。可以轻松地创建简单或复杂的任务调度方案。
  • **分布式调度支持:**Quartz支持分布式任务调度,可在多个节点上运行并协调任务的执行。这种分布式架构提供了可靠、高可用的任务调度解决方案。
  • **持久化存储:**Quartz支持将任务和触发器的状态信息存储在数据库中,以便在应用程序重启后能够保持任务的持久化和恢复。它提供了与多种数据库的集成,并有内置的任务存储机制。
  • **错误恢复和重试机制:**Quartz提供了丰富的错误处理和恢复机制,以确保任务执行的稳定性。如果任务执行失败,Quartz会根据预定义的策略进行错误处理和任务重试。
  • **监控和管理:**Quartz提供了一套管理和监控API,可用于手动管理任务调度器、查询任务和触发器的状态、查看执行日志等。这些API可以方便地集成到的应用程序或管理工具中。
  • **插件扩展性:**Quartz具有良好的扩展性,允许开发人员编写自定义的任务存储、调度器监听器、触发器监听器等插件,以满足特定的需求和业务逻辑。

二.案例

入门案例

引入依赖

java 复制代码
<dependency>
    <groupId>org.springframework.boot</groupId>

    <artifactId>spring-boot-starter-quartz</artifactId>

</dependency>
java 复制代码
// 1.job定义
public class HelloJob implements Job {
    @Override
    public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        StringJoiner joiner = new StringJoiner("")
                .add("job 执行")
                .add(sdf.format(new Date()))
                .add(Thread.currentThread().getName())
                .add(jobExecutionContext.getTrigger().getKey().getName());
        System.out.println(joiner);
    }
}
java 复制代码
// 2.测试
public class QuartzTest {
    public static void main(String[] args) {
        try {
            // 获取一个默认的调度器
            Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler();
            // 启动调度器(在哪启动都可以,启动的时候将任务和触发器关联起来也是没问题的)
            scheduler.start();

            // 创建一个名为 job 的任务细节,指定任务的执行类为 HelloJob ,并为其设置标识 job1 和所属组 group1
            JobDetail job = newJob(HelloJob.class)
                    .withIdentity("job1","group1")
                    .build();

            // 创建一个名为 trigger 的触发器,设置标识为 trigger1 ,所属组为 group1 ,立即开始执行,并且设置执行的时间间隔为每 5 秒执行一次,永远重复
            Trigger trigger = newTrigger()
                    .withIdentity("trigger1","group1")
                    .startNow()
                    .withSchedule(simpleSchedule()
                            .withIntervalInSeconds(5)
                            .repeatForever())
                    .build();

            // 使用 scheduler.scheduleJob 方法将任务和触发器关联起来,让调度器按照设定的规则执行任务
            scheduler.scheduleJob(job,trigger);
            Thread.sleep(20000000);
            // scheduler.shutdown 方法关闭调度器
            scheduler.shutdown();

        } catch (SchedulerException e) {
            throw new RuntimeException(e);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
    }
}

springBoot整合

1.引入依赖

java 复制代码
       <dependency>
            <groupId>org.springframework.boot</groupId>

            <artifactId>spring-boot-starter-quartz</artifactId>

        </dependency>

2.定义job类

java 复制代码
@PersistJobDataAfterExecution
@DisallowConcurrentExecution
public class QuartzJob extends QuartzJobBean {
    @Override
    protected void executeInternal(JobExecutionContext context) throws JobExecutionException {
        try {
            Thread.sleep(2000);
            System.out.println(context.getScheduler().getSchedulerInstanceId());
            System.out.println("taskName=" + context.getJobDetail().getKey().getName());
            System.out.println("执行时间:" + new Date());
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (SchedulerException e) {
            e.printStackTrace();
        }
    }
}

3.配置调度器

java 复制代码
@Configuration
public class SchedulerConfig {
 
    @Autowired
    private DataSource dataSource;
 
    @Bean
    public Scheduler scheduler() throws IOException {
        return schedulerFactoryBean().getScheduler();
    }
 
    @Bean
    public SchedulerFactoryBean schedulerFactoryBean() throws IOException {
        SchedulerFactoryBean factory = new SchedulerFactoryBean();
        factory.setSchedulerName("cluster_scheduler");
        // 注入数据源
        factory.setDataSource(dataSource);
        // 选填
       factory.setApplicationContextSchedulerContextKey("application");
        factory.setQuartzProperties(quartzProperties());
        // 读线程池配置
        factory.setTaskExecutor(schedulerThreadPool());
        // 等待设置其他属性
        return factory;
    }
 
    @Bean
    public Properties quartzProperties() throws IOException {
        PropertiesFactoryBean propertiesFactoryBean = new PropertiesFactoryBean();
        propertiesFactoryBean.setLocation(new ClassPathResource("/spring-quartz.properties"));
        propertiesFactoryBean.afterPropertiesSet();
        return propertiesFactoryBean.getObject();
    }
 
    @Bean
    public Executor schedulerThreadPool() {
        ThreadPoolTaskExecutor taskExecutor = new ThreadPoolTaskExecutor();
        // 处理器的核心数
        taskExecutor.setCorePoolSize(Runtime.getRuntime().availableProcessors());
        // 最大线程数
        taskExecutor.setMaxPoolSize(Runtime.getRuntime().availableProcessors());
        // 容量
        taskExecutor.setQueueCapacity(Runtime.getRuntime().availableProcessors());
        return taskExecutor;
    }
}

4.监听

java 复制代码
@Component
public class StartApplicationListener implements ApplicationListener<ContextRefreshedEvent> {
 
    @Autowired
    private Scheduler scheduler;
 
    @Override
    public void onApplicationEvent(ContextRefreshedEvent event) {
        // 开启调度
        // 可以先判断触发器是否存在
        /**
         * JobDetail有一个类型为JobKey的重要属性key,相当于是该任务的键值,JobDetail注册到任务调度器Schedule中的时候,key值不允许重复。***整个任务调度过程中,Quartz都是通过Jobkey来唯一识别JobDetail的。试图将重复键值的JobDetail注册到任务调度器中而不指定覆盖的话,是不被允许的。***
         *
         * JobKey可以通过JobBuiler的withIdentity方法指定,该方法接收name或name+group参数,从而唯一确定一个任务JobDetail。
         *
         * 如果在JobDetail创建过程中不指定JobKey的话,Quartz会通过UUID的方式为该任务生成一个唯一的key值。
         *
         * ***所以,同一个Job实现类(也就是同一个任务),可以通过不同的JobKey值注册到任务调度器中、绑定不同的触发器执行!
         */
        TriggerKey triggerKey = TriggerKey.triggerKey("trigger1", "group1");
        try {
            Trigger trigger = scheduler.getTrigger(triggerKey);
            if (trigger == null) {
                // 触发器
                TriggerBuilder.newTrigger().withIdentity(triggerKey)
                        .withSchedule(CronScheduleBuilder.cronSchedule("0/10 * * * * ?")).build();
                //
                JobDetail jobDetail = JobBuilder.newJob(QuartzJob.class)
                        .withIdentity("job1", "group1")
                        .build();
                scheduler.scheduleJob(jobDetail, trigger);
                scheduler.start();
            }
        } catch (SchedulerException e) {
            e.printStackTrace();
        }
 
 
    }
}

基本配置

  • Scheduler配置
java 复制代码
#============================================================================
# Scheduler 调度器属性配置
#============================================================================
# 调度标识名 集群中每一个实例都必须使用相同的名称,可以为任意字符串,对于scheduler来说此值没有意义,但是可以区分同一系统中多个不同的实例
org.quartz.scheduler.instanceName = ClusterQuartz
# ID设置为自动获取 每一个必须不同
org.quartz.scheduler.instanceId= AUTO
 
org.quartz.scheduler.rmi.export = false
org.quartz.scheduler.rmi.proxy = false
 
# 默认false,若是在执行Job之前Quartz开启UserTransaction,此属性应该为true。
#Job执行完毕,JobDataMap更新完(如果是StatefulJob)事务就会提交。默认值是false,可以在job类上使用@ExecuteInJTATransaction注解,
# 以便在各自的job上决定是否开启JTA事务。
org.quartz.scheduler.wrapJobExecutionInUserTransaction = false
#一个scheduler节点允许接收的trigger的最大数,默认是1,这个值越大,定时任务执行的越多,但代价是集群节点之间的不均衡。
org.quartz.scheduler.batchTriggerAcquisitionMaxCount=1
  • Scheduler线程池配置
java 复制代码
#============================================================================
# 配置ThreadPool
#============================================================================
# 线程池的实现类(一般使用SimpleThreadPool即可满足几乎所有用户的需求)
org.quartz.threadPool.class=org.quartz.simpl.SimpleThreadPool
 
# 指定线程数,一般设置为1-100直接的整数,根据系统资源配置
org.quartz.threadPool.threadCount = 10
 
# 设置线程的优先级(可以是Thread.MIN_PRIORITY(即1)和Thread.MAX_PRIORITY(这是10)之间的任何int 。默认值为Thread.NORM_PRIORITY(5)。)
org.quartz.threadPool.threadPriority = 5
 
#加载任务代码的ClassLoader是否从外部继承
org.quartz.threadPool.threadsInheritContextClassLoaderOfInitializingThread= true
 
#是否设置调度器线程为守护线程
org.quartz.scheduler.makeSchedulerThreadDaemon=true
  • JobStore作业存储配置
java 复制代码
#============================================================================
# 配置JobStore
#============================================================================
# JobDataMaps是否都为String类型,默认false
# 若是true的话,便可不用让更复杂的对象以序列化的形式保存到BLOB列中
org.quartz.jobStore.useProperties=false
 
# 存储相关信息表的前缀,默认QRTZ_
org.quartz.jobStore.tablePrefix = QRTZ_
 
# 是否加入集群
#是否是应用在集群中,当应用在集群中时必须设置为TRUE,否则会出错。
#如果有多个Quartz实例在用同一套数据库时,必须设置为true。
org.quartz.jobStore.isClustered = true
 
# 调度实例失效的检查时间间隔 ms
#只用于设置了isClustered为true的时候,设置一个频度(毫秒),用于实例报告给集群中的其他实例。
#这会影响到侦测失败实例的敏捷度。
org.quartz.jobStore.clusterCheckinInterval = 5000
 
# 当设置为"true"时,此属性告诉Quartz 在非托管JDBC连接上调用setTransactionIsolation(Connection.TRANSACTION_READ_COMMITTED)。
org.quartz.jobStore.txIsolationLevelReadCommitted = true
 
# 数据保存方式为数据库持久化,选择JDBC的存储方式
org.quartz.jobStore.class = org.quartz.impl.jdbcjobstore.JobStoreTX
 
# 数据库代理类,一般org.quartz.impl.jdbcjobstore.StdJDBCDelegate可以满足大部分数据库
org.quartz.jobStore.driverDelegateClass = org.quartz.impl.jdbcjobstore.StdJDBCDelegate
 
#这个值必须datasource的配置信息
org.quartz.jobStore.dataSource = myDataSource
 
#最大能忍受的触发超时时间,如果超时则认为"失误"
org.quartz.jobStore.misfireThreshold = 60000
 
#这是JobStore能处理的错过触发的Trigger的最大数量。处理太多(2打)很快就会导致数据库表被锁定够长的时间,
#这样会妨碍别的(还未错过触发)trigger执行的性能。
org.quartz.jobStore.maxMisfiresToHandleAtATime=20
 
#设置这个参数为true会告诉Quartz从数据源获取连接后不要调用它的setAutoCommit(false)方法。
#在少数情况下是有用的,比如有一个驱动本来是关闭的,但是又调用这个关闭的方法。但是大部分情况下驱动都要求调用setAutoCommit(false)
org.quartz.jobStore.dontSetAutoCommitFalse=false
 
#这必须是一个从LOCKS表查询一行并对这行记录加锁的SQL。假设没有设置,默认值如下。
#{0}会在运行期间被前面配置的TABLE_PREFIX所代替
org.quartz.jobStore.selectWithLockSQL=SELECT * FROM {0}LOCKS WHERE LOCK_NAME = ? FOR UPDATE
 
#值为true时告知Quartz(当使用JobStoreTX或CMT)调用JDBC连接的setTransactionIsolation(Connection.TRANSACTION_SERIALIZABLE) 方法。这有助于某些数据库在高负载和长时间事务时锁的超时。
org.quartz.jobStore.txIsolationLevelSerializable=false

三.组件介绍

JobDetail介绍

在Quartz中,JobDetail是描述任务(Job)的具体细节的类。它包含了任务的标识、执行类、执行时所需的数据/参数等信息。

以下是JobDetail的一些主要属性:

  • name:任务名称,同一个分组下必须是唯一的。
  • group:任务分组,用于将任务进行分类,方便管理和调度。
  • jobClass:任务执行类,即实现了org.quartz.Job接口的类。它负责实际执行任务任务的逻辑,真正的业务代码执行。
  • jobDataMap:任务的数据/参数,可以传递一些额外的数据给任务类,供其使用。
  • durability:任务的持久性标志,指示任务是否应该存储在调度器中,即使没有与之关联的触发器。
  • requestsRecovery:任务的失败恢复标志,指示任务是否应该在调度器发生故障后恢复执行。

使用JobDetail,可以定义要执行的具体任务的细节,并为任务提供必要的信息。

以下是示例代码,展示如何创建一个JobDetail对象:

java 复制代码
JobDetail jobDetail = JobBuilder.newJob(MyJob.class)
                           .withIdentity("jobName", "jobGroup")
                           .usingJobData("param1", "value1")
                           .build();

在上述示例中,创建了一个JobDetail对象,指定了任务的执行类为MyJob,名称为"jobName",分组为"jobGroup",并使用了一个名为"param1"的参数

JobDetail 和job 的关系

  • 在设计上可以将job 包装成各种各样的 jobDetai,一个Job 可以对应为多个jobDetai,但是一个jobDetai 只能对用某一个job,但是通常在业务开发中 一个job 只创建一个jobDetai 。
  • JobDetail是描述任务的具体细节的类,它包含了任务的标识、执行类、执行时所需的数据/参数等信息。它是任务的静态信息。Job是org.quartz.Job接口的实现类,负责实际执行任务任务的逻辑。它是任务的动态逻辑。
  • Quartz中,通过将一个JobDetail实例与一个Trigger实例相关联,形成一个任务调度的单元。当触发器触发时,调度器会使用JobDetail中的信息创建一个Job实例,并执行该实例中的任务逻辑。
  • JobDetail是任务的定义,而Job是任务的实例。这种设计能够使任务实例具有并发执行的能力。

Trigger介绍

在Quartz中,Trigger是用于定义任务(Job)执行时间的组件。它指定了任务应该何时运行、运行频率和执行规则。

Trigger可以分为以下几种类型:

  • SimpleTrigger:简单触发器,用于指定任务在特定时间点触发一次或多次的执行。可以设置触发时间、重复次数、重复间隔等参数。
  • CronTrigger:Cron触发器,基于Cron表达式定义触发时间规则。Cron表达式可以更灵活地指定任务的触发时间,如每天的特定时间执行、每周的特定日期执行等。
  • CalendarIntervalTrigger:日历间隔触发器,以特定的日历间隔来触发任务的执行。可以指定固定的时间间隔或基于日历规则定义触发时间。
  • DailyTimeIntervalTrigger:每日时间间隔触发器,基于特定的时间间隔和每天的时间窗口来触发任务的执行。

通过使用这些不同类型的Trigger,可以灵活地定义任务的触发时间和执行规则。一旦触发器被触发,Quartz调度器将会调度与之相关联的任务(通过JobDetail),并按照预定义的规则执行任务逻辑。

以下是一个示例代码,展示如何创建一个简单触发器:

java 复制代码
Trigger trigger = TriggerBuilder.newTrigger()
                .withIdentity("triggerName", "triggerGroup")
                .startNow()
                .withSchedule(SimpleScheduleBuilder.simpleSchedule()
                        .withIntervalInSeconds(10)
                        .repeatForever())
                .build();

在上述示例中,创建了一个简单触发器,设置了触发器的名称为"triggerName",分组为"triggerGroup",开始时间为当前时间,每隔10秒触发一次任务执行,重复执行无限次。

Trigger是Quartz中重要的组件之一,它定义了任务的触发时间和执行规则。通过使用不同类型的触发器,可以根据需求灵活地调度和执行任务。同时,调度器会管理触发器的调度和执行,并自动触发与之关联的相应任务的执行。

trigger 和jobDetail 的关系

  • JobDetail是描述任务的静态信息,包括任务的标识、执行类、执行时所需的数据/参数等信息。Trigger是用于定义任务(Job)执行时间的组件,指定了任务应该何时运行、运行频率和执行规则。
  • 在Quartz中,Trigger和JobDetail是密切相关的并且有着父子关系。一个jobDetail 可以被多个trigger 触发
  • 在Quartz调度器中,当一个Trigger被触发时,调度器会使用与之关联的JobDetail信息创建一个Job实例,并执行该实例中定义的任务逻辑。这个Job实例是任务的动态实例。一个JobDetail可以有多个关联的Trigger,每个关联的Trigger都会创建一个新的Job实例并并发执行。
  • JobDetail是描述任务的静态信息,而Trigger是定义任务触发时间和执行规则的组件。

以下是一个使用Quartz进行调度的简单示例代码:

java 复制代码
// 创建JobDetail对象
JobDetail jobDetail = JobBuilder.newJob(MyJob.class)
        .withIdentity("myJob", "group1")
        .build();

// 创建Trigger对象
Trigger trigger = TriggerBuilder.newTrigger()
        .withIdentity("myTrigger", "group1")
        .withSchedule(SimpleScheduleBuilder.repeatSecondlyForever(10))
        .build();

// 创建调度器对象
Scheduler scheduler = new StdSchedulerFactory().getScheduler();

// 将JobDetail对象和Trigger对象关联起来
scheduler.scheduleJob(jobDetail, trigger);

// 启动调度器开始调度任务
scheduler.start();

Schedule介绍

在Quartz中,调度(schedule)将一个具体的jobDetail 和trigger 关联起来,安排和管理任务的执行时间和频率。

调度过程包括以下步骤:

  • 创建JobDetail对象,描述任务的具体细节,包括任务的标识、执行类、执行时所需的数据/参数等信息。创建Trigger对象,定义任务的触发时间和执行规则,例如简单触发器(SimpleTrigger)、Cron触发器(CronTrigger)等。
  • 将JobDetail对象和Trigger对象关联起来,形成任务调度的单元。将任务调度单元通过调度器(Scheduler)的scheduleJob()方法进行调度,即安排任务的执行。调度器会根据Trigger定义的触发时间规则,按照预定的时间表执行任务。当触发时间到达时,调度器会创建一个Job实例,执行任务逻辑。任务执行完毕后,调度器会根据Trigger的定义,继续安排下一次任务的执行,并持续调度任务执行。
  • 通过Quartz的调度功能,可以灵活地安排和管理任务的执行时间,实现定时任务和定时调度的需求。可以配置多个JobDetail和Trigger来实现不同的任务调度策略,并通过调度器进行统一管理和执行。

以下是一个使用Quartz进行调度的简单示例代码:

java 复制代码
// 创建JobDetail对象
JobDetail jobDetail = JobBuilder.newJob(MyJob.class)
        .withIdentity("myJob", "group1")
        .build();

// 创建Trigger对象
Trigger trigger = TriggerBuilder.newTrigger()
        .withIdentity("myTrigger", "group1")
        .withSchedule(SimpleScheduleBuilder.repeatSecondlyForever(10))
        .build();

// 创建调度器对象
Scheduler scheduler = new StdSchedulerFactory().getScheduler();

// 将JobDetail对象和Trigger对象关联起来
scheduler.scheduleJob(jobDetail, trigger);

// 启动调度器开始调度任务
scheduler.start();

在上述示例中,使用Quartz创建了一个任务调度的简单示例。创建了一个JobDetail对象来描述任务细节,并创建一个触发器Trigger来定义任务的触发规则,每10秒执行一次。然后将JobDetail对象和Trigger对象关联起来,并通过调度器调度任务的执行。

SimpleScheduleBuilder(简单调度器)和 CronScheduleBuilder(Cron 调度器)主要有以下区别:

  1. 时间表达的灵活性:
    • SimpleScheduleBuilder 主要用于设置简单的重复模式,例如每隔固定的时间间隔执行任务,或者在一定次数内重复执行。
    • CronScheduleBuilder 基于 Cron 表达式,能够提供非常灵活和复杂的时间调度规则,可以精确到秒、分钟、小时、日期、月份、星期等,并且可以支持各种复杂的周期性和特定日期时间的组合。
  2. 复杂程度:
    • SimpleScheduleBuilder 的配置相对简单直观,适合简单的周期性任务。
    • CronScheduleBuilder 由于 Cron 表达式的复杂性,配置可能相对较难理解和编写,需要对 Cron 表达式的语法有一定的了解。
  3. 功能丰富度:
    • CronScheduleBuilder 能够实现更丰富的定时需求,比如在特定的月份、星期几、日期等特定条件下执行任务。
    • SimpleScheduleBuilder 的功能相对较为基础。

四.注解扩展

非并行执行

@DisallowConcurrentExecution 是否允许并行执行;加上这个注解,同一个任务不能被并行执行:

场景:定时任务每隔 3分钟,退款订单; 每次任务执行时间假如超过了3分钟;则在第二次任务触发时,则可能发生同一个订单重复退款的情况;

此时需要增加@DisallowConcurrentExecution 让其同一个job 不能并行的执行(是否同一个job 通过JobKey 判断,JobKey 包含了job name 和group )

数据持久化

PersistJobDataAfterExecution 对数据持久化,在任务执行完成后持久化作业数据,以便下次再次执行该作业时能够使用上一次执行时修改的数据。

五.任务失火

misfire,中文意思是"失火"。在 quartz 中的含义是:到了任务触发时间,但是任务却没有被触发失火的原因可能是:

  • 使用了 @DisallowConcurrentExecution 注解,而且任务的执行时间 >任务间隔
  • 线程池满了,没有资源执行任务
  • 机器宕机或者人为停止,过段时间恢复运行

SimpleScheduleBuilder 简单调度任务器失火策略:

  • MISFIRE_INSTRUCTION_IGNORE_MISFIRE_POLICY:忽略失火,即不采取任何特殊处理,按照原定计划等待下一次触发。
  • MISFIRE_INSTRUCTION_FIRE_NOW:立即触发执行一次任务。
  • MISFIRE_INSTRUCTION_RESCHEDULE_NOW_WITH_REMAINING_COUNT:立即以当前时间为触发频率执行,并将重复次数设置为剩余的次数。此策略会导致 trigger 忘记原始设置的开始时间和重复次数。
  • MISFIRE_INSTRUCTION_NEXT_WITH_EXISTING_COUNT:不立即触发,等待下次触发频率周期时刻,按照既定的结束时间(finalTime)执行剩余的周期次数,以调度或恢复调度的时刻为基准计算周期频率。即使中间出现暂停(pause),恢复(resume)后保持 finalTime 时间不变。
  • MISFIRE_INSTRUCTION_NEXT_WITH_REMAINING_COUNT:与MISFIRE_INSTRUCTION_NEXT_WITH_EXISTING_COUNT类似,也是等待下次触发频率周期时刻执行剩余次数,但同样保持 finalTime 不变。
  • MISFIRE_INSTRUCTION_NOW_WITH_EXISTING_COUNT(默认):立即以当前时间为触发频率执行,执行至 finalTime 的剩余周期次数,以调度或恢复调度的时刻为基准计算周期频率,调整后的 finalTime 会略大于根据开始时间计算得到的 finalTime 值。
  • MISFIRE_INSTRUCTION_NOW_WITH_REMAINING_COUNT:类似于MISFIRE_INSTRUCTION_NOW_WITH_EXISTING_COUNT,也是立即以当前时间为触发频率执行剩余次数,并根据剩余次数和当前时间计算 finalTime,其值也会略大于根据开始时间计算的 finalTime 值。

CronScheduleBuilder Cron 调度器失火策略:

  • MISFIRE_INSTRUCTION_DO_NOTHING:不触发立即执行,而是等待下次 Cron 触发频率到达时刻开始按照 Cron 频率依次执行。
  • MISFIRE_INSTRUCTION_FIRE_AND_PROCEED(默认):以当前时间为触发频率立刻触发一次执行,然后按照 Cron 频率依次执行。
  • MISFIRE_INSTRUCTION_IGNORE_MISFIRES:以错过的第一个频率时间立刻开始执行,重做错过的所有频率周期,当下一次触发频率发生时间大于当前时间后,再按照正常的 Cron 频率依次执行。

六.异常处理

任务执行过程中抛出异常,后续任务正常执行,不影响后续的任务调度;如果是已知的异常,可以在catch 中进行一次处理后,重新发起下一次任务的调用;

1.任务异常后手动触发补偿本次任务

第一种方式: 重新构建任务后 通过startNow 启动任务

第二种方式:每次 调用JobExecutionContext 都会产生一个新的context:使用同一个context,对job或者trigger中的JobDataMap 进行数据修改;

2.任务异常后续改关联任务不在执行

获取到跟当前job 所有的触发器,进行任务的停止执行;

第一种方式:

第二种方式:

七.日期排除

定时任务的执行,需要把某些时间排除在外;

想一下这样的场景,某产业园有家食堂,给 办过会员卡的用户 每天早上10点发一条短信"xxx您好,本店今日供应午餐xxx,欢迎前来就餐"此时就需要将节假日排除在外,quartz 中提供了几种类 来处理:

  • CronCalendar 用来排除 给定CronExpression表示的 时间集
  • AnnualCalendar 用来排除 年 中的 天
  • HolidayCalendar 用来排除 某年 中的 某天 (与 AnnualCalendar 类似,区别是把年考虑进去了)
  • MonthlyCalendar用来排除 月 中的 天
  • WeeklyCalendar 用来排除 星期 中的 天
  • DailyCalendar 用来排除 一天中的 某个时间段 (不能跨天)(可以反转时间段的作用)

用法示例:

相关推荐
p-knowledge3 分钟前
spring集成kafka
java·spring·kafka
新手小袁_J26 分钟前
RabbitMQ的发布订阅模式
java·开发语言·redis·spring·缓存·java-rabbitmq
小笨猪-30 分钟前
RabbitMQ应用问题
java·redis·分布式·rabbitmq
码上一元1 小时前
缓存淘汰策略:Redis中的内存管理艺术
java·redis·缓存
cyt涛1 小时前
SpringCloudGateway — 网关路由
java·开发语言·网关·gateway·路由·断言·转发
睡觉谁叫1 小时前
一文解秘Rust如何与Java互操作
android·java·flutter·跨平台
漫天转悠2 小时前
windwos安装多版本Maven(图文详细版)
java·maven
慧都小妮子2 小时前
Spire.PDF for .NET【页面设置】演示:获取 PDF 文件中的页数
java·pdf·.net
minos.cpp3 小时前
《Java并发编程的艺术》笔记 —— 第一章
java·笔记·idea
程序猿进阶3 小时前
系统上云-流量分析和链路分析
java·后端·阿里云·面试·性能优化·系统架构·云计算