分布式定时任务调度框架实践

分布式任务调度框架几乎是每个大型应用必备的工具,本文介绍了任务调度框架使用的需求背景和痛点,对业界普遍使用的开源分布式任务调度框架的使用进行了探究实践,并分析了这几种框架的优劣势和对自身业务的思考 。
一、业务背景1.1 为什么需要使用定时任务调度(1)时间驱动处理场景:整点发送优惠券,每天更新收益,每天刷新标签数据和人群数据 。
(2)批量处理数据:按月批量统计报表数据,批量更新短信状态,实时性要求不高 。
(3)异步执行解耦:活动状态刷新,异步执行离线查询,与内部逻辑解耦 。
1.2 使用需求和痛点(1)任务执行监控告警能力 。
(2)任务可灵活动态配置,无需重启 。
(3)业务透明,低耦合,配置精简,开发方便 。
(4)易测试 。
(5)高可用,无单点故障 。
(6)任务不可重复执行,防止逻辑异常 。
(7)大任务的分发并行处理能力 。
二、开源框架实践与探索 2.1 JAVA 原生 Timer 和ScheduledExecutorService2.1.1 Timer使用

分布式定时任务调度框架实践

文章插图
 
 
Timer缺陷:
  1. Timer底层是使用单线程来处理多个Timer任务,这意味着所有任务实际上都是串行执行,前一个任务的延迟会影响到之后的任务的执行 。
  2. 由于单线程的缘故,一旦某个定时任务在运行时,产生未处理的异常,那么不仅当前这个线程会停止,所有的定时任务都会停止 。
  3. Timer任务执行是依赖于系统绝对时间,系统时间变化会导致执行计划的变更 。
由于上述缺陷,尽量不要使用Timer, idea中也会明确提示,使用ScheduledThreadPoolExecutor替代Timer。
2.1.2 ScheduledExecutorService使用ScheduledExecutorService对于Timer的缺陷进行了修补,首先ScheduledExecutorService内部实现是ScheduledThreadPool线程池,可以支持多个任务并发执行 。
对于某一个线程执行的任务出现异常,也会处理,不会影响其他线程任务的执行,另外ScheduledExecutorService是基于时间间隔的延迟,执行不会由于系统时间的改变发生变化 。
当然,ScheduledExecutorService也有自己的局限性:只能根据任务的延迟来进行调度,无法满足基于绝对时间和日历调度的需求 。
2.2 Spring Task2.2.1 Spring Task 使用spring task 是spring自主开发的轻量级定时任务框架,不需要依赖其他额外的包,配置较为简单 。
此处使用注解配置
分布式定时任务调度框架实践

文章插图
 
2.2.2 Spring Task缺陷Spring Task 本身不支持持久化,也没有推出官方的分布式集群模式,只能靠开发者在业务应用中自己手动扩展实现,无法满足可视化,易配置的需求 。
2.3 永远经典的 Quartz2.3.1 基本介绍Quartz框架是Java领域最著名的开源任务调度工具,也是目前事实上的定时任务标准,几乎全部的开源定时任务框架都是基于Quartz核心调度构建而成 。
2.3.2 原理解析核心组件和架构
分布式定时任务调度框架实践

文章插图
 
 关键概念
(1)Scheduler:任务调度器,是执行任务调度的控制器 。本质上是一个计划调度容器,注册了全部Trigger和对应的JobDetail, 使用线程池作为任务运行的基础组件,提高任务执行效率 。
(2)Trigger:触发器,用于定义任务调度的时间规则,告诉任务调度器什么时候触发任务,其中CronTrigger是基于cron表达式构建的功能强大的触发器 。
(3)Calendar:日历特定时间点的集合 。一个trigger可以包含多个Calendar,可用于排除或包含某些时间点 。
(4)JobDetail:是一个可执行的工作,用来描述Job实现类及其它相关的静态信息,如Job的名称、监听器等相关信息 。
(5)Job:任务执行接口,只有一个execute方法,用于执行真正的业务逻辑 。
(6)JobStore:任务存储方式,主要有RAMJobStore和JDBCJobStore,RAMJobStore是存储在JVM的内存中,有丢失和数量受限的风险,JDBCJobStore是将任务信息持久化到数据库中,支持集群 。
2.3.3 实践说明(1)关于Quartz的基本使用
  • 可参考Quartz官方文档和网上博客实践教程 。
(2)业务使用要满足动态修改和重启不丢失, 一般需要使用数据库进行保存 。