Quartz分布式调度平台高性能无限水平扩容改造实践

962 阅读3分钟

Quartz是一个很棒的开源任务调度框架,基本可以说是任务调度框架的鼻祖,但是在分布式集群部署环境下,遇到大数据量短周期任务时,节点越多,性能相对较差。今天就针对Quartz分布式集群下水平扩容方案尝试进行优化改造。 开源代码:gitee.com/zxxnj/quart…

1.Quartz部分原理介绍

Scheduler(调度器)指调度框架quartz的定时任务调度器的核心处理程序,用来管理定时任务调度与分发,并保证job能被触发执行,Scheduler处于Quartz框架的根位置,驱动着整个Quartz框架。

下图整理了任务调度的大致流程:

image.png

2.Quartz分布式部署实现原理

Quartz采用了去中心化的设计,在分布式集群部署环境下采用DB锁实现集群环境下的并发控制。随着集群实例的增加,锁竞争比较激烈。同一个任务只能有一个节点运行,其他节点将不执行任务,当碰到大量短周期任务时,各个节点竞争DB锁激烈,节点越多性能就会越差。

quartz在mysql环境下获取DB锁如下 SELECT * FROM QRTZ_LOCKS WHERE SCHED_NAME = ? AND LOCK_NAME = ? FOR UPDATE;

image.png

3.性能问题分析

Quartz采用去中心化设计,使用DB锁的方式实现集群环境下分布式调度并发控制,多个实例部署只起到备份作用,水平扩展能力缺失。随着集群实例的增加,锁竞争还会比较激烈。

4.改造方案

1)采用大锁拆小,把锁分散到不同的表中,分表策略,划分多组quartz对应的不同前缀的表,启动多组Scheduler调度器,间接的增加了DB锁的数量,提高了Scheduler定时扫描执行的任务数量。具体可参考上篇文章juejin.cn/post/719628…

2)分表改造后,锁竞争仍然存在,而且单个JVM启用多组quartz会产生大量线程,线程在竞争CPU资源时还将产生其他性能的开销。针对此问题可采用动态分配策略,将多组Scheduler均分到不同实例中,每个Scheduler只存在于唯一实例中,此时可修改Quartz配置去除DB锁。随着任务数量的增加,可综合评估采用分表策略不断增加quartz表以及动态分配Scheduler到不同实例以实现水平扩容满足业务的激增。 可提前定义需要启动多少个Scheduler以及准备集群部署多少个实例,比如启用9个Scheduler对应Scheduler1、Scheduler2 ... Scheduler9 以及启动三个实例节点。

  • 针对每一个实例:
    • n = Scheduler总数/Quartz总实例数
    • m = Scheduler总数%Quartz总实例数
  • 前m个实例每个分配n+1个Scheduler,后面的 (总实例数-m)个实例每个分配n个Scheduler

定义Scheduler分配表,记录实例编号以及上报时间戳LAST_CHECKIN_TIME,间隔10s上报一次心跳,当前时间减去LAST_CHECKIN_TIME超过30s即认为该实例死亡,进行重新分配。

image.png