一、SIA-TASK 的产生
1.1 背景
无论是互联网应用还是企业级应用,都充斥着大量的批处理任务,常常需要一些任务调度系统帮助我们解决问题。随着微服务化架构的逐步演进,单体架构逐渐演变为分布式、微服务架构。
在这样的背景下,很多之前的任务调度平台或组件已经不能满足业务系统的需求,于是出现了一些基于分布式的任务调度平台。这些平台各有其特点,但也各有不足之处,比如不支持任务编排、与业务高耦合、不支持跨平台等问题。
1.2 种类
按照任务与时间的关系,我们把批处理任务分成三类,飞机型、地铁型、公共汽车型。
飞机型是指每年/月/周/天固定某一时刻执行的任务。这种任务在我们的业务系统中非常常见,比如每天 1 点要执行一个跑批任务去清理前一天的日志;每月 10 号要给公司全员发工资,这些都属于飞机型任务。
地铁型是指每隔固定时间执行任务,不可并发。我们也经常遇到这样的批处理任务,第一个任务没有结束,第二个任务是不可以执行的,这就是不可并发。
公共汽车型是指每隔固定时间执行任务,可并发。如果是公共汽车型的任务,前一个任务没有结束,下一个任务也可以按点开始执行。
1.3 问题
在跑批任务的过程中会遇到以下问题:
遗忘,忘记了还在运行的定时任务。在我们公司发生过一个这样的案例,若干年前的一个冬天,我们的一个项目团队用 3 个月的时间做了一个项目,运行一段时间后发现项目的效果并不是很理想,便将相关的程序都停掉了,却忘了有一个跑批任务的节点还在继续运行,直到两年后,这个节点产生的日志把磁盘填满了,触发了监控报警,我们才发现。
单点,就是没有热备,跑批任务是一个单点运行的定时任务,出了故障需要转入手工处理。
依赖,利用时间差来处理依赖反复造成问题数据。大家知道项目有的时候是需要有依赖关系的。比如某个项目的跑批流程 A 和跑批流程 B 存在先后次序,项目组设置跑批流程 A 在凌晨 2 点运行,跑批流程 B 在凌晨 4 点运行,从时间上保证先后次序,万一跑批流程 A 执行时间过长,超过 2 小时,就会导致数据出现问题,需要手工处理出现问题的数据。
1.4 关系
前文提到任务之间是有关系的,那到底存在哪些关系呢?我认为主要有以下 3 种:
串行,存在先后关系的两个任务。即任务 B 在任务 A 后执行,要先执行任务 A 之后再执行任务 B。
并行,可以并发执行的两个任务。比如任务 B 和 C 都要在任务 A 之后执行,而任务 A 执行完成后,任务 B 和 C 可以同时执行,那 B 和 C 就是并行关系。
分支,根据前置任务的返回结果进行判断,不同的结果执行不同的后续任务。比如返回 0 的时候,执行任务 A,返回 1 的时候执行任务 B,这是一种分支的情况。
1.5 思考
基于上述的几种关系,我们在建设任务调度平台的时候会思考以下两个方面:
平台化。项目团队总是希望把更多的精力投入到业务开发中,希望把其它与业务开发无关的事情尽可能地放到架构团队。他们希望有一个执行任务的平台,仅仅需要把编写好的业务逻辑放到这个平台就可以了,这个平台会完成所有的工作,项目组只需要关心业务逻辑。
微服务。为了更好地满足项目的需求,我们希望能把任务的业务逻辑和任务的编排调度区隔开来,采用注册和发现机制来建设任务调度平台,与业务相关的部分交给项目团队处理,把其他的部分交给任务平台来处理。
1.6 因素
除了上述两个方面的考虑以外,我们还需要思考以下八个因素。
任务编排。多个业务之间的定时任务存在流程次序,前面提到任务之间有并行的关系、有串行的关系,还有分支的关系,我们希望平台能有相应的编排功能去处理和支持这些任务。
任务分片。对于一个大型任务,需要分片并行执行。
跨平台。除了使用 Java 技术栈(SpringBoot、Spring 等)的项目之外,还要能够支持使用其他语言的应用。
无侵入。业务不希望与调度高耦合,只关注业务的执行逻辑,希望平台对业务本身代码是无侵入的,将影响降到最低。
高可用/故障转移。调度系统自身必须保证高可用,不能有单点,任务执行过程中遇到问题有补偿措施,能够平滑处理,减少人工介入。
可视化。任务调度的操作提供可视化页面,方便使用。
实时监控。平台要有实时监控系统,实时获取任务的执行状态。
动态编辑。业务的任务时钟参数可能变动,在可视化的基础上,对所有任务执行的操作都实时反映到业务系统中去,不需要停机部署。
基于以上的背景与考虑,我们建设了微服务任务调度平台 SIA-Task。
二、SIA-TASK 的核心设计思想
2.1 简介
SIA 是“Simple is Awesome”的简称。
SIA-TASK(微服务任务调度平台)是其中的一项重要产品,SIA-Task 契合当前微服务架构模式,具有跨平台、可编排、高可用、无侵入、一致性、异步并行、动态扩展、实时监控等特点。
SIA-TASK 是任务调度的一体式解决方案,对任务进行元数据采集,然后进行任务可视化编排,最终进行任务调度,并且对任务采取全流程监控,简单易用。对业务完全无侵入,通过简单灵活的配置即可生成符合预期的任务调度模型。
SIA-TASK 借鉴微服务的设计思想,获取分布在每个任务执行器上的任务元数据,上传到任务注册中心。利用在线方式进行任务编排,可动态修改任务时钟,采用 HTTP 作为任务调度协议,统一使用 JSON 数据格式,由调度中心进行时钟解析,执行任务流程,进行任务通知。
2.2 术语
简单介绍一下 SIA-TASK 的术语。
任务(Task): 基本执行单元,执行器对外暴露的一个 HTTP 调用接口;
作业(Job): 由一个或者多个存在相互逻辑关系(串行/并行)的任务组成,任务调度中心调度的最小单位;
计划(Plan): 由若干个顺序执行的作业组成,每个作业都有自己的执行周期,计划没有执行周期;
任务调度中心(Scheduler): 根据每个的作业的执行周期进行调度,即按照计划、作业、任务的逻辑进行 HTTP 请求,它是一个单独的节点;
任务编排中心(Config): 编排中心使用任务来创建计划和作业;
任务执行器(Executer): 接收 HTTP 请求进行业务逻辑的执行;
Hunter:Spring 项目扩展包,负责执行器中的任务抓取,上传注册中心,业务可依赖该组件进行 Task 编写。
Job、Task、Plan 的关系
Task 是业务执行的基本单元,执行器对外暴露的一个 HTTP 调用接口。若干个 Task 构成一个 Job,而 Plan 是由若干个顺序执行的 Job 构成。
为什么这里需要一个 Plan?有的时候两个任务不光有顺序关系(就是 A 任务执行完之后再执行 B 任务),还需要满足一定的时间要求,比如上午 10 点执行任务 A,下午 2 点执行任务 B,而且必须保证上午 10 点任务 A 按时执行完成。
打个比方,今晚 8 点有一场足球比赛的直播,如果晚上 8 点我还不能到家,那我就没办法看直播,而如果今天我下班早,下午 6 点多就到家,也必须等到 8 点才能开始看球赛,这就是 Plan 计划的来源。
2.3 组成
SIA-TASK 任务调度平台由以下几个部分组成:
任务执行器,就是你的业务代码在哪里,这是属于项目组的。
任务注册中心,我们用的是 ZooKeeper。
任务编排中心
持久存储,我们用的是 MySQL。
任务调度中心
2.4 运行
接下来详细介绍 SIA-TASK 的运行逻辑。
首先,通过注解抓取任务执行器中的任务上报到任务注册中心。任务执行器在启动的时候,会有一个叫 online Task 的注解,只要把这个注解放到 control 代码的方法上,就会自动把 HTTP 接口抓取出来,然后上报到任务注册中心,这里我们用的是 ZooKeeper。
任务编排中心从任务注册中心获取数据进行编排保存入持久化存储。也就是说,相当于在执行器里,把业务调用 HTTP 接口请求的 URL 地址、端口等实例抓取出来上传到 ZooKeeper 里,ZooKeeper 就拿到了一个个的任务,ZooKeeper 会把任务本身的信息抓取出来放到 MySQL 里。
这里要区别一下什么是任务,什么是任务实例。任务实例和任务的关系,有点像类和对象的关系,就是一份业务逻辑代码可能部署在多个节点上,也就是说这些节点的业务逻辑代码是一模一样的,在运行阶段抓取的时候会把每个节点上业务逻辑代码都抓取上来,针对这个业务它就是一个任务,但是每一个端口、每个 IP 地址对应的可能就是一个任务实例。比如高可用热备时,我们会把任务本身的信息经过处理之后保存到持久存储里,而实例本身的信息只会停留在 ZooKeeper 里。
任务配置中心可以根据 ZooKeeper 里的信息和 MySQL 里的信息进行配置,就是根据抓取的任务,给这些 Task 加时钟、策略,然后编排出 Job 和 Plan,并把现在的这些信息保存到 MySQL 里。
任务调度中心从持久化存储获取调度信息,知道编排的 Job、Plan、时钟、策略等逻辑,任务调度中心按照调度逻辑访问任务执行器,对这些从执行器上抓取来的 Task 进行调度。
这就是 SIA-TASK 的运行逻辑,同时我们会把调度日志存到 Kafka 里。
2.5 特性
1)基于注解自动抓取任务
在暴露成 HTTP 服务的方法上加入 @OnlineTask 注解,@OnlineTask 会自动抓取方法所在的 IP 地址、端口、请求路径、请求方法、请求参数格式等信息上传到任务注册中心(zookeeper),并同步把任务信息写入持久化存储中。
2)基于注解无侵入多线程控制
单一任务实例必须保持单线程运行,任务调度框架自动拦截 @OnlineTask 注解进行单线程运行控制,保持在一个任务运行时不会被再次调度。而且整个控制过程对开发者完全无感知。就是在一个任务实例上,要保证任务在运行的时候是单线程状态。其实这是由用户自己控制的,如果需要是单线程的,这里可以加以控制;如果需要是多线程的,可以不加控制。这个控制并不需要另加代码,只需要在注解上去处理。
3)高度灵活任务编排模式
SIA-TASK 的设计思想是以任务为原子,把多个任务按照执行的关系组合起来形成一个作业(Job)。同时运行时分为任务调度中心和任务编排中心,使得作业的调度和作业的编排分隔开来,互不影响。在我们需要调整作业的流程时,只需要在编排中心进行处理即可。同时编排中心支持任务按照串行、并行、分支等方式组织关系。在相同任务不同任务实例时,也支持多种调度方式进行处理,而且整个的处理编排都是在页面上完成的,这个功能非常好用,这也是 SIA-TASK 平台的一个亮点。
4)调度器自适应任务分配
任务执行过程中出现失败、异常时,可以根据任务定制的策略进行多点重新唤醒任务,保证任务的不间断执行。我们设定了很多策略,比如某个 Task 出现问题了怎么办?是再唤醒一次?还是不管了?还是人工干预发警报?我们定制了很多策略去处理这些问题。
2.6 关键点
了解了平台特性,我们来梳理 SIA-TASK 的技术关键点。
任务流。实现任务与任务之间可配置的流向关系,形成有向无环图(DAG)。任务流可由定时时间(Cron 表达式)或外部请求(提供 API 地址) 开始,根据 DAG 逻辑执行。
元数据管理。微服务中各个任务元数据的管理同步数据抓取、录入。
智能运维。可视化的任务实时监控,所有监控都是有页面可以看到的;实时预警机制,出现问题的时候,会发送邮件或短信给相关人员告警;半智能化的自主修复,嗅探重试,不需要人工干预。
资源隔离。进程间的资源隔离;进程内的资源隔离,提高系统吞吐,提供稳定性。时钟用的是 Core Schedule,一个调度中心对一个项目组用一个 Core Schedule,每个项目组在同一个调度的时候,同一个调度器上都是隔离的,一个项目组出问题,不会影响到其他的项目组,这就相当于代表了隔离性负载均衡。
负载均衡。调度中心调度任务的时候,任务的执行周期时间不一样,可能有的任务需要的时间长一点,有的任务需要的时间短一点,调度器的资源也不太一样,有的 CPU 高一点,有的 CPU 低一点,那如何保证调度负载均衡?如何保证资源隔离的负载均衡?我们会根据这种任务调度的历史值(任务耗时)以及机器本身性能的值进行考量,使每一个任务调度中心拥有的调度数量差不多、消耗也差不多。这是一种新的负载,而不是简单的流量负载。
三、SIA-TASK 组成模块
3.1 首页
任务调度管理首页主要包括三部分:调度器信息、调度次数、对接项目详情。
调度器信息:调度中心调度器的数量。
调度次数:调度中心调度 Job 的历史累计总数。
对接项目详情:调度中心对接的项目组总数,Job 总数。
目前 SIA-Task 平台上已经接入了 51 个项目,上面跑的 Job 数有 600 多个,今年上线的版本,Job 已经跑了 3000 多万次。
调度器上有几个值需要了解一下,每台调度器都有三个指标。
Job 上限值:所能负载的 Job 动态阈值;
Job 运行数量:该调度器当前运行的 Job 数量;
Job 预警值:当调度器运行的 Job 数超过预警值时,会发邮件通知管理员。
3.2 调度器管理
关于调度器有几个 信息需要了解,如图所示,点击某个调度器(柱状图),会显示该调度器所抢占的 Job 详情列表:
JobKey:所配置的 Job 名称,每个 Job 都有自己的名字。
类型:配置 Job 的定时任务类型,分为 Cron 与 fixRate 两类。
Job 类型值:如果是 Cron 表达式,6 位时间戳怎么写;如果是 fixRate,那就是需要间隔多少时间。
预警邮箱:该 Job 配置的预警邮箱。
描述信息:描述该 Job 的功能信息,便于管理员能够迅速发现某台调度器所抢占的 Job 详情。
调度器包括工作调度器、下线调度器、离线调度器、白名单。
工作调度器:这类调度器具有抢占和调度 Job 的能力。对某调度器进行下线操作,它会立即失去抢占 Job 的能力,已经抢占的 Job 执行完毕后会自动释放,进而被其他调度器抢占,调度器下线后会进入下线调度器列表中;工作调度器列表提供下线以及批量下线的功能。简单来说,工作调度器就是正在工作中的调度器。
下线调度器:这类调度器进程仍然存活,但失去了抢占 Job 与参与调度的能力。对这类调度器执行上线操作,会进入工作调度器列表,且开始具有抢占和调度 Job 的能力;下线调度器列表提供上线及批量上线的功能。就是说,下线调度器依然活着,只是不再参与抢占 Job,之前已经有的 Job 还是会继续执行完成,如果点击上线就重新具备抢占 Job 的能力,变成工作调度器。
离线调度器:这类调度器进程不再存活,当下线调度器进程死亡后,会自动进入离线调度器列表,这类调度器进程重新启动后,会自动进入下线调度器列表;离线调度器列表也提供删除及批量删除的功能。离线调度器一般都是出现问题了,可能是进程挂掉了,也可能是网络故障了。
白名单:将某个 IP 加入白名单之后,它具有调用所有执行器实例的权限;白名单列表提供批量删除的功能,删除该 IP 后自动失去该权限。
3.3 调度监控
上图所示是 SIA-TASK 的调度监控页面,分着的一块一块区域属于不同项目组。目前 SIA-Task 接入了 51 个项目,准备中的有 500 多个,正在运行的有 25 个。
有的 Job 执行非常快,几秒钟就执行完了,有的 Job 执行非常慢,需要很长的时间,我们在状态抓取的时候,只能抓取到时间长的 Job,这些被抓取的 Job 显示为正在运行,而时间短的捕捉不到,但它们都处于执行状态,这些没有被抓取到的 Job 就显示为准备中。
可能有的 Job 这段时间不需要运行,可以手动停止,剩下的就是异常停止的 Job,需要发送邮件告警。
我们也提供了检索的能力,可以接受不同项目组登录查询自己的项目运行状态。
3.4 Task 管理
Task 管理界面中,Task 按项目组分组显示,主要提供 Task 的配置、修改与删除等功能。
Task 包含两部分:
一部分 Task 使用了 sia-Task-hunter 组件,通过标准注解实现 Task 的自动抓取,这类 Task 不允许修改;
另外一部分 Task 是由用户手动添加的,我知道访问的 URL 和 HTTP 地址,手动添加进来,这部分 Task 支持跨平台的抓取,而且可以修改和删除。
一个 Task 管理包含以下几个部分内容:项目名称、应用名称、任务名称、机器地址、描述、以及查看/修改/连通性测试等操作。同一个 Task 名称,不同的机器地址,代表一个任务和不同的任务实例。
3.5 Job 管理
前面介绍了一个 Job 由若干个 Task 组成,图中每一个不同的列代表项目名称,点击下拉列表可以显示所有的项目,可以进行过滤、添加、状态查看等操作。
其中状态操作可以手工执行,可以停止或激活 Job,Job 配置好之后属于未激活的状态,需要激活一下。还可以修改 Job 里的信息,配置 Job 等。如何添加 Job?假如我要添加一个 Cron 表达式类型的 Job,需要添加哪些内容呢?
因为 Job 是 Cron 表达式类型的,首先我需要输入六位表达式内容,还要添加一个预警邮箱,再描述这个 Job,每个 Job 都有一个 key,最后还需要添加 Job_key。这样一个新的 Job 就添加好了。
回过头来看,添加 Job 需要配置 Task 信息,这是一个比较复杂的过程。一个 Job 由若干个 Task 组成,我们可以用拖拉拽的方式根据 Task 之间的关系确定形成组成 Job 的所有 Task 的顺序关系。还可以以不同颜色代表不同项目进行区分,当然只有管理员才有权限看到所有项目,各个项目的负责人只能看到自己所属项目的状态。
上传 Task 的时候会带一些参数,所以还涉及到参数的处理,比如参数类型、参数值、过期时间等。重点聊聊过期时间。
通过 HTTP 方式调用会遇到一个问题:到底 Task 什么时间会执行完成。为解决这个问题,就需要设一个 Task 的过期时间,只要过期时间一到,就会转入其他策略,比如放弃或人工处理等。因为作为异步调用,不可能无休止地等待客户端返回结果。
当然也可能存在一种情况:我得到的结果是超时了,实际上任务是在正确执行,而且再过一段时间给我返回结果了。我们曾经设计了一种队列补偿机制来处理这个问题,但是好像意义不大。当然,这只是一种可能,平台上线至今没有出现过。
目前平台的 Task_选取实例策略包括两种:
随机,从可选的列表中,随机选择实例,即 IP+端口;
固定 IP,指定实例,随后需要从可选列表中人工指定实例。
平台支持四种 Task_调用失败策略:
STOP,停止策略,调用失败则整个 Job 停止,不再执行后续 Task;
IGNORE,忽略策略,调用失败则跳过该 Task,继续执行后续 Task;
TRANSFER,转移策略,选取该 Task 的其他实例执行,如果依然失败,则使用停止策略;
MULTI_CALLS_TRANSFER,多次调用再转移策略,重复调用该 Task 多次,如果依然失败,则使用转移策略。
3.6 调度日志
日志管理提供了 Job 的运行日志相关信息,按项目组分组显示,一条 Job 日志的关键元素包含:
执行状态:表示该 Job 执行结果;
执行时间:表示调度器调度 Job 的时间;
执行完成时间:表示 Job 执行完成的时间;
调度信息:表示执行 Job 的调度器实例;
执行信息:Job 执行的具体信息,并且已实现 Job 与所引用的 Task 的执行日志信息的关联,日志默认保存七天。
四、开源
SIA-TASK 作为 SIA 团队的一个重要产品,在公司接入了数十个项目,运行着数百个 Job,经受住了稳定性的考验。
SIA-TASK 微服务调度平台于 5 月已经开源,开源地址:https://github.com/siaorg/sia-Task,感兴趣的同学可以登录查看详细介绍。
开源项目:
微服务任务调度框架 :https://github.com/siaorg/sia-task
微服务路由网关 :https://github.com/siaorg/sia-gateway
作者介绍:
梁鑫:宜信高级架构师 &开发平台负责人
本文转载自公众号宜信技术学院(ID:CE_TECH)。
原文链接:
https://mp.weixin.qq.com/s/qGpr3lXOHiONt4r4nGp9yQ
评论