分布式任务系统cronsun

苏创绩 · · 3440 次点击 · · 开始浏览    
这是一个创建于 的文章,其中的信息可能已经有所发展或是发生改变。

一、任务系统


图 1

图 2

说任务系统的话大家想起的就是要执行任务了,什么时间什么地点做什么事情。图 1 是简单的任务,在每天 8 点输出 Hello Gophers!,大家用的比较多的任务系统就是 crond,其实这个系统(图 2)也很简单,就是守护进程加一个任务列表。


|早期的 cron

说一下早期的 cron,早期的时候(大概在80 年代在 Unix 系统里面 cron 运行逻辑

1. /usr/lib/crontab 文件

2.如果有命令要在当前时间执行,就用 root 用去执行命令

3.每秒钟去运行一次,有就执行,没有就重复,比较简单


|支持多用户的 cron

-Unix System V,1983

1. 启动的时候读取所有用户下的 .crontab 文件

2. 计算出每个 crontab 文件里需要执行的命令的下一次执行时间

3. 把这些命令按下一次执行时间排序后放入队列里

4. 进入主循环

  • 计算队列里第一个任务的执行时间与当前的时间差 

  • Sleep 直到第一个任务执行时间

  • 后台执行任务

  • 计算这个任务的下一次执行时间,放回队列,排序

后面发展成支持多用户的,这时候的执行算法改了。这相当于按照时间顺序排列,谁先执行的就放在那里,计算现在的时间与下个时间间隔,再让程序暂停,需要时唤醒程序执行,然后重复。


|近代的 cron

Linux,1991

1. Vixiecron(PaulVixie1987)

2. Version3Vixiecron(1993) 

3. Version4.1ISCCron(2004) 

4. anacron,dcron,fcron


现在的话 90 年代,大多数的都是用 4.0 的版本。大家用了这么久,基本都是在一些单机上跑一些任务,也是这样的操作。功能很简单,一个时间的规则,一个命令,如果公司有很多服务器的话,维护起来就不方便了,必须得一台一台去管理的或者是用脚本管理,多机器的情况下任务维护成本较高。

而且如果单机的话,如果失败了之后怎么办?就无法继续了,如果能做成分布式的就好了。


二、分布式任务系统


|分布式任务系统特点

  • 分布性

  • 对等性

  • 并发性

  • 缺乏全局时钟 

  • 故障总是会发生

分布式系统有什么特点呢?大概的特点就不延伸了,大家可以理解为把一个简单的软件放在多台机器上,然后多台机器是都可以完成这个任务的。那么分布式的 crond 的问题,怎么解决呢?

图 3

第一个就是把守护进程做成多个分布式的进程(图 3),把命令分布在数据库里面,这是最简单的实践方式。


|市面上的一些任务系统

  • Azkaban 

  • Chronos 

  • Airflow 

  • dkron

  • swoole-crontab 

  • Saturn

现在我们也想用一个这样的东西,一开始的时候找了市面上的方案,如上述现在市面上有很多方案,国内国外都有,最后一个是唯品会弄的,我挑几个讲一下。

1.Azkaban 是一个 Hadoop 上的任务管理,提供功能清晰,简单易用的 Web UI 界面,也提供 job 配置文件快速建立任务和任务之间的依赖关系等等好处,但只能对 Hadoop 做任务管理,符合我们的要求,我们的要求是替换单机版的 crond

2.Chronos 适合在容器编排管理工具上的调动,里面全部是替代了 crond,里面有 UI,也有灵活的调度,但是要依赖于容器,对于我们来说应该还有更好的方案。

3.Dkron 这个就比较接近我想要的东西,比较简单高可用,也可控。但是不好的就是对任务的支持比较简单,而且都跑在容器上,相当于替换成容器。因为我们的机器很多都是本地任务需要无缝替换 crond,因此也不考虑。


|需求

对很多任务系统进行考察之后,我们有一下需求:

  • 可替换 crond

  • 分布式、高可用

  • 支持多种任务属性

  • 易用

  • 易部署

因此,我们开发了 cronsun。


三、cronsun


图 4

图 4 是 cronsun 的系统架构,简单来说就是把任务存储在一个分布式 etcd 里,单个 crond 部署成一个服务,也就是node.1\2\3等等部署下去,再由 web 界面去管理。


|cronsun 特性

  • 部署简单

  • Web界面统一管理任务

  • 任务失败重试

  • 任务失败邮件提醒

  • 多机单任务(防止单机挂掉任务不按时执行)

  • 单机任务并行数限制

  • 执行单次任务

  • 多机器严格的时间间隔任务

  • 支持安全性配置,可以限制任务脚本的后缀和执行用户

  • ....

现在看一下 cronsun 的特性。首先第一点就是部署简单,只需要在每个机器上部署 crond 节点再部署一个界面管理就可以了。如果 cronweb 挂掉了,只是这个界面不能对任务进行更新,但是不影响任务节点运行,也就不影响任务的执行。其他的任务属性也讲了,最主要的是对任务有几种任务类型,就是可以在单台机器替换 crond ,也解决单点的问题。比如说某个机器死了另外一台机器就会执行这个任务,保证任务执行。


|cronsun 主要组件

图 5

cronsun 主要有 3 个主件,都是通过 etcd 通讯的。cronnode 负责节点的分组及节点的状态,croeweb 是管理任务的、执行结果都可以在上面看。


|任务类型

  • 普通任务,和 crontab 中的任务一样

  • 单机单进程任务,普通的 crontab 任务是单机的,如果执行任务的机器出现问题,任务可能执行失败。 cronsun 提供此任务类型是保证有多台机器可以执行一个任务,但在执行任务被执行时,只有一台机器在执行任务

  • 一个任务执行间隔内允许执行一次,这个类型的任务和单机单进程任务类似,但限制更严格。因为多台机器间,时间可能会不一致,而某些任务要求执行的时间间隔要严格一致时,可以考虑采取这种类型

刚才说的任务就是这 3 种类型,单机单进程是只想在一台机器上执行,有 n 个节点,但是只会在一台机器上执行结果。此外还有一种情况,假如有3台机器,每台机器的时间不一致的话,这种任务保证在一个时间间隔内,不管机器时间差怎样,保证只执行一次,不重复执行,解决机器时间不同步的情况。


|任务属性

  • 失败重试

  • 超时设置

  • 安全设置

  • 同时执行任务数设置 

  • 分组设置

任务的属性也大概说一下,如果随便填任务的话,web 任务界面暴露出去人家随便填几个任务就可以攻击你,安全设置只要加了脚本的后缀(就是本机器有的脚本),假如你的机器是安全的,那么整个系统就是安全的。还有同时执行任务数量设置,还有分组设置便于方便管理的。


|任务定时器

图 6

定时器,跟 crontab 是一样的,只是多了一个秒级。crontab 便于集中管理,多台服务器的时候就比较方便,保证了不会单点失败。还有就是简单的任务调度,web 是通过 API 来调度的,如果想使用的话稍微看一下 API 就可以调用了,整个系统大概就是这样。


四、心得体会

说一个开发过程中的体会。

第一个是方案的选择,方案选择一开始的时候基本都有这样的问题,你到底要做什么东西,系统要搞很多东西,我想调度,我想备份,能达成多种任务的系统里面。另外一种就是我把我想要的东西某个点做的很好,基于我们的人力物力,而且我们做的足够好,至于后面想扩展的时候能够扩展就可以了。


|timer 的使用


图 7

如图 7 是一个典型的运用场景,我先跑一个任务,假如这个任务超时了就设置超时。在监听的过程中,有任务就执行,没任务就执行超时处理。


图 8

刚才的使用只是单次的话是没有问题的,但是在 for 循环里面会每次循环的话都会新建一个timer,这样的情况在要求性能比较高的场合,这是不可行的。有什么替代的方法呢?


图 9

图 10

熟悉的小伙伴应该就会想到直接做一个 tick(图 9 ),自动给出消息,但是这个代码有一些问题,例如上面的代码,这个代码假如 1 秒之后没有了就超时。有个执行(图 10),如果你在这里可以直接刷新的话,这个超时的时候不会刷新的。

图 11

如果想超时也能刷新的话就需要做一些改造(图 11)。改造后就会刷新它,来的时候就会刷新超时,处理完了第一个,再处理完第二个,这样就可以解决 timer up 的问题。


|goroutine 退出

图 12

第二个是 goroutine 的退出,大家可能有这种习惯就是每当起任务的时候都会起 goroutine ,这是不好的习惯。解决的办法是就是限定一定的 goroutine,限定 goroutine 的数量而防止流量暴涨的后续问题(图 12),这是比较好的方案。

大家可以看一下 goroutine 退出的机制,每起一个 goroutine ,就起一个监听,退出的时候就等待 goroutine 退出,再结束。

图 13

这里有个问题,这种方式只适合 goroutine 一跑就结束的形式,并不能通过信号去让 goroutine 的退出。如果要通过信号控制 goroutine 的退出怎么办呢?需要有一个等待的函数(图 13),就是退出接受信号的函数。


图 14

具体是怎么实现的呢?如图 14 函数监听退出的信号,goroutine 接到信号就直接退出,在外面的时候就等待,整个退出机制就很完善了。

图 15

图 16

但是以上情况是如果有一个信号退出的时候所有 goroutine 就会退出了,如果想实现链式的退出怎么办呢?就需要使用到信号传递了(图15)。可以这么做(图16),如图信号传递在第一个结束后再做传递,第二个监听完退出再传递,如此类推到所有退出完毕,就可以实现链式的有秩序退出。


|语言的选择

最后再来说一下为什么选择 Go 语言来做开发语言,总的来说可以总结为以下几点:

  • 简单、开发效率高

  • 部署简单,只需一个二进制文件,无依赖

  • 适合自己的才是最好的

一是我们看中它容易学习,开发效率高,运营也不错。两个人在工作之余,相当于工作的 20% 的时间,团队一个月就弄了一个版本出来,我们觉得还不错。

二是部署很简单,部署的时候就是二进制,很小没有依赖,服务器只要太老的系统部署十分简单轻便。

最后一个就是适合自己的才是最好的, Go 语言对我们公司来讲实现的成本是最低的,是最适合我们的,在我们的眼里是最好的



有疑问加站长微信联系(非本文作者)

本文来自:微信公众平台

感谢作者:苏创绩

查看原文:分布式任务系统cronsun

入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:701969077

3440 次点击  
加入收藏 微博
暂无回复
添加一条新回复 (您需要 登录 后才能回复 没有账号 ?)
  • 请尽量让自己的回复能够对别人有帮助
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`
  • 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
  • 图片支持拖拽、截图粘贴等方式上传