乐趣区

关于后端:项目实战接入分布式定时任务框架

我是 3y,一年 CRUD 教训用十年的 markdown 程序员👨🏻‍💻长年被誉为优质八股文选手

挺早就布局了要引入分布式定时工作框架了,在年前 austin 就曾经接入了,但代码过年始终都没写,文章也就始终拖到明天了。明天次要就跟大家在聊聊 定时工作 这个话题。

看完这篇文章你会理解到什么是定时工作,以及为什么 austin 我的项目要引入分布式定时工作框架,能够把代码下载下来看到我是怎么应用 xxl-job 的。

01、如何简略实现定时性能?

我是看视频入门 Java 的,那时候学 Java 根底 API 的时候,看的视频也带有讲定时性能(JDK 原生就反对),我记得视频讲师写了 Timer 来解说定时工作。

过后并不知道定时工作有什么理论作用,所以在初学阶段的我,素来没应用过 Timer 来实现定时的性能。

再起初,我学到 并发 了。那时候的讲师提到了 ScheduledExecutorService 这个接口,它比 Timer 更加弱小,个别咱们在 JDK 里能够用它来实现定时的性能

强就强在于 ScheduledExecutorService 外部是线程池,Timer是单线程,它能更正当的利用资源。

我学并发的时候,我也并不太关注它(它并不是并发的重点),所以我也没用过 ScheduledExecutorService 来实现定时的性能。

起初吧,要到学习做我的项目了,那时候视频有个 Quartz 课程。我记得了解了很久,最初我才反馈过去了,原来 写了这么多的代码 就是用它来实现定时的性能。

至于比 ScheduledExecutorServiceTimer好在哪里呢,最直观的是:它反对 cron 表达式。

为啥我会了解很久呢,因为 Quartzapi太简单了(它也有着本人的专业术语和概念性的货色)。不过这种跟着做我的项目的,我是一步一步跟着敲代码的。

Quartz 相干的 API 我是记不住了,但那时候我了解了:原来咱们写代码能够靠「组件包」来实现想要的性能,原来这就是 cron 表达式。

等到我大三的时候,我想用本人学过的知识点来写个小我的项目,也算是梳理一遍本人到底学了什么货色。于是,我想起了Quartz

那时候我曾经学到了 Spring/SpringBoot 了。所以当我在网上搜 SpringQuartz整合的时候,理解到了 SpringTask,再起初发现了@Schedule 注解。

只须要一个简略的注解,就能实现定时工作的性能,并且反对 cron 表达式。

那那那那,还要个锤子的 Quartz 啊!

02、实习 && 工作 定时工作

等我工作了之后,我学到了一个新的名词「分布式定时工作框架」。等我踏入职场了当前,我才发现原来定时工作这么好使!

列举下我实在工作时应用定时工作的常见姿态:

1、动态创建定时工作 推送 经营类的音讯(定时推送音讯)

2、广告结算定时工作 扫表 找到对应的可结算记录(定时扫表更新状态)

3、每天定时更新数据记录(定时更新数据)

还很多人问我有没有用过 分布式事务 ,我往往会答复:没有啊,咱们都是 扫表 一把梭保证数据最终一致性的当然了,如果是面试的时候被问到,能够吹吹分布式事务。实际上是怎么扫表的呢?就是 定时 扫的咯。

另外,我过后简略看了下公司自研的分布式定时工作框架是怎么做的,我记得是基于 Quartz 进行扩大的,扩大有 failover 分片 等等机制。

一般来说,应用定时工作就是在 利用启动 或者 提前在 Web 页面 配置好定时工作(定时工作框架都是反对 cron 表达式的,所以是周期或者定时的工作),这种场景是最最最多的。

03、为什么分布式定时工作

在后面提到 Timer/ScheduledExecutorService/SpringTask(@Schedule) 都是单机的,但咱们一旦上了生产环境,利用部署 往往 都是集群模式的。

在集群下,咱们个别是心愿 某个定时工作 只在某台机器上执行,那这时候,单机实现的定时工作就不太好解决了。

Quartz是有 集群部署 计划的,所以有的人会利用 数据库行锁 或者应用 Redis 分布式锁 来本人实现定时工作跑在某一台 利用机器 上;做必定是能做的,包含有些挺闻名的分布式定时工作框架也是这样做的,能解决问题。

但咱们遇到的问题不单单只有这些,比方我想要反对 容错 性能(失败重试)、分片 性能、手动触发 一次工作、有一个比拟好的治理定时工作的 后盾界面 路由 负载平衡等等。这些性能,就是作为「分布式定时工作框架」所具备的。

既然当初曾经有这么多的轮子了,那咱们作为 应用方 / 需求方 就没必要本人从新实现一套了,用现有的就好了,咱们能够学习现有轮子的实现设计思维。

04、分布式定时工作根底

Quartz是优良的开源组件,它将定时工作形象了三个角色:调度器 执行器 工作,以至于市面上的分布式定时工作框架都有相似角色划分。

对于咱们应用方而言,个别是引入一个 client 包,而后依据它的规定(可能是应用注解标识,又或是实现某个接口),随后自定义咱们本人的定时工作逻辑。

看着下面的执行图对应的角色形象以及个别应用姿态,应该还是比拟容易了解这个过程的。咱们又能够再略微思考两个问题:

1、工作信息以及调度的信息是须要 存储 的,存储在哪?调度器是须要「告诉 」执行器去执行的,那「 告诉」是以什么形式去做?

2、调度器是怎么找到行将须要执行的工作的呢?

针对第一个问题,分布式定时工作框架又能够分成了 两个流派:中心化和去中心化

  • 所谓的「中心化」指的是:调度器和执行器 拆散,调度器对立进行调度,告诉执行器去执行定时工作
  • 所谓的「去中心化」指的是:调度器和执行器 耦合,本人调度本人执行

对于「中心化」流派来说,存储相干的信息很可能是在 数据库 (DataBase),而咱们引入的client 包实际上就是 执行器 相干的代码。调度器 实现了任务调度 的逻辑,近程调用 执行器触发对应的逻辑。

调度器「告诉」执行器去执行工作时,能够是通过「RPC」调用,也能够是把工作信息写入音讯队列给执行器生产来达到目标。

对于「去中心化」流派来说存储相干的信息很可能是在 注册核心 (Zookeeper),而咱们引入的client 包实际上就是 执行器 + 调度器 相干的代码。

依赖注册核心来实现 工作的调配,「中心化」流派在调度的时候是须要保障一个工作只被一台机器生产,这就须要在代码里写分布式锁相干逻辑进行保障,而「去中心化」依赖注册核心就免去了这个环节。

针对第二个问题,调度器是怎么找到行将须要执行的工作的呢?当初个别较新的分布式定时工作框架都用了「工夫轮」。

1、如果咱们日常要找到筹备要执行的工作,可能会把这些工作放在一个 List 里而后进行判断,那此时查问的工夫复杂度为 O(n)

2、略微改良下,咱们可能把这些工作放在一个最小堆里(对工夫进行排序),那此时的增删改工夫复杂度为 O(logn),而查问是 O(1)

3、再改良下,咱们把这些工作放在一个 环形数组 里,那这时候的增删改查工夫复杂度都是 O(1)。但此时的环形数组大小决定着咱们能寄存工作的大小,超出环形数组的工作就须要用另外的数组构造寄存。

4、最初再改良下,咱们能够有 多层 环形数组,不同档次的环形数组的 精度 是不一样的,应用多层环形数组能大大提高咱们的精度。

05、分布式定时工作框架选型

分布式定时工作框架当初可抉择的还是挺多的,比拟闻名的有:XXL-JOB/Elastic-Job/LTS/SchedulerX/Saturn/PowerJob等等等。有条件的公司可能会基于 Quartz 进行拓展,自研一套合乎本人的公司内的分布式定时工作框架。

我并不是做这块出身的,对于我而言,我的 austin 我的项目技术选型次要会关注两块(其实跟抉择 apollo 作为分布式配置核心的理由是一样的):成熟、稳固、社区是否沉闷

这一次我抉择了 xxl-job 作为 austin 的分布式任务调度框架。xxl-job曾经有很多公司都曾经接入了(阐明他的 开箱即用 还是很到位的)。不过最新的一个版本在2021-02,近一年没有比拟大的更新了。

06、为什么 austin 须要分布式定时工作框架

回到 austin 的零碎架构上,austin-admin后盾治理页面曾经被我造出来了,这个后盾管理系统会提供「音讯模板」的治理性能。

那发送一条音讯不单单是「技术侧」调用接口进行发送的,还有很多是「经营侧」通过设置定时进而推送。

而这个性能,就须要用到分布式定时工作框架作为中间件撑持我的业务,并且很重要的一点:分布式定时工作框架须要反对 动静 创立定时工作的性能。

当在页面点击「启动 」的时候,就须要创立一个定时工作,当在页面点击「 暂停 」的时候,就须要进行定时工作,当在页面点击「 删除」模板的时候,如果已经有过定时工作,就须要把它给一起删掉。当在页面点击「编辑」并保留的时候,也须要把进行定时工作。

嗯,所须要的流程就这些了

07、austin 接入 xxl-job

接入 xxl-job 分布式定时工作框架的步骤还是蛮简略的(看下文档根本就会了),我简略说下吧。接入具体的代码大家能够拉 ausitn 的下来看看,我会重点讲讲我接入时的感触。

官网文档:https://www.xuxueli.com/xxl-job/#%E4%BA%8C%E3%80%81%E5%BF%AB%E9%80%9F%E5%85%A5%E9%97%A8

1、本人我的项目上引入 xxl-job-core 的 maven 依赖

2、在 MySQL 中执行 /xxl-job/doc/db/tables_xxl_job.sql 的 SQL 脚本

3、从 GiteeGitHub下载 xxl-job 的源码,批改 xxl-job-admin 调度核心的 数据库 配置,启动 xxl-job-admin 我的项目。

4、在本人我的项目上增加 xxl-job 相干的配置信息

5、应用 @XxlJob 注解润饰办法编写定时工作的相干逻辑

从接入或者曾经看过文档的小伙伴应该就很容易发现,xxl-job它是属于「中心化」流派的分布式定时工作框架,调度器和执行器是拆散的。

在后面我提到了 austin 须要 动静 增删改定时工作,而 xxl-job 是反对的,但我感觉没封装得足够好,只在调度器上给出了 http 接口。而调用 http 接口是绝对麻烦的,很多相干的 JavaBean 都没有在 core 包定义,只能我本人再写一次。

所以,我花了挺长的工夫和挺多的代码去实现 动静 增删改定时工作这个工作。

调度器和执行器是离开部署的,意味着,调度器和执行器的网络是必须 可通 的:本来我在本地是没有装任何的环境的,包含 MySQL 我都是连贯云服务器的,然而当初我要调试就必须在网络可通的环境内,所以我不得不在本地启动 xxl-job-admin 调度核心来调试。

在启动执行器的时候,会开一个新的端口给 xxl-job-admin 调度核心调用而不是复用 SpringBoot 默认端口也是挺奇怪的?

08、总结

这篇文章次要讲了什么是定时工作、为什么要用定时工作、在 Java 畛域中如果有定时工作相干的需要能够用什么来实现、分布式定时工作的基础知识以及如何接入XXL-JOB

置信大家对分布式定时工作框架有了个根本的理解,如果感兴趣能够挑个开源框架去学学,想理解接入的代码能够把我的 austin 我的项目拉下来看看。

次要的代码就在 austin-cronxxl包下,而分布式应用的代码次要在 austin-webMessageTemplateController跟模板的增删改查耦合在一起了。

下一篇想来讲讲当定时工作被触发,失去了一个人群文件,我是怎么设计去调用音讯进行推送下发的。

都看到这里了,点个赞一点都不过分吧?我是 3y,下期见。

关注我的微信公众号【Java3y】除了技术我还会聊点日常,有些话只能轻轻说~ 【对线面试官 + 从零编写 Java 我的项目】继续高强度更新中!求 star!!原创不易!!求三连!!

austin 我的项目源码 Gitee 链接:gitee.com/austin

austin 我的项目源码 GitHub 链接:github.com/austin

退出移动版