一个Batch作业调度系统构思
最近在构思一个作业调度系统(Job scheduling system), 其实作业调度系统也不是什么新东西, 很多大学的超级计算中心和气象中心都有这种系统, 最典型的开源软件架构是Torque加Maui, 这两个软件都是由Cluster Resources Inc维护的开源软件, 在维基百科上列出了一堆软件(http://en.wikipedia.org/wiki/Category:Job_scheduling).
我所考虑的是作业调度系统, 不是面向科学计算, 而是针对金融行业日结月结作业或者DW/BI系统的refresh, 这类作业调度系统和气象中心的作业调度系统有很大的不同. 具体不同之处, 我就不列了. 这类调度系统的特点是, 作业数量很多, 主要是来回导数据和运行stored procedure. 在我看来, 这些操作, 由大到小可以分为3个层次, Cycle->Batch->Job. 先给出我自己对这3个概念的定义.
Cycle: 一个完整的刷新过程, 比如EOD(日结) refresh, 就可以算作一个cycle
Batch: 每次单独被调起的刷新过程段, 就是一个batch. 一个Cycle可以包含多个batch, 比如CRM batch和ERP batch.
Job: 一个不能再分解的作业, 比如一个ETL Job.
构思中的系统为Batch级的调度系统, 它将不涉及Cycle级别的管理, 因为, 在我看来Cycle级的管理, 多是来维护Batch之间的依赖关系, 通常一个企业的Batch不会很多, 所以维护起来并不复杂, 可以写一个简单的shell, 或者用一些可视化的control flow工具完成, 比如微软SSIS的 control flow, IBM DataStage(以下有时会简称为ds) 的Job Sequence, 或者CA的Autosys来管理. 而Batch内部的调度要比这个复杂得多了去, 需要一个专门的系统来管理.
这个Batch级的调度系统, 具有如下特性:
1. 提供水平扩展能力(即可以动态增加或减少工作节点Node)
2. 提供Load Balance特性(*考虑到LB算法的复杂性, 可先采用一个高效但不准确的算法)
3. 如果某个节点出现故障, 作业将自动转移到其他节点上运行.
4. 提供出错重试机制(能做到一定程度的High Availability)
5. 超时告警机制
6. Job调度设置的版本管理功能(这个我认为很重要, 如果Job调度调整错了, 作为应急, 可以很容易恢复到以前的版本. )
想法由来:
金融行业日结月结的数据量很大, 花费时间一般都很长. 而在DW/BI系统中, 留给DW/BI的刷新窗口往往也不会很大, 同时业务对于DW/BI系统的实时性要求也越来越高了, 除了传统的EOD batch, 有的还需要mini batch和micro batch. 这时候一个高效可靠的调度就显得尤为重要了. 另外,在一些大公司中, 往往会使用多种数据库(SQL Server, Oracle, MySQL), 多种ETL工具(比如SQL Server SSIS,Datastage). 这些都给batch调度带来了不大不小的困难.
触发这个构思的最直接的原因, 公司采用了国内一个系统服务商开发的调度系统, 这个系统不太稳定, 而且使用超复杂. 下面是我公司的情况, 故事挺长的, 建议读者略过.
###############
公司以前的DW/BI系统调度是通过Shell脚本来实现的
(简单讲, 就是用Shell来调用datastage的dsjob命令
或者sqlplus), 随着job越来越多, batch的时间越来
越长, 都快要突破留给DW/BI的时间窗口了. 缩短
batch一个很简单的方法是,再增加一台datastage服务
器, 这样2台Datastage并行运行, 应该会快一些,
同时一台datastage server宕掉后, 如果能将所有
ds job切换到另一台, 也算做到了高可用性(HA),
这些shell开发的调度程序难以支持这种需求. 为
此, 公司引进了一国内系统服务商自己研发的产品,
这里就不点名了, 如果有兴趣的话, google
搜 " 企业ETL服务管理平台软件 ", 该产品号称支持:
* 动态负载均衡(Load Balance)
* 拥有横向扩展能力(High Scalability)
* 可以进行集群管理
经过一段时间的使用, 发现这个软件真不咋样,
1. 用户体验不友好, 不是一般的不友好.
2. 系统稳定性不行, 有几次无法被自动调起
(这有点讽刺意味, 宣称提供HA特性的系统,
本身却不够稳定)
3. 该软件是通过不断扫描(或监听)外部状态,
来启动batch. 而没有任何机制, 来实现
batch暂停, batch终止.
4. 该软件的维护起来真困难, 需要多一套配置
(曰为base配置),
5. 该软件权限管理比较别扭, 这将直接影响到
调度作业修改的流程控制
另外, 更深一些, 它是假集群, 在某些情形下,
会造成很严重的后果, 简单列几条:
1. 比如在生产环境中, 可以部署2个该软件服务
端, 同时只有一个服务端是主服务端(master),
另一个是standby, 这些服务端通过tcp通讯
来完成心跳测试的. 很明显, 这种架构,
存在假死现象. 即master端和standby端之间,
如果出现网络异常情况, 这个standby服务端就
会切换成主服务端, 这时候, 整个环境会有2个
主服务端, 不敢想象这种情况下调度会是什么
样子, 估计调度会乱作一团的. (建议该厂家
研究一下真正cluster软件是如果测心跳的,
比如SQL Server Cluster或Oracle RAC,
其实安装一下这些系统, 就能猜出一二)
2. 再比如, 开始时由服务端A作为主服务端, 调起
了Job_1, 未等Job_1结束, 服务端A宕掉, 服务
端B被切换成主服务端, 这时, Job_1就是一个
孤儿job, 该Job有可能正常运行结束, 也有可
能运行失败. 一旦出现了孤儿job, 该软件采用
的是简单重调方式, 它会再次调起Job_1. 如果
Job_1是不可重调的, 这可能会引起严重的问题.
3. 另外, 这种宣称的集群, 其实不应该算作集群.
我认为一个集群, 应该是作为一个整体向用户
提供计算能力, 从用户角度看, 集群就像是一个
单一的计算机. 而该软件在生产环境中, 可以部
署多个服务端, 却又不能提供一个统一的访问
渠道来和这几个服务端通讯.
###############
Job调度模型
本调度框架构思来自于高速公路收费口排队. 姑且叫做Batch调度之高速公路收费口调度模型. 在讲述模型之前, 先做一些假设:
假设1: 有2条京沪高速公路, 不是2车道, 是复线, 比如路R1和路R2, 分别有3个收费口和4个收费口, 也就是说这两条路的处理能力不一样. (构建一个多节点的情形, R1节点同时能处理3个Job, 而R2同时能处理4个Job)
假设2: 通向R1收费口的路(gateway), 宽为5米, R1的所有收费口都共用这5米的入口; 通向R2收费口的路宽为6米, (每个节点的处理能力不同)
假设3: 有些收费口只允许小汽车通过, 有的允许大客车和大货车通过, 有的不限制汽车的类型. (比如SSIS Job只能在windows节点上运行, 而不能再linux上运行)
情景模拟一下, 如果你是一个司机的话, 你是如何选择收费口呢? 你需要看看你开的是什么车, 哪些收费口允许你的车通过; 如果R1和R2上还有其他车在交费,它们将占用一定的路宽, 你需要再看看你的车宽度, 看看能不能转钻过去到达收费口. 如果你没有喝醉酒, 你应该会选择一个合适的收费口.
现在春节刚过, 相信不少朋友又一次领略了铁老大的威武. 在我记忆中, 最气愤的一次购票经历是, 有一次在北京转车, 火车站广场露天排队, 好几个长龙, 我就跟在一个队伍的后边排着, 大概排了两三个小时, 好不容易快到窗口了, 队伍前面一阵骚乱, 原来这个窗口卖票员要吃午饭, 窗口就这样关啦. 我和我后面的很多人算是白排了.
构思中的调度系统, 并没有窗口排队概念, 而是采用即时任务分派策略(Just-in-time dispatch), 这样做将比窗口排队方式好很多. 如果你采用排队方式, 当某个节点宕掉, 你需要考虑排这个节点上的任务该如何分配到其他节点上, 要做到公平, 算法恐怕会有点复杂.
软件结构
程序:
1. Batch级的命令行程序(执行启动/暂停/继续/终止操作,以及查询batch的所处状态)
2. 静态模型的版本控制工具
常驻进程:
1. Job分派进程
2. 一个类似cron的进程, 不断扫描event是否可以触发
3. 测超时的进程
外部需扩展
1. 超时算法由外部程序扩展, 推荐的算法是runtime>max(timeout_threshold,average_time*ratio)
2. 每次Batch启动时候, 可以一个XML字符串的形式给batch附加一些全局信息, 比如交易日期等等
3. 每个job, 在执行前调命令和执行命令以及执行后调命令, 都给命令附加额外的参数, 这些参数包括, job的所有静态和动态信息, 以及batch的信息.
操作说明
常见的batch驱动机制包括: 被动被call起来, 定时被call起来, 还有一种情形是, 满足某个条件后, Batch应该被call起来, (比如, 当FTP文件下载完毕). 目前本调度程序不仅支持第一种情形, 对于后面2种情形, 可以通过定时事件(cron_event)来完成. 定时启动, 就不说了, 对于对于FTP下载完成等启动, 也可用cron来扩展, cron任务来监控某个文件是否下载完成(如果它的md5值和源头那边一致, 就认为文件传输完毕), 待下载完成后, 调用batch.
batch级的操作
1. 启动一个batch(指定batch级别的Indicator)
2. 暂停一个batch
3. 继续运行一个batch
4. 强制结束一个batch
5. 强制重新开始一个的batch(或者不提供强制重启, 提供另一个reset命令, 强制重启=reset+启动)
6. 查询一个Batch的运行状态
如何一个启动Batch:
batch_manage run Batch_ID='Batch_ERP' Cycle_No='Cycle_20101215' Frequency='D' BatchIndictor='''<batchIndicator transactionDate='20101215' something='hello' />'''
数据模型
在开始介绍数据模型之前, 先看看一个Job会有什么样的属性, 这样有利于我们更好地管理Job.
首先Job会有一个Batch属性, 这样当我们启动一个batch时候, 就知道我们该call哪些job.
其次, Job运行会有频率属性的, 比如job A在天结/周结/月结都需要运行, 而job B只在月结是运行, 显然一个job可能有一个或多个频率值.
为了很好的组织Job, 我们引入了Job_Group概念, 作为Job的类型归类, 比如我们可以有一个Datastage group, 作为所有datastage etl job的归类, SSIS group 作为所有ssis job的归类.
数据模型包括4个部分, 分别为Batch定义模型部分, 运行时模型, 运行时归档模型, Batch定义归档模型.
Batch定义模型是整个系统的基础, 它定义了Batch级以及Job级别的静态模型; 运行时模式, 顾名思义, 保存着batch运行时的动态信息, batch结束后, 所有的动态信息被转存到运行时归档模型中. Batch定义归档模型, 是考虑到我们需要对Batch调度调整进行版本管理引入的, 也就是保存着Batch定义模型的revise数据.
我手头上PowerDesigner/ERWin, 甚至连Visio都没有, 只好用Excel来做建模工作, 所以表的relationship没有办法直观地表达出来, 需要看字段外键. 这里只贴出Batch定义模型和运行时模型, 其他2个部分其实仅仅是这2个部分的简单扩展.
Batch定义模型部分
Node_Static(节点表)
Frequency_Static(频率表)
Job_Group_Static(job组别)
Batch_Static(Batch主表)
Job_Static (任务主表), 这是一个宽表, 字段很多.
Job_Timeout_Def_Static
Job_Depend_Static,
这个表有一个四态控制字段Control_Flag, Control_Flag=Successful 表示, 前一个job必须运行成功, 才能运行后续job, Control_Flag=Failed, 表示前一个job必须失败, 才能运行后续job, Control_Flag=Completed, 表示只要前一个job运行结束(不管是成功还是失败), 后续job都可以运行, Control_Flag=Disable, 表示不考虑这个依赖.
Cron_Events_Static(类似cron的定义表)
Mutex_Lock_Static
Job_Event_Handler
这个表定义每个job启动/结束(包括成功和失败), 会触发哪些事件, 我们可以在这里将job的运行状态写到一个外部log文件, 当然本调度系统应该默认会将这些重要的log写到数据库中.
运行时模型
Batch_status_rt
Batch_Operation_rt
Job_status_rt(1个job最多一条记录)
Job_operation_rt表
Mutex_Lock_Status_rt
Exception_rt表(记录运行时的异常)
整个数据模型确立后, 其实这个batch调度系统在我脑中已经成形了, 剩下的只是花时间实现了. 当然, 我现在没有业余时间来实现这个东西, 期望能在一个新的岗位上有机会完成它, 如果能用python, 那简直太棒了. 如果你正在搞类似的东西, 希望这篇文章会对你有启发.