分布式定时任务框架Dkron使用指南

dkron是使用golang写的一个分布式定时任务框架,作者称是因为受到google的Reliable Cron across the Planet的启发写的;

特性:

  1. 部署简单,一个binary就能启动
  2. 支持不同类型的后端存储
  3. 支持metrics
  4. 有一个前端页面可以显示任务的状态信息
  5. 有api可以方便的对任务做CRUD
  6. 支持不同类型的executor
  7. 支持任务串联(dag)
  8. 可以进行并发控制
  9. 支持集群

遇到的问题:

  1. 前端页面的jobs不支持分页,找起来很麻烦
  2. api的错误提示不友好,需要不断尝试
  3. 并发控制只能控制集群中单个点的执行的并发,无法全局控制
  4. 集群没有保证任务只执行一次,有几个节点就会执行多少次
  5. 在频繁对任务进行CUD的情况下,调度会miss很多任务的执行,因为其内部在不断的scheduler restart

其中我们主要的使用方式为定期对任务进行调度

  1. 定时进行模型训练
  2. 定时进行异常检测
  3. 支持手动触发模型训练和异常检测:在时间还没有到的时候,人为手工触发训练和检测

在使用的过程中,有用户频繁手工触发训练和检测,导致那个时间段内出现任务无法正常调度的情况,短则十几分钟,长则一个小时,影响线上服务稳定性;

线上的运行图

通过读代码,了解了dkron的整体架构

dkron架构图

以上是一个dkron架构的简略图,它有如下一些设计要点

  1. 每个Agent就是一个运行的实例
  2. 每个Agent大概包括三个组成部分
    1. store是用来和数据库交互的模块
    2. serf是用来在实例直接传递消息的,主要是分发定时任务到节点上
    3. sched是调度器,用来驱动定时任务的执行,一旦时间点到了之后就通过serf发送执行任务的消息到集群中
  3. 每个Agent可以标记自己是否为server,只有sever才会运行sched调度器,纯agent只被动接收server发过来的广播信息,来执行任务
  4. 集群的选主是通过etcd的分布式锁实现的

添加任务/修改任务/删除任务的过程

  1. 首先修改任务到store,落盘
  2. 重启scheduler;重启过程会全量从store中拉取所有的任务,一次性加载到scheduler内存中

基于以上设计,每次的对任务进行CUD都会造成重启scheduler,当一段时间密集的进行任务的CUD的时候,会造成scheduler一直在重启,没有时间执行任务;

这么设计好处在于可以保证scheduler内存中的数据始终保持和store中的一致,减少管理的复杂度;

打个比喻来说,这种设计方式可以理解为飞机开着的时候不允许上下乘客,必须降落才能上下乘客,我们遇到的问题是乘客不断上下飞机,导致飞机刚起飞就必须降落来拉乘客,所以飞机一直飞不远,为了解决这个问题,我们需要让飞机支持一边飞还可以上下乘客;

具体方式就是在添加任务/修改任务/删除任务的时候一方面修改store中的值,一方面修改scheduler内存中的任务,从而达到目的;

线上的运行图

经过一段时间的运行,可以看到scheduler次数大大减少,再也没有出现过任务无法调度的情况。