FLO集中调度系统
概述
系统取名为FLO, 这来自于一款之前比较流行的休闲小游戏<<美女餐厅>>中的女主角, 相信玩的好的玩家可以让flo有条不紊的分配各种餐厅服务任务,我们的寓意希望FLO可以帮助我们很好的去管理,监控各种调度任务.随着公司业务的快速发展, 支撑业务发展的技术框架也越来越复杂, 我们需要一些程序按照特定的规则自己执行一些逻辑.这就催生了越来越多的任务.如果从任务调用的方式上来划分,可以分为静态调度任务和动态调度任务.
静态调度任务
当一个程序在执行任务之前就可以确定是按照怎样的规则去执行, 并且有明确的Cron Expression来描述执行规则. 暂且称这部分任务为静态调度任务. 它可能存在如下业务场景中(只罗列部分实例):
由于分布式服务越来越多, 其中很多调用链中的某一个服务调用出错而引起的数据不一致问题, 这需要某个调度任务间隔固定的时间来处理这些不一致的数据.
检查某一个表中数据的待办状态.
定时更新缓存
这些静态调度任务, 我们通常的做法是通过quartz或者java自带的task, Thread Scheduled 来完成. 我们只需要在程序启动之前就设定好执行规则即可, 由于我们现在业务的开发历史原因, 大多数是静态调度任务, 所以本文主要针对静态调度任务来做概要设计.
动态调度任务
当某个业务逻辑在执行半小时之后需要去触发一个状态检查任务, 这是在具体的业务场景中去触发某一个调度任务, 这样的在时间上不具备确定性. 暂且称这种任务为动态调度任务. 像这种类型的调度任务, 可以在一些业务场景中减少我们轮询表中的数据状态, 这同时需要分布式MQ充当消息总线的角色.
系统架构
设计该系统的时候也是基于具体的业务场景, 尽量做到简单,通用. 为了做到部署,接入够简单, 我们尽量不用太多的中间件, 目前我们主要使用zookeeper帮助我们实现分布式锁, 心跳监控, 规则存储的需求. 当然日志收集系统是我们本身已有的产品, 可以直接接入使用. flo主要分为flo-admin, flo-client, flo-trigger三部分. 下图为flo主要结构.
Zookeeper中的数据结构
/flo/task-name(任务名字)/actors/actor-name(具体任务执行者)
/flo/task-name(任务名字)/triggers/trigger-name(具体的触发器)
存储在zookeeper中的数据节点actor-name为临时节点, trigger-name为临时顺序节点(利用临时有序节点实现分布式锁,避免多个trigger同时触发同一个任务)
flo-admin
flo-admin做为系统的管理监控中心, 可以在这上面看到所有调度服务的实时状态,执行历史,执行规则等.它主要实现如下核心功能:
监控所有具体任务执行者的心跳状态, 执行状态.
可以查询每个任务中的每一个执行者的执行历史.
根据Cron Expression灵活配置任务执行规则.
可以动态的暂停, 启动, 修改任务执行状态(如果某一个任务执行者正在执行具体的业务逻辑, 这种情形将不受控制, 需要该执行者执行完该次任务之后才能变更状态).
可以修改某个任务中执行者的权重.
注: flo-admin可以单节点部署, 不需要高可用, 这里只是一个管理监控系统, 他的可用性不会影响任务的正常调度
flo-trigger
flo-trigger作为具体任务的触发器, 它是否可以正常运转将直接影响任务的正常调度. 所以flo-trigger将会是集群部署, 它主要提供如下核心功能:
针对某一个任务flo-trigger集群中只能有一个trigger实例来触发该任务中某一个具体执行者来完成调度任务.
需要注册到zookeeper中, 并发送心跳数据.
可以详细记录每次触发时的调度日志, 任务执行者返回的结果数据.
可以实时的获取flo-admin中设置的具体的执行规则.
支持分组功能, 主要满足针对调度任务超过10万级别以后的横向拓展.
根据权重对调度任务的负载均衡
flo-client
flo-client是我们具体的任务执行者接入flo系统的唯一切入点, 因为会影响到具体的业务系统, 所以我们需要该client做到对业务系统最小的入侵, 目前我们只需要接入的业务系统引入flo-client的jar,并在需要作为任务执行者的class头上添加flo-client自定义的annotation. 我们通过这种方式暴露一个http服务, 该服务的功能就是直接触发添加标注的class. 它需要提供如下核心功能:
支持自定义标注, 支持xml配置, 支持直接通过new暴露http服务
发送实时心跳数据.
根据每次任务的执行情况返回response数据(包括状态,异常,执行开始时间,执行结束时间)
其他
这里还有需要描述的地方就是我们的日志收集系统, 因为这已经是开发完成的中间件, 所以我们会直接拿来使用, 每个任务具体的调用,都会输出特定格式的日志,收集起来, 统一由日志收集系统提供个性化的检索接口. 这可以让我们很方便的将每个任务的执行历史, 状态快速展示在flo-admin中. 因为有zookeeper临时节点的特性, 可以让我们知道有哪些trigger和client是存活并正常的. 为了降低我们利用Cron Expression去触发调度任务, 我们直接使用了quartz在trigger中来完成任务触发工作.如果后续针对动态调度任务的需求逐渐增加, 我们将会借助MQ(这里会选用Rabbitmq或则Kafka)来实现动态延迟调度任务的功能.
(转载本站文章请注明作者和出处 Panda)