定时任务之Quartz使用

定时任务之Quartz使用

Quartz是一个用Java开发的开源定时任务框架。
Quartz的核心对象如下:

Quartz中的概念介绍

JobDetail

  • JobDetail就是作业详细信息,包含作业名称、作业组名称、作业描述、具体作业实现类型等信息
  • JobDetail有一个唯一的名称(name)和组名(group),它们共同构成了作业在调度器中的唯一标识,通过这种方式,用户可以对不同的作业进行组织和分类
  • JobDetail必须关联一个实现了Job接口的类,该类封装了作业的实际执行逻辑,通常使用JobBuilder的ofClass方法来指定关联的Job
  • JobDetail中有一个JobDataMap属性,使用键值对存储数据,这些数据会在作业执行时传递给关联的Job实例,可用传递运行时上下文所需要的数据

在这里插入图片描述

Job

  • 任务的执行逻辑,任何需要在Quartz中执行的任务都需要实现Job接口
  • Job接口中只有一个方法:void execute(JobExecutionContext context) throws JobExecutionException
  • execute 方法是每个Job 类必须实现的核心逻辑,当作业被触发并分配给工作线程执行时,该方法会被调用
  • JobExecutionContext 参数提供了与作业执行相关的上下文信息,包括触发作业的 Trigger、作业自身的 JobDetail(含 JobDataMap)、调度器的Scheduler等
  • 如果作业的执行时间很长,但是每个作业被触发的间隔时间很短,有可能会出现并发执行的情况;可以通过注解设置并发策略
    • @DisallowConcurrentExecution:标记在 Job 类上,指示调度器不应在同一时刻并发执行同一个作业实例
    • @PersistJobDataAfterExecution:标记在 Job 类上,表示作业成功执行后,应将修改过的 JobDataMap 数据持久化保存

Trigger

  • 触发器,定义Job的触发时机、重复频率以及其它的调度相关的细节;Trigger 必须与一个已定义的 JobDetail 关联,表明它将触发哪个作业的执行

  • SimpleTrigger:用于安排一次性或重复一定次数的简单调度任务;SimpleTrigger的主要属性如下

    • startTime:触发器首次生效的时间点
    • endTime:触发器失效的时间点(可选),超过此时间则不再触发作业
    • repeatCount: 重复执行的次数(SimpleTrigger),0 表示只执行一次,-1 或 RepeatIndefinitely 表示无限次重复
    • repeatInterval: 重复执行之间的间隔时间(单位通常是毫秒,SimpleTrigger)
  • CronTrigger: 基于 Cron 表达式的复杂调度触发器,支持按照诸如分钟、小时、天、月、周等周期性模式来触发作业。CronTrigger 提供了极大的灵活性,适用于各种复杂的定时任务场景,如每周一至周五的某个时段执行、每月最后一个工作日执行等

  • Trigger 有其独立的生命周期,可以被暂停、恢复、删除,也可以动态修改其触发规则

  • Misfire Instructions:

    • 当调度器因故未能在预期时间触发作业时(称为 Misfire),可以通过设置 Misfire Instructions 指定在这种情况下应该如何处理。例如,可以选择立即执行、跳过此次执行、按原计划下一次触发时间执行等

Scheduler

  • Scheduler是Quartz框架的核心组件,它负责管理和调度所有Job及其对应的触发器,主要职责包括:
    • 注册和管理Job:Scheduler 维护一个内部的 Job 注册表,用于存储所有已定义的 JobDetail 对象。提供 API 用于添加、修改、查询和删除 JobDetail
    • 调度 Triggers:根据关联的 Trigger 规则,决定何时触发相应的 Job 执行。处理 Trigger 的触发事件,如首次触发、重复触发、结束触发等
    • 监控与控制:监听并响应 Job 和 Trigger 的状态变化,如成功、失败、暂停、恢复等;提供 API 用于启动、停止、暂停、恢复整个调度器或单个 Job 与 Trigger
      JobBuilder
  • 用于构建JobDetail实例

TriggerBuilder

  • 用于构建Trigger实例

Quartz中的表

当Quartz被配置为使用JDBC Strore时,它会手动或者自动地在数据库中创建出以qrtz_开头的表,这些表用来存储与调度相关的各种实体和状态信息。

  • qrtz_triggers

    • 存储触发器的相关信息。每个触发器对应表中的一条记录
    • 主要字段包括:
      • trigger_name, trigger_group:组成触发器的唯一标识
      • job_name, job_group: 关联的作业名称和组
      • trigger_state: 触发器当前的状态;如:WAITING、ACQUIRED、PAUSED、COMPLETE、ERROR等
      • trigger_type: 触发器类型;如:SIMPLE、CRON等
      • start_time, end_time:触发器的有效起止时间
      • next_fire_time, previous_fire_time: 触发器的下一次及上一次实际触发时间
      • priority: 触发器的优先级,用于决定多个触发器同时准备就绪时的执行顺序
  • qrtz_job_details

    • 存放一个JobDetail的详细信息,每个Job对应表中的一条记录
    • 主要字段如下:
      • job_class_name: 实现作业逻辑的类的全限定名,Quartz 根据此字段值来实例化作业对象
    • job_name 和 job_group:作业的唯一标识,通过名称和组进行区分
    • is_durable:标记作业是否持久化。如果为 true,即使调度器关闭,作业也会被保存并在调度器重启后恢复
    • is_nonconcurrent:标记作业是否允许并发执行。如果为 true,则同一时刻只能有一个实例运行
    • requests_recovery:标记作业是否请求在执行失败后被重新安排执行
  • qrtz_scheduler_state

    • 存储调度器实例的状态信息
    • 主要字段如下:
      • instance_name: 调度器实例的唯一标识
      • last_checkin_time: 最近一次心跳检查的时间
      • checkin_interval: 心跳检查间隔,用于监控调度器的健康状态
  • qrtz_locks

    • 存储 Quartz 内部使用的锁信息,用于协调多节点集群或单节点内的并发访问,保证数据一致性
    • 包括各种全局和特定资源的锁记录,如 TRIGGER_ACCESS, JOB_ACCESS, CALENDAR_ACCESS 等
  • qrtz_cron_triggers

    • 专门用于存储 CronTrigger 类型触发器的 Cron 表达式及其相关配置
    • cron_expression字段是用于定义复杂时间规则的Cron表达式
  • qrtz_blob_triggers

    • 用于存储二进制大对象(BLOB)类型的触发器数据,通常用于存储那些无法用标准字段表示的复杂触发器信息
  • qrtz_calendars

    • 存储 Quartz 日历(Calendar)对象,用于定义排除某些特定时间范围(如节假日)的复杂调度规则
  • qrtz_simple_triggers

    • 仅适用于简单触发器(SimpleTrigger),存储简单触发器特有的重复次数、间隔等信息。
    • 如果触发器是基于时间间隔的简单触发器,其详细配置将存储在此表中
  • qrtz_fired_triggers

    • 记录已触发(fired)但尚未完成的触发器实例。当触发器触发后,直到作业执行完毕或触发器完成其生命周期,相应的记录才会从这张表中删除
    • 用于跟踪调度器内部状态,确保作业正确执行和故障恢复
  • qrtz_job_listeners 和 qrtz_trigger_listeners

    • 分别用于存储作业监听器(JobListener)和触发器监听器(TriggerListener)的配置信息,这些监听器可以在作业执行的特定阶段被触发以执行额外操作

Quartz使用

引入Quartz依赖,目前Quartz的最新稳定版本为2.3.0

        <dependency>
            <groupId>org.quartz-scheduler</groupId>
            <artifactId>quartz</artifactId>
            <version>2.3.0</version>
        </dependency>

Quartz配置

spring:
  quartz:
    job-store-type: jdbc
    jdbc:
      initialize-schema: always  #每次启动时创建quartz表,第一次启动后请改为never,不然会报错
  application:
    name: quartz-demo
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    jdbc-url: jdbc:mysql://localhost:3306/cloud-demo?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai
    username: root
    password: root

using:
  spring:
    schedulerFactory: true  #控制使用哪种整合方式

Quartz单独使用

Quartz配置文件如下

# thread-pool
org.quartz.threadPool.class=org.quartz.simpl.SimpleThreadPool
org.quartz.threadPool.threadCount=10
org.quartz.threadPool.threadPriority=5
org.quartz.threadPool.threadsInheritContextClassLoaderOfInitializingThread=true
 
## Spring boot 3.0以后需要使用LocalDataSourceJobStore类型
org.quartz.jobStore.class=org.springframework.scheduling.quartz.LocalDataSourceJobStore
#??????????????????????Triggers???????????????????????????????????60000?60???
org.quartz.jobStore.misfireThreshold=60000
org.quartz.jobStore.driverDelegateClass=org.quartz.impl.jdbcjobstore.StdJDBCDelegate
org.quartz.jobStore.dataSource=quartzDataSource

Job实现类:

@Component
@DisallowConcurrentExecution
public class SampleJob implements Job {

    private static final Logger logger = LoggerFactory.getLogger(SampleJob.class);

    /**
     * 当触发器Trigger被触发时会执行execute方法
     * @param jobExecutionContext
     * @throws JobExecutionException
     */
    @Override
    public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {
        JobDetail jobDetail = jobExecutionContext.getJobDetail();
        String description = jobDetail.getDescription();
        String name = jobDetail.getKey().getName();
        String group = jobDetail.getKey().getGroup();
        logger.info("jobDetail:{},name:{},group:{}", description, name, group);
    }

}

JobDetail、Trigger、Scheduler配置

@Configuration
@ConditionalOnExpression("'${using.spring.schedulerFactory}' == 'false'")
public class QuartzConfiguration {

    private static final Logger LOGGER = LoggerFactory.getLogger(QuartzConfiguration.class);

    @Autowired
    private ApplicationContext applicationContext;

    @PostConstruct
    public void init() {
        LOGGER.info("hello world from Quartz...");
    }

    @Bean(name = "springBeanJobFactory")
    public SpringBeanJobFactory springBeanJobFactory() {
        SpringBeanJobFactory  springBeanJobFactory = new SpringBeanJobFactory();
        LOGGER.debug("configuring Job factory");
        springBeanJobFactory.setApplicationContext(applicationContext);
        return springBeanJobFactory;
    }

    @Bean(name = "scheduler")
    public Scheduler scheduler(@Autowired Trigger trigger, @Autowired JobDetail jobDetail,
                               @Autowired SchedulerFactoryBean factoryBean) throws SchedulerException {
        Scheduler scheduler = factoryBean.getScheduler();
        scheduler.scheduleJob(jobDetail, trigger);
        scheduler.start();
        return scheduler;
    }


    @Bean(name = "schedulerFactoryBean")
    public SchedulerFactoryBean schedulerFactoryBean() throws IOException {
        SchedulerFactoryBean schedulerFactoryBean = new SchedulerFactoryBean();
        schedulerFactoryBean.setJobFactory(springBeanJobFactory());
        schedulerFactoryBean.setQuartzProperties(quartzProperties());
        return schedulerFactoryBean;
    }



    public Properties quartzProperties() throws IOException {
        PropertiesFactoryBean propertiesFactoryBean = new PropertiesFactoryBean();
        propertiesFactoryBean.setLocation(new ClassPathResource("quartz.properties"));
        propertiesFactoryBean.setIgnoreResourceNotFound(false);
        propertiesFactoryBean.setSingleton(true);
        try {
            propertiesFactoryBean.afterPropertiesSet();
        } catch (Exception e) {
            LOGGER.error("quartz properties load fail", e);
        }
        return propertiesFactoryBean.getObject();
    }


    @Bean(name = "jobDetail")
    public JobDetail jobDetail() {
        return JobBuilder.newJob(SampleJob.class).storeDurably().withIdentity(JobKey.jobKey("Quartz_key"))
                .withDescription("SampleJob")
                .build();
    }


    @Bean(name = "trigger")
    public Trigger trigger(@Autowired JobDetail jobDetail) {
        Trigger trigger = TriggerBuilder.newTrigger().forJob(jobDetail)
                .withIdentity(TriggerKey.triggerKey("Quartz_Trigger"))
                .withDescription("Sample Trigger")
                .withSchedule(SimpleScheduleBuilder.simpleSchedule().withIntervalInSeconds(10).repeatForever())
                .build();
        return trigger;
    }

}

Spring Boot整合Quartz

@Configuration
@ConditionalOnExpression("'${using.spring.schedulerFactory}'== 'true'")
public class SpringQuartzConfiguration {

    private static final Logger logger = LoggerFactory.getLogger(SpringQuartzConfiguration.class);

    @Autowired
    private ApplicationContext applicationContext;

    @PostConstruct
    public void init() {
        logger.debug("SpringQuartzConfiguration init");
    }


    @Bean(name = "springBeanJobFactory")
    public SpringBeanJobFactory springBeanJobFactory() {
        logger.debug("configure job factory");
        AutoWiringSpringBeanJobFactory autoWiringSpringBeanJobFactory = new AutoWiringSpringBeanJobFactory();
        autoWiringSpringBeanJobFactory.setApplicationContext(applicationContext);
        return autoWiringSpringBeanJobFactory;
    }


    @Bean
    public SchedulerFactoryBean scheduler(@Autowired Trigger trigger, JobDetail jobDetail, DataSource dataSource) {
        SchedulerFactoryBean factoryBean = new SchedulerFactoryBean();
        //设置quartz配置文件
        factoryBean.setConfigLocation(new ClassPathResource("quartz.properties"));
        factoryBean.setJobFactory(springBeanJobFactory());
        //设置
        factoryBean.setJobDetails(jobDetail);
        //实则触发器
        factoryBean.setTriggers(trigger);
        //设置数据源
        factoryBean.setDataSource(dataSource);
        return factoryBean;
    }



    @Bean
    public JobDetailFactoryBean jobDetail() {
        JobDetailFactoryBean jobDetailFactoryBean = new JobDetailFactoryBean();
        jobDetailFactoryBean.setJobClass(SampleJob.class);
        jobDetailFactoryBean.setDescription("");

        jobDetailFactoryBean.setDurability(true);
        return jobDetailFactoryBean;
    }


    @Bean
    public SimpleTriggerFactoryBean simpleTriggerFactoryBean(JobDetail jobDetail) {
        SimpleTriggerFactoryBean simpleTriggerFactoryBean = new SimpleTriggerFactoryBean();
        simpleTriggerFactoryBean.setJobDetail(jobDetail);
        int frequencyInSec = 10;
        simpleTriggerFactoryBean.setRepeatInterval(frequencyInSec * 1000);
        simpleTriggerFactoryBean.setRepeatCount(SimpleTrigger.REPEAT_INDEFINITELY);
        simpleTriggerFactoryBean.setName("Quartz_Name");
        return simpleTriggerFactoryBean;
    }


    @Bean
    @QuartzDataSource
    @ConfigurationProperties(prefix = "spring.datasource")
    public DataSource dataSource() {
        return DataSourceBuilder.create().build();
    }
    
}

参考

  1. Scheduling in Spring with Quartz
  2. spring-quartz代码示例
  3. SpringBoot 之 Quartz 使用
  4. 分布式定时任务框架选型,写得太好了!
  5. 浅析后端微服务涉及到定时任务时如何解决多集群定时任务重复执行并发的方案对比
  • 12
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 好的,下面是关于使用Spring BootQuartz实现定时任务的简介: Quartz是一个流行的Java定时任务框架,可以在应用程序中执行各种定时任务,如定期备份、数据抽取和数据分析等。而Spring Boot是一个快速开发框架,提供了方便的集成Quartz的方式。 要使用Spring BootQuartz实现定时任务,首先需要在pom.xml文件中添加QuartzSpring Boot Quartz的依赖项。然后,定义一个定时任务,可以使用注解@Scheduled来指定任务的执行时间。例如: ```java @Component public class MyScheduler { @Scheduled(cron = "0 0/10 * * * ?") public void myTask() { //执行定时任务的代码 } } ``` 上述代码表示每隔10分钟执行一次myTask()方法。然后,在Spring Boot的主类中,使用@EnableScheduling注解来开启定时任务的支持。例如: ```java @SpringBootApplication @EnableScheduling public class MyApp { public static void main(String[] args) { SpringApplication.run(MyApp.class, args); } } ``` 最后,启动应用程序,定时任务将按照预定时间自动执行。如果需要更高级的定时任务控制,还可以使用Quartz的其他功能,如JobDetail和Trigger等。 ### 回答2: SpringBoot是一个开源的Java开发框架,它提供了很多有用的工具和插件,其中之一便是定时任务quartzquartz是一款功能强大的开源调度框架,它提供了简单易用的任务调度机制,支持多线程和分布式部署,可以满足大部分的调度需求。 在SpringBoot中,我们可以很方便地使用quartz来实现定时任务。以下是使用SpringBootquartz实现定时任务的基本步骤: 1. 在pom.xml文件中添加quartz的依赖项: ``` <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-quartz</artifactId> </dependency> ``` 2. 创建一个任务类,实现Job接口,并实现execute方法来定义任务逻辑: ``` @Component public class MyTask implements Job { @Override public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException { // 定时任务的执行逻辑 } } ``` 3. 在配置文件中定义任务调度器,并为任务分配cron表达式: ``` spring: quartz: job-store-type: jdbc properties: org.quartz.scheduler.instanceName: MyScheduler org.quartz.threadPool.threadCount: 5 job-details: myJob: durability: true requestsRecovery: true trigger-details: myTrigger: cron: 0 0/1 * * * ? job-name: myJob job-data-map: name: world ``` 4. 在任务执行类中,使用@Scheduled注解来指定任务的执行时间: ``` @Component public class MyTask { @Scheduled(cron = "0 0/1 * * * ?") public void doTask(){ // 定时任务的执行逻辑 } } ``` 以上4种方法都是使用SpringBootquartz实现定时任务的简单示例,根据实际需求可以进行相应调整和扩展。总的来说,使用springbootquartz实现定时任务非常方便,能够让我们更加轻松地管理、调度和执行任务,提高开发效率和质量。 ### 回答3: Spring Boot是一款快速构建基于Spring应用的工具,可帮助简化开发和部署。在Spring Boot中,与Quartz相比,Scheduled定时任务通常是首选的。但是,Quartz比Scheduled更灵活、功能更强大、配置更灵活,因此在一些特殊情况下,使用Quartz进行定时任务调度是比较必要的。 Quartz是用Java语言编写的一个开源的调度框架,其主要用于在一个预定义的时间间隔内重复执行某个任务,或者在特定时间点执行某个任务。Quartz提供了众多功能,比如支持分布式定时任务管理、支持动态创建/删除任务、支持时间表达式、状态存储、监控和事件触发等等。 Spring Boot集成了Quartz非常容易,只需要在Spring Boot项目中添加Quartz相关依赖,在Spring Boot配置文件中增加Quartz配置即可。以下是一个完整的Spring Boot集成Quartz的示例代码: 1.在pom.xml中添加QuartzSpring Boot Starter Web依赖: ``` <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.quartz-scheduler</groupId> <artifactId>quartz</artifactId> <version>${quartz.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context-support</artifactId> </dependency> ``` 2.在配置文件中增加Quartz相关配置: ``` spring.quartz.job-store-type=memory spring.quartz.properties.org.quartz.threadPool.threadCount=10 spring.quartz.properties.org.quartz.jobStore.class=org.quartz.simpl.RAMJobStore ``` 3.编写定时任务处理类: ``` @Component public class JobScheduler { @Autowired private Scheduler scheduler; @Scheduled(cron = "0 0/1 * * * ?") public void doSomething() { JobDetail jobDetail = buildJobDetail(); Trigger trigger = buildJobTrigger(jobDetail); try { scheduler.scheduleJob(jobDetail, trigger); } catch (SchedulerException e) { e.printStackTrace(); } } private JobDetail buildJobDetail() { JobDataMap jobDataMap = new JobDataMap(); jobDataMap.put("key", "value"); return JobBuilder.newJob(Job.class) .withIdentity(UUID.randomUUID().toString(), "job-group") .withDescription("sample job") .usingJobData(jobDataMap) .storeDurably(true) .build(); } private Trigger buildJobTrigger(JobDetail jobDetail) { return TriggerBuilder.newTrigger() .forJob(jobDetail) .withIdentity(UUID.randomUUID().toString(), "trigger-group") .withDescription("sample trigger") .withSchedule(CronScheduleBuilder.cronSchedule("0 0/1 * * * ?")) .build(); } } ``` 虽然Quartz需要对定时任务进行配置,但是它提供了更为灵活的条件和任务调度,非常适合在实际生产环境中使用。总之,使用Spring Boot集成Quartz可以非常方便地实现定时任务的调度,既简单又强大,可以大大提高应用的效率和可靠性。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值