轻松构建微服务之分布式任务调度

微信公众号:内核小王子
关注可了解更多关于数据库,JVM内核相关的知识;
如果你有任何疑问也可以加我pigpdong[^1]

前言

???? 我们在应用开发的时候,应该都碰到过这种需求:每天固定时间点跑一个任务;创建一些临时的任务去初始化数据或者做数据迁移;固定一个时间周期去轮询是否有新的状态发生;在java中有两个类可以帮我们处理这种需求,一个是java.util.TimerTask,一个是 java.util.concurrent.ScheduledExecutorService , 但是随着业务的发展,任务调度的需求会越来越多,对调度器的要求也会更高,例如能够监控任务的执行进度,能够根据应用负载动态路由选择更健康的执行器去执行任务,所以我们需要一个系统将调度器和执行器分离开,在调度系统中增加更多功能来辅助我们运维业务中的各种任务。

关键词解释

  • 管理后台:一个后台管理系统,提供web页面供业务人员对任务参数和执行模式进行编辑,查看任务进度和任务结果
  • 执行器:提供任务具体执行的环境,一般指代应用本身,应用本身作为一个执行器,内部可以有多个待执行的任务单元
  • 任务单元:需要定时执行的任务单元,依赖具体业务场景,例如基金行业的日切任务计算每日收益
  • 调度器:依托特定调度的算法,例如每日早上8点,每间隔5分钟等去唤醒执行器去执行指定的任务单元

????我们可以思考一下,一个集中式任务调度系统需要哪些功能点,才能方便我们对业务中的应用进行管理。

  • 1.任务管理,能够通过后台管理页面,新增,删除任务,能够查看当前正在执行的任务进度和执行结果
  • 2.执行器能够在启动和关闭的时候自动通知到调度中心,这样在配置任务单元的时候就不需要指定IP地址去选择对应的执行器了,执行器已经自动注册到调度中心了
  • 3.执行器内部待执行的任务单元通过注解和接口的形式自动被扫描出来,然后向调度中心注册,多个执行器的同一个任务可以做到去重
  • 4.调度中心能够集群部署,避免单点,以及分摊调度负载
  • 5.调度器可以支持特定的路由算法来选择执行器进行调度,例如随机,轮询,权重,指定等
  • 6.支持失败重试,超时预警等功能,在检测到任务执行器明确返回任务执行失败后可以选择另外一个执行成功率高的执行器重新执行
  • 7.支持邮件或者短信向业务人员报警的功能,核心的任务可能需要随时进行监控,有些报表类任务单元在执行完成后输出的报表也可以通过邮件通知到相关业务
  • 8.支持是否允许同一个任务并发执行的模式,这样可以避免一个任务单元还没有执行结束,结果调度器又启动了该任务单元,当任务单元无法保证幂等的时候,调度中心可以做下限制,可以等上一个任务单元结束在执行
  • 9.任务依赖,一个任务执行完立即开始执行另外一个关联任务,或者校验前置任务A没有执行完不能执行后置任务B,这两种情况都是任务系统可以考虑做的
  • 10.支持GLUE模式,可以通过管理后台动态上传一段groove代码,选择执行器,在执行器内部转换成任务单元然后执行,这种方式可以在某些紧急情况下可以不用发布就可以修改线上数据,但是如果没有相关的审计流程可能并不安全,
  • 11.任务分片,支持根据某种分片算法,例如日期间隔,用户ID哈希等,将分片结果以参数的形式分发给多个执行器去并发执行,这样可以解决当一台执行器去执行某个任务单元会执行太慢
  • 12.可以通过管理后台暂停或者取消一个正在执行的任务单元,这个在当这个任务是在批处理数据的情况下发现之前处理的数据有错误可以及时停止,减少影响范围
  • 13.调度器高可以

核心功能解析

????对于一个调度中心,我们要解决的核心问题主要是两个,有了这两个功能那么一个调度中心就基本可用,而其他功能就是将这个系统打造的更易用,更完善
一个是调度器的实现,这个我们可以借助业界常用的quartz框架,quartz也支持集群部署。另外一个是调度器如何通过rpc通知执行器去执行某个任务单元,并将执行参数传输过去,如果执行器是一个web容器,部署在tomcat中,那么可以在执行器中增加一个servlet来接受调度器的请求,在这个servlet内部去派发到对应的任务单元执行和取消,当然我们也可以借助其他的rpc框架,例如dubboo,grpc等,由于调度器和执行器之间的调用比较简单,我们也可以通过netty实现一个简单rpc程序

1.执行器注册

????当没有注册功能的时候,我们需要手工编辑线上的执行器,而每次应用变更IP或者上下线都需要手工维护,如果不维护执行器我们就无法做路由,而且每次调度都需要输入执行器地址。
注册功能我们可以借助zookeeper来实现,执行器也就是我们的应用在启动的时候可以将相关信息写入一个临时节点,当执行器退出的时候这个临时节点会自动删除,而这些信息都可以通知到调度器,类似的注册中心实现还有eureka,etcd等,加入这些注册中心可能让我们的应用很重,执行器和调度器都需要依赖注册中心的客户端应用,而此时我们只是需要将执行器相关信息在启动和退出的时候发送给调度中心,我们可以在调度中心监听一个http端口,执行器在启动和退出的时候可以直接通过httpp将信息发给调度中心,配合域名也很方便的可以做调度中心的负载均衡,当然如果我们调度器和执行器之间本身已经有RPC调用了,这些注册信息也可以通过rpc进行传输,执行器的启动可以在相关核心类的初始化方法中实现,执行器的关闭可以借助相关资源类的释放或者JVM的关闭钩子

2.任务单元的注册

????如果任务单元不自动注册,那么我们每次上线需要手工在管理后台编辑新增任务,并且选择对应的执行器列表,如果任务单元也可以自己向调度中心注册,我们的使用将更加简单,我们可以规定我们的任务单元都实现一个指定的接口,或者加一个注解,更推荐用注解的形式,我们可以通过在注解上可以配置一些调度策略,否则的话这些调度策略只能在接口中体现了,而我们只需要在应用启动的时候将这些接口和注解扫描出来发给注册中心就好了,由于我们的执行器都是分布式部署的,所以同一个任务单元随着应用的重启会向注册中心注册多次,而注册中心需要根据任务单元的特性进行防重复设计,而任务单元在代码废除后,我们可以手工在调度器进行删除,或者先在注解上加一个参数,之后在下一个版本中删除

3.任务中断

????一般我们的任务单元在被调度的时候都会在一个线程工厂创建的特定线程里面运行,而将正在执行的任务中断我们只能调用thread.interrupt()方法,而这个方法可能只能在这个线程wait的时候才能退出,我们建议针对任务单元设计一个volatile变量标示任务是否终止,而在任务单元内部可以循环遍历该变量是否已经取消,这种方式当我们的任务是处理一些批量数据,并且需要循环遍历的时候相对优雅很多

4.任务调度模式

????路由策略,失败重试,超时检查,并发控制,任务依赖,这些我们一般通过编写特定的策略就可以实现,关于分片,类似于数据库的分库分表,这种一般需要任务单元内部支持,而调度中心只是在上层辅助进行调度,选择多少个执行器并发执行,将分片结果以参数形式分发给执行器

5.GLUE模式

????这个我们需要借助groove的classloader将传人的代码加载成JVM里面的任务单元

下面我们分析下开源的XXL-job的实现

????xxl-job 调度中心和执行器之间通过一个叫XXL-RPC的中间件进行双向通讯,调度中心通过RPC向执行器下发调度请求,执行器向调度中心注册执行器信息,XXL-RPC是作者开源的一个机于protobuff协议实现的RPC,而调度器采用开源的quartz实现
调度器和应用端集成,需要通过配置文件来配置调度中心的地址,如果应用是在spring来管理bean的生命周期,可以配置一个bean: XxlJobExecutor,并在属性中设置调度中心的IP地址以及通讯信息,并设置初始化方法init-method, 在init-method方法内部,会去初始化RPC相关类,然后将执行器注册到调度中心,然后会扫描任务单元并缓存对应的bean实例。

调度器的高可用

如果调度器要集群部署,有两种模式

  • 1.主备模式

这种模式需要集群中选一个master,其他节点都为slave节点,同一时刻只有一个调度器能够下发任务指令(所有节点在下发指令前判断是否是master节点),因为我们不允许同一个任务被多个调度器触发执行造成job重复执行,当master节点挂了之后,其他slave选举出一个新的节点作为master继续提供服务,选取master的算法可以利用paxos算法或者raft算法,在多个调度器节点之间进行选举,并且通过心跳检测来发现master节点挂了之后重新进行选举,这类算法实现起来较为复杂,我们可以利用zookeeper的客户端在失去连接后会自动删除这个客户端创建的所有临时节点,并可以通知监听程序来实现,所有的节点在启动的时候都向zookeeper创建同一个节点,第一个创建成功的就是master节点,其他节点发现已经存在该节点就自动作为slave节点,并监听这个节点的变化,当这个节点被删除后,所有的slave节点在收到通知后在创建这个节点来争master,这种方式实现起来比较简单,但是调度器的可用性不能仅仅通过和zookeeper是否能够连接成功表示,有时候调度器如果连接不是数据库也会
导致调度器不可用,这种情况下可以在调度器启动一些监控程序,查看和数据库的连接,cpu负载情况,当达到阈值后主动让出master节点

  • 2.多主模式

这种有点类似于redis集群,当用上面的主备模式,如果被调动的任务单元数量上升会对调度器造成很大的压力,那么一个任务单元如果能够在多个调度器之间做随机选择一个被调度,这样可以减少调度器的压力,当其中一个调度器挂了之后,这个调度器所管理的任务单元将有其他调度器接管,
这种模式下我们可以把任务调度和任务管理拆分出来部署

  • 3.多主多备模式
    类似于redis的集群模式,将任务单元进行分片,每一个master执行器管理一部分,每个master执行器可以有多个slave节点,当master节点挂掉后slave顶上

原文地址:https://www.cnblogs.com/pigpdong/p/10900252.html

时间: 2024-08-30 16:52:10

轻松构建微服务之分布式任务调度的相关文章

Spring Cloud构建微服务架构 分布式服务跟踪(跟踪原理)【Dalston版】

通过上一篇<分布式服务跟踪(入门)>的例子,我们已经通过Spring Cloud Sleuth往微服务应用中添加了实现分布式跟踪具备的基本要素.下面通过本文来详细说说实现分布式服务跟踪的一些要点. 分布式系统中的服务跟踪在理论上并不复杂,它主要包括下面两个关键点: 为了实现请求跟踪,当请求发送到分布式系统的入口端点时,只需要服务跟踪框架为该请求创建一个唯一的跟踪标识,同时在分布式系统内部流转的时候,框架始终保持传递该唯一标识,直到返回给请求方为止,这个唯一标识就是前文中提到的Trace ID.

Spring Cloud构建微服务架构分布式配置中心

Spring Cloud Config是Spring Cloud团队创建的一个全新项目,用来为分布式系统中的基础设施和微服务应用提供集中化的外部配置支持,它分为服务端与客户端两个部分.其中服务端也称为分布式配置中心,它是一个独立的微服务应用,用来连接配置仓库并为客户端提供获取配置信息.加密/解密信息等访问接口:而客户端则是微服务架构中的各个微服务应用或基础设施,它们通过指定的配置中心来管理应用资源与业务相关的配置内容,并在启动的时候从配置中心获取和加载配置信息.Spring Cloud Conf

轻松构建微服务之高效缓存

微信公众号:内核小王子 关注可了解更多关于数据库,JVM内核相关的知识; 如果你有任何疑问也可以加我pigpdong[^1] 前言 在分布式系统中最好耗性能的地方就是最后端的数据库,一般情况下数据库上的insert操作很快,而update和delete操作如果带有索引也不会慢,前提要控制好单表的数据量,并且不要建太多索引, 而最容易出现性能问题的往往是select语句,我们抛开join和group不说,大多数应用都是读多写少而且,而且带有排序和limit等耗时操作,有些查询还需要根据非索引字段进

Spring Cloud构建微服务架构 分布式配置中心(加密解密)

在微服务架构中,我们通常都会采用DevOps的组织方式来降低因团队间沟通造成的巨大成本,以加速微服务应用的交付能力.这就使得原本由运维团队控制的线上信息将交由微服务所属组织的成员自行维护,其中将会包括大量的敏感信息,比如:数据库的账户与密码等.很显然,如果我们直接将敏感信息以明文的方式存储于微服务应用的配置文件中是非常危险的.针对这个问题,Spring Cloud Config提供了对属性进行加密解密的功能,以保护配置文件中的信息安全.比如下面的例子: spring.datasource.use

轻松构建微服务之远程调用

微信公众号:内核小王子 关注可了解更多关于数据库,JVM内核相关的知识; 如果你有任何疑问也可以加我pigpdong[^1] 前言 前面我们了解了,服务调用方和服务提供方,如何能够通过注册中心做到水平扩展,从而满足高可用和高并发,那么服务之间如何才能实现相互调用呢? 综合上一节的内容,服务双方无非就两种模式,一种直接通过网络调用,另一种通过中间代理进行转发,那么无论哪一种我们只需要在服务双方通过socket,弄一个channel,一边write,一边read就可以搞定了 但是仔细一想,我们要解决

轻松构建微服务之监控平台

微信公众号:内核小王子 关注可了解更多关于数据库,JVM内核相关的知识; 如果你有任何疑问也可以加我pigpdong[^1] 前言 随着微服务化,以及集群规模化,传统的日志检索,指标监控,调用链分析作为功能单一的系统,已经无法更好的帮我们分析问题,我们需要一个监控平台将他们之间的数据进行整合和分析,输出更友好的视图给用户. 指标报警 -> 应用 -> 服务 -> 事物 -> 堆栈 -> 日志 以下为随手记的监控平台的Focus架构 下图描述了典型的三层监控体系,将基础层,中间

Spring Cloud构建微服务架构 分布式服务跟踪(抽样收集)【Dalston版】

通过Trace ID和Span ID已经实现了对分布式系统中的请求跟踪,而这些记录的跟踪信息最终会被分析系统收集起来,并用来实现对分布式系统的监控和分析功能,比如:预警延迟过长的请求链路.查询请求链路的调用明细等.此时,我们在对接分析系统时就会碰到一个问题:分析系统在收集跟踪信息的时候,需要收集多少量的跟踪信息才合适呢? 理论上来说,我们收集的跟踪信息越多就可以更好的反映出系统的实际运行情况,并给出更精准的预警和分析,但是在高并发的分布式系统运行时,大量的请求调用会产生海量的跟踪日志信息,如果我

微服务之分布式跟踪系统(springboot+zipkin)

微服务之分布式跟踪系统(springboot+zipkin) 一.zipkin是什么 zipkin是一个开放源代码分布式的跟踪系统,由Twitter公司开源,它致力于收集服务的定时数据,以解决微服务架构中的延迟问题,包括数据的收集.存储.查找和展现.它的理论模型来自于Google Dapper 论文. 每个服务向zipkin报告计时数据,zipkin会根据调用关系通过Zipkin UI生成依赖关系图,显示了多少跟踪请求通过每个服务,该系统让开发者可通过一个 Web 前端轻松的收集和分析数据,例如

使用Spring Boot构建微服务(文末福利)

本文主要内容 学习微服务的关键特征 了解微服务是如何适应云架构的 将业务领域分解成一组微服务 使用Spring Boot实现简单的微服务 掌握基于微服务架构构建应用程序的视角 学习什么时候不应该使用微服务 软件开发的历史充斥着大型开发项目崩溃的故事,这些项目可能投资了数百万美元.集中了行业里众多的顶尖人才.消耗了开发人员成千上万的工时,但从未给客户交付任何有价值的东西,最终由于其复杂性和负担而轰然倒塌. 这些庞大的项目倾向于遵循大型传统的瀑布开发方法,坚持在项目开始时界定应用的所有需求和设计.这