我是3y,一年CRUD
教训用十年的markdown
程序员长年被誉为优质八股文选手
挺早就布局了要引入分布式定时工作框架了,在年前austin就曾经接入了,但代码过年始终都没写,文章也就始终拖到明天了。明天次要就跟大家在聊聊定时工作这个话题。
看完这篇文章你会理解到什么是定时工作,以及为什么austin我的项目要引入分布式定时工作框架,能够把代码下载下来看到我是怎么应用xxl-job
的。
01、如何简略实现定时性能?
我是看视频入门Java的,那时候学Java根底API的时候,看的视频也带有讲定时性能(JDK原生就反对),我记得视频讲师写了Timer
来解说定时工作。
过后并不知道定时工作有什么理论作用,所以在初学阶段的我,素来没应用过Timer来实现定时的性能。
再起初,我学到并发了。那时候的讲师提到了ScheduledExecutorService
这个接口,它比Timer
更加弱小,个别咱们在JDK里能够用它来实现定时的性能
强就强在于ScheduledExecutorService
外部是线程池,Timer
是单线程,它能更正当的利用资源。
我学并发的时候,我也并不太关注它(它并不是并发的重点),所以我也没用过ScheduledExecutorService
来实现定时的性能。
起初吧,要到学习做我的项目了,那时候视频有个Quartz
课程。我记得了解了很久,最初我才反馈过去了,原来写了这么多的代码就是用它来实现定时的性能。
至于比ScheduledExecutorService
和Timer
好在哪里呢,最直观的是:它反对cron
表达式。
为啥我会了解很久呢,因为Quartz
的api
太简单了(它也有着本人的专业术语和概念性的货色)。不过这种跟着做我的项目的,我是一步一步跟着敲代码的。
而Quartz
相干的API我是记不住了,但那时候我了解了:原来咱们写代码能够靠「组件包」来实现想要的性能,原来这就是cron
表达式。
等到我大三的时候,我想用本人学过的知识点来写个小我的项目,也算是梳理一遍本人到底学了什么货色。于是,我想起了Quartz
。
那时候我曾经学到了Spring
/SpringBoot
了。所以当我在网上搜Spring
与Quartz
整合的时候,理解到了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、从Gitee
或GitHub
下载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-cron
的xxl
包下,而分布式应用的代码次要在austin-web
的MessageTemplateController
跟模板的增删改查耦合在一起了。
下一篇想来讲讲当定时工作被触发,失去了一个人群文件,我是怎么设计去调用音讯进行推送下发的。
都看到这里了,点个赞一点都不过分吧?我是3y,下期见。
关注我的微信公众号【Java3y】除了技术我还会聊点日常,有些话只能轻轻说~ 【对线面试官+从零编写Java我的项目】 继续高强度更新中!求star!!原创不易!!求三连!!
austin我的项目源码Gitee链接:gitee.com/austin
austin我的项目源码GitHub链接:github.com/austin