SpringBoot + MySql + Quartz 集群模式部署

背景

quartz 可用于管理调度定时任务,有集群模式和单机模式,quartz 的单机模式部署,所有任务执行信息都在内存中保存,存在单点故障,quartz 的集群模式具备高可用,自动负载均衡等特点,可保障定时任务的执行。

1 SpringBoot + Mysql + Quartz 集群模式搭建

注: 集群模式依赖实例所在机器之间的时间同步,请自行部署 ntp 服务进行时间同步。

1.1 Quartz 相关表建立

  • 去官网下载 quartz, [下载地址]
  • 解压后,执行 docs/dbTables/tables_mysql_innodb.sql 脚本建表
  • 检查 db 中是否存在以下 11 个表
+--------------------------+
| QRTZ_BLOB_TRIGGERS       |
| QRTZ_CALENDARS           |
| QRTZ_CRON_TRIGGERS       |
| QRTZ_FIRED_TRIGGERS      |
| QRTZ_JOB_DETAILS         |
| QRTZ_LOCKS               |
| QRTZ_PAUSED_TRIGGER_GRPS |
| QRTZ_SCHEDULER_STATE     |
| QRTZ_SIMPLE_TRIGGERS     |
| QRTZ_SIMPROP_TRIGGERS    |
| QRTZ_TRIGGERS            |
+--------------------------+

1.2 maven 中引入 Quartz 相关包

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

1.3 创建quartz配置文件

org.quartz.jobStore.useProperties = false
org.quartz.jobStore.tablePrefix = QRTZ_
# 开启集群模式
org.quartz.jobStore.isClustered = true
# 集群实例检测时间间隔 ms
org.quartz.jobStore.clusterCheckinInterval = 5000
# misfire 任务的超时阈值 ms
org.quartz.jobStore.misfireThreshold = 60000
org.quartz.jobStore.txIsolationLevelReadCommitted = true
org.quartz.jobStore.class = org.quartz.impl.jdbcjobstore.JobStoreTX
org.quartz.jobStore.driverDelegateClass = org.quartz.impl.jdbcjobstore.StdJDBCDelegate

org.quartz.scheduler.instanceId= AUTO
org.quartz.scheduler.rmi.export = false
org.quartz.scheduler.rmi.proxy = false
org.quartz.scheduler.wrapJobExecutionInUserTransaction = false

# 工作线程的线程池设置
org.quartz.threadPool.class = org.quartz.simpl.SimpleThreadPool
org.quartz.threadPool.threadCount = 5
org.quartz.threadPool.threadPriority = 5
org.quartz.threadPool.threadsInheritContextClassLoaderOfInitializingThread = true

特别解释一下这个参数 org.quartz.jobStore.misfireThreshold = 60000, misfire 任务为错过调度触发时间的任务,而 misfireThreshold 为判定触发任务为 misfire 的判定条件,比如规定 11:30 要执行一次 Job, 如果因为实例挂掉或者线程池忙导致 11:33 才触发调度,超时了 3 分钟,超时时间 > 60000ms, 因此判定为 misfire。

判定为 misfire 的处理规则在后面的原理介绍相关文章会提及。

1.4 生成 ScheduleFactory Bean

@Configuration
public class SchedulerConfig {
    @Autowired
    private DataSource dataSource;

    @Bean
    public SchedulerFactoryBean schedulerFactoryBean() throws IOException {
        SchedulerFactoryBean factory = new SchedulerFactoryBean();
        factory.setSchedulerName("Cluster_Scheduler");
        factory.setDataSource(dataSource);
        factory.setApplicationContextSchedulerContextKey("applicationContext");
        factory.setQuartzProperties(quartzProperties());
        return factory;
    }

    @Bean
    public Properties quartzProperties() throws IOException {
        PropertiesFactoryBean propertiesFactoryBean = new PropertiesFactoryBean();
        propertiesFactoryBean.setLocation(new ClassPathResource("/quartz.properties"));

        propertiesFactoryBean.afterPropertiesSet();
        return propertiesFactoryBean.getObject();
    }
}

1.5 定义并添加 Job

@PersistJobDataAfterExecution
@DisallowConcurrentExecution
public class QuartzJob extends QuartzJobBean {
    private static final Logger logger = LoggerFactory.getLogger(QuartzJob.class);

    @Override
    protected void executeInternal(JobExecutionContext context) throws JobExecutionException {
        String taskName = context.getJobDetail().getJobDataMap().getString("name");

        logger.info("---> Quartz job {}, {} <----", new Date(), taskName);
    }
}
private void addJob(String id, String jobName) throws SchedulerException {
    Scheduler scheduler = gSchedulerFactory.getScheduler();

    JobDetail jobDetail = JobBuilder.newJob(QuartzJob.class)
            .withIdentity(id, JOB_GROUP_NAME)
            .usingJobData("name", jobName).build();
    CronTrigger trigger = TriggerBuilder.newTrigger()
            .withIdentity(String.valueOf(id), TRIGGER_GROUP_NAME)
            .withSchedule(CronScheduleBuilder.cronSchedule("0/10 * * * * ?")).build();

    scheduler.scheduleJob(jobDetail, trigger);
}

1.6 启动 Scheduler

public void start() throws SchedulerException {
    gSchedulerFactory.getScheduler().start();
}

1.7 启动程序

quartz 集群和其他分布式集群不一样,集群实例之间不需要互相通信,只需要和DB 交互,通过 DB 感知其他势力,实现 Job 调度。因此只需要按照普通 java 程序启动即可,扩容也只需要新启动实例,不需要做额外配置。

下一篇会介绍 quartz 集群模式的原理。

    原文作者:不智鱼
    原文地址: https://www.jianshu.com/p/94511f41c124
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞