Skip to content

基于 Spring Boot SmartLifecycle 扩展点启动 quartz 定时任务

在企业级应用开发中,定时任务是一个非常常见的需求。Spring Boot 提供了非常便捷的方式来集成各种定时任务框架,其中 Quartz 是一个功能强大且灵活的调度框架。本文将详细介绍如何在 Spring Boot 项目中整合 Quartz 定时任务,并介绍采用 SmartLifecycle 优雅的启动 quartz 定时任务。

核心组件

  • Job: 用于被调度的任务,JobDetail 描述 Job 的信息,比如类名,作业名称,组名等等,一般通过 JobBuilder 来创建
  • Trigger: 定义调用的的规则,一般是 cron 表达式,一个 Job 可以被多个 Trigger 关联,但是一个 Trigger 只能关联一个 Job, 一般通过 TriggerBuilder 来创建
  • Schedular: 调度器,根据 Trigger 定义的规则调度任务;
  • JobStore: 用于持久化 quartz 的相关数据;

table 介绍

  • qrtz_blob_triggers: 以 Blob 类型存储的触发器
  • qrtz_calendars: 存放日历信息,quartz 可配置一个日历来指定一个时间范围
  • qrtz_cron_triggers: 存放 cron 类型的触发器
  • qrtz_fired_triggers: 存放已触发的触发器
  • qrtz_job_details: 存放一个 jobDetail 信息
  • qrtz_locks: 存储程序的悲观锁的信息(假如使用了悲观锁)
  • qrtz_paused_trigger_grps: 存放暂停掉的触发器
  • qrtz_scheduler_state: 调度器状态
  • qrtz_simple_triggers: 用来存储简单的 Trigger,包括重复次数,间隔,以及已触发的次数。
  • qrtz_simprop_triggers:用来存储存储 CalendarIntervalTrigger 和 DailyTimeIntervalTrigger。
  • qrtz_triggers: 存储了所有触发器的基本信息

整合 spring-boot-starter-quartz

添加相关依赖

springboot 的版本采用 2.7.15

xml
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-jdbc</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-quartz</artifactId>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>8.0.31</version>
        </dependency>
    </dependencies>

定义 Job

@DisallowConcurrentExecution: 同一个任务在任意时间点上只能有一个实例在执行。

java
@Slf4j
@Component
@DisallowConcurrentExecution
public class DemoJob extends QuartzJobBean {


    @Override
    protected void executeInternal(JobExecutionContext context) throws JobExecutionException {
        log.info("执行测试定时任务1");
    }
}


@Slf4j
@Component
@DisallowConcurrentExecution
public class DemoJob2 extends QuartzJobBean {


    @Override
    protected void executeInternal(JobExecutionContext context) throws JobExecutionException {
        log.info("执行测试定时任务2");
    }
}

配置 Job,Trigger,Schedule 之间的关系

java

@Slf4j
@Component
public class QuartzConfig implements SmartLifecycle {


    private boolean isRunning = false;

    @Autowired
    private Scheduler scheduler;

    @Override
    public void start() {
        isRunning = true;
        try {
            loadJobs();
        } catch (SchedulerException e) {
            log.info(e.getMessage());
        }
    }

    @Override
    public void stop() {
        isRunning = false;
    }



    @Override
    public boolean isRunning() {
        return isRunning;
    }


    public void loadJobs() throws SchedulerException {
        // 定义 Job 1
        JobDetail demoJobDetail = JobBuilder.newJob(DemoJob.class)
                .withIdentity("demoJob")
                .storeDurably()
                .build();
        // 定义 Trigger 1
        CronScheduleBuilder cronScheduleBuilder = CronScheduleBuilder.cronSchedule("0/5 * * * * ?");
        CronTrigger trigger = TriggerBuilder
                .newTrigger().forJob(demoJobDetail)
                .withIdentity("demoJobTrigger")
                .withSchedule(cronScheduleBuilder)
                .build();

        // 定义 Job 2
        JobDetail demoJobDetail2 = JobBuilder.newJob(DemoJob2.class)
                .withIdentity("demoJob2")
                .storeDurably()
                .build();
        // 定义 Trigger 2
        CronScheduleBuilder cronScheduleBuilder2 = CronScheduleBuilder.cronSchedule("0/5 * * * * ?");
        CronTrigger trigger2 = TriggerBuilder
                .newTrigger().forJob(demoJobDetail2)
                .withIdentity("demoJobTrigger2")
                .withSchedule(cronScheduleBuilder2)
                .build();

        // 调度 Job 1
        scheduler.scheduleJob(demoJobDetail, trigger);
        // 调度 Job 2
        scheduler.scheduleJob(demoJobDetail2, trigger2);
    }
}

这里采用基于 Spring Boot SmartLifecycle 扩展点,自动启动 job

application.yml 配置相关属性

yml
spring:
  quartz:
    job-store-type: jdbc
    wait-for-jobs-to-complete-on-shutdown: true
    overwrite-existing-jobs: true
    auto-startup: true
    startup-delay: 0s
    jdbc:
      initialize-schema: always # 建议:开发的时候可以先alway,然后把数据库拷贝出来(也可以通过从组件quartz git 仓库拷贝来),通过 flyway 来管理
    properties:
      org:
        quartz:
          scheduler:
            # 调度器实例名称
            instanceName: QuartzScheduler
            # 分布式节点ID自动生成
            instanceId: AUTO
          jobStore:
            class: org.springframework.scheduling.quartz.LocalDataSourceJobStore
            driverDelegateClass: org.quartz.impl.jdbcjobstore.StdJDBCDelegate
            # 表前缀
            tablePrefix: QRTZ_
            # 是否开启集群
            isClustered: true
            # 数据源别名(自定义)
            #            dataSource: quartz
            # 分布式节点有效性检查时间间隔(毫秒)
            clusterCheckinInterval: 10000
            useProperties: false
          # 线程池配置
          threadPool:
            class: org.quartz.simpl.SimpleThreadPool
            threadCount: 10
            threadPriority: 5
            threadsInheritContextClassLoaderOfInitializingThread: true
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3306/quartz_db?useSSL=false&serverTimezone=UTC
    username: root
    password: 123456

测试

启动应用程序可以看到相关的日志执行清空, 以及登录到数据库可以看到相关的表信息

text
2024-11-19 14:17:50.026  INFO 5056 --- [eduler_Worker-1] com.demo.job.crons.DemoJob               : 执行测试定时任务1
2024-11-19 14:17:50.036  INFO 5056 --- [eduler_Worker-2] com.demo.job.crons.DemoJob2              : 执行测试定时任务2
2024-11-19 14:17:55.018  INFO 5056 --- [eduler_Worker-3] com.demo.job.crons.DemoJob               : 执行测试定时任务1
2024-11-19 14:17:55.036  INFO 5056 --- [eduler_Worker-4] com.demo.job.crons.DemoJob2              : 执行测试定时任务2

Released under the MIT License.