1.后台定时任务是我们二次开发中不可避免的业务。例如,在某些电子商务系统中,可能会定期向用户发送生日优惠券。在一些对帐系统中,可以定期执行对帐。大概在很久以前,每个服务可能只有一台机器,直接在这台机器上一个Timerschedule基本可以满足我们的业务需求,但是随着时代的变迁,单台机器已经远远不能满足我们的需求了,这个时候,我们可能需要10台、20台甚至更多的机器来运行我们的业务,接受我们的流量。这就是我们所说的横向扩展。但是这里有个问题,如果这么多机器还在使用我们的Timerschedule会怎么样呢?在上面的电子商务系统中,有可能向某个用户发送大量的生日优惠券,这会给公司造成很大的损失。所以我们需要一些其他的方法来让定时任务在多台机器上只执行一次。在这里想问一下大家在知道或者使用分布式任务调度框架之前是如何做定时任务的?在Spring项目中,大家肯定都知道Spring-Scheduler,只需要在Spring中bean对应的方法中添加@Scheduler注解就可以完成我们的定时任务,但是仅仅使用这个注解是远远不能保证定时任务一定会完成的。被执行多次。我们需要一些其他的手段来保证。一般来说,方法可能不外乎以下几种(都是基于Spring的项目):一台机器,我们可以用专门的服务台托管一些不太重要的定时任务,然后用单机运行,即使挂了,只要我们能在可接受的时间内接受,几天之内就会恢复,不影响我们的业务。多台机器,加上分布式锁,只要我们在执行任务的时候先获取一个分布式锁,如果长时间获取失败,证明其他服务已经重新运行,如果获取成功,证明没有服务正在运行计划任务,然后可以执行。多台机器,使用ZooKeeper在Leader机器上执行定时任务。很多业务已经使用了ZK,所以在执行定时任务的时候,判断一下你是不是leader。如果不是,则不执行。如果是,执行业务逻辑。我们的宗旨。目前我司也采用以上三种方式进行定时任务。在业务初期,使用这些方法基本可以满足。然而,随着时间的推移,我们遇到的问题越来越多。在这里跟大家分享一下:首先是一道单机题。如何划分业务并不是很重要。这部分本来就很复杂。每个人都可能会说他们的业务很重要。其次,如果单机挂了,有可能是宕机或者别的什么。在某些情况下,这个时间如何保证我们能够在可接受的范围内恢复,是一个难点。目前我们在使用定时任务的时候,如果想让它立即执行,这时候可能需要额外写一个Rest接口或者单独写一个Job。还有就是我们需要改变定时任务的执行时间。比如现在有一个要求,执行时间从每12小时改为每6小时。我们要修改代码,提交pr,然后在线打包,仅仅修改一次又要占用我们很多时间。暂停我们的定时任务是不可能的。当我们的定时任务可能会出现一些问题,比如一些定时告警需求,当告警突然变多的时候,我们就需要暂停一下,停止发送告警。这个时候,我们可能会用一些分发用公式配置的开关来做,然后在逻辑上判断是否开启了定时任务开关,然后再做。虽然这样做比较简单,但是我们需要添加一些与任务无关的逻辑。缺少对计划任务的监控。开发人员无法知道任务失败后的情况。有人说有错误日志。如果一条Error日志只报警一次,你的服务能承受得住吗?一般来说,只有连续几次Error才会触发。Alarm,而且我们定时任务的周期性,不容易触发连续的Error。当然,还有一些或多或少的小问题,这里就不一一列举了。如果你有这种经历,你可以自己慢慢体会。2.研究的基本原则上面第一章讲了我们构架的缘由。不管你想引进什么,改进什么,都需要一个理由,因为任何事情都是有成本的。经常看到一些小项目开始引入对于消息队列,或者分布式事务等等,这是本末倒置。例如,一些博客系统可能会使用消息队列来削峰填谷。这可能不如同步调用快。当我们有理由的时候,我们就可以开始做一些研究或者技术方案设计。在这里,我将谈谈我的研究框架的一些基本原则。如果以后有类似的研究框架需求,可以套用到这上面。简单-开发人员可以轻松访问,用户也可以轻松使用。文档丰富,有很多开源项目文档很少,当然也有一些开源项目只有英文文档。如果你的英语不好,你可能需要考虑大部分是中文的文件。有管理界面,非常方便进行操作和统计。支持主流框架:比如Spring、Springboot等,当然这个至少要支持你业务中的主流框架。该框架轻巧,易于根据您的需要进行定制。高性能、高可靠、高可用:框架不能成为业务的瓶颈。代码更新频率和社区使用:公司用得越多,人越喜欢。代码更新频率越高,出现的问题就越少。最好由大厂开源维护。多语言需求:如果你的业务有多语言需求,比如你的公司使用了很多开发语言,需要一个调度框架,那么你就需要使用多语言支持。比如Rpc支持多语言的代表就是Thrift。能不能解决现在的痛点:这个最重要。如果你连自己的问题都解决不了,那用这个有什么意义呢?有了上述原则之后,我们就可以进入研究了。3.研究框架3.1TBSchedule总体研究了Java系统的一些框架。可以先看看阿里有没有开源。毕竟阿里这几年在开源方面做得非常好。然后上网一搜,发现阿里已经开源12年了。开源了一个调度框架TBSchedule,现在搜索代码,发现茶凉了,代码也清理干净了。当然也有个人项目会fork并持续维护,但是用户确实很少,这里就不多说了。github地址:github.com/taobao/TBSc...3.2elastic-jobelastic-Job是当当网开源的分布式调度解决方案,由两个独立的子项目Elastic-Job-Lite和Elastic-Job-Cloud组成。定位为轻量级去中心化解决方案,以jar包的形式为分布式任务提供协调服务。支持分布式调度协调、弹性伸缩、故障转移、重触发未执行作业、并行调度、自诊断修复等功能特性。这个框架在大约2年前非常流行。当时很多公司都在用,想必很多人都听说过,可惜现在已经不再维护了。代码已经2年没有更新了。这违反了更新频率的原则。如果有问题,可能没人帮你,所以不推荐。github地址:github.com/elasticjob/…3.3网上一些比较小众的。一些比较小众的githubstar很少,更新频率也很低:Uncode-Schedule,LTS,openCron等,这些不符合我们3.4XXL-JOB因为没有CNCF,Apache等基础等等,对于分布式定时任务来说,选择起来可能没有那么难。与消息队列不同的是,Apache中有好几种:Kafka、rocketmq、plusar等,每一种都有庞大的社区,可能很难取舍。那么我们基本上就剩下两种选择,一种是自研,这种任务调度框架的开发难度远低于消息队列的开发,所以其实很多公司选择自研,比如:美团的Crane这些.但是对于一些比较复杂的中间件,比如消息队列,可能会选择二次开发。比如美团的mafka就是基于kafka二次开发的,滴滴的DDMQ也是基于Rocketmq的。但是目前如果选择自研,在资源上显然是不够的。这里我们还是采用二次开发框架的策略。当然还有XXL-Job的选择:www.xuxueli.com/xxl-job,基本符合我们的原则。目前代码正在不断更新中,问题作者也在积极响应中。有超过200家公司在使用它Home,其中包括以前的评论,其他原则也符合。一般来说,当你决定选择某个框架时,你需要详细列出优点,这样才能让别人信服。xxl-job具有以下特点:简单:支持通过网页对任务进行CRUD操作,操作简单,一分钟上手;动态:支持动态修改任务状态、启动/停止任务、终止正在运行的任务,即时生效;调度中心HA(Central):调度采用中央设计。“调度中心”自研调度组件,支持集群部署,保证调度中心HA;ExecutorHA(distributed):分布式执行任务,任务“executors”支持集群部署,可以保证任务执行HA;注册中心:执行器会定时自动注册任务,调度中心会自动发现注册的任务并触发执行。同时也支持手动录入执行者地址;弹性伸缩:一旦有新的executor机器上线或下线,在下一次调度时会重新分配任务;路由策略:执行器集群部署时提供了丰富的路由策略,包括:first、last、polling、random、consistentHASH、leastfrequentedused、leastrecentlyused、failover、busytransfer等;Failover:在任务路由策略选择“failover”的情况下,如果executor集群中的某台机器发生故障,会自动进行failover,切换到一个正常的executor发送调度请求。阻塞处理策略:调度过于密集,执行者来不及处理时的处理策略。策略包括:单机串行(默认),丢弃后续调度,覆盖先前调度;事件触发:除“Cron模式”和“任务依赖模式”外的触发任务除了执行之外,它还支持基于事件的触发任务。调度中心提供触发单次执行任务的API服务,可根据业务事件灵活触发。任务进度监控:支持实时监控任务进度;滚动实时日志:支持在线查看调度结果,支持实时查看执行者在Rolling模式下输出的完整执行日志。基本上,上述一些功能是我们业务所需要的。所以这里我最终选择了XXL-JOB4。总结一下,俗话说:授人以鱼不如授人以渔。之前的文章每次都介绍了某个框架。这次我更愿意介绍我是如何选择这个框架的。让大家在以后的研究过程中遵循这个思路。如果你也有好的、不同的研究思路,欢迎留言或进群交流。当然,一般研究完成后,作为研究者,如果不了解这个框架的源码和实现原理,那你就是不合格的研究者,所以我会在中详细介绍XXL-Job的实现原理下一篇。
