调度属性配置
概述
什么是调度
在典型的批处理场景中,任务都是周期性的运行(例如每天运行一次),且不同的任务之间会存在上下游依赖关系,例如数据抽取任务会首先运行,之后才运行SQL处理任务。在这一过程中,周期性、任务的上下游依赖关系就是「调度」的基本概念,调度系统的职责可以归结为2方面:
- 生成周期实例:参考实例的生成
- 按计划运行实例:由于任务之间存在上下游关系(有些同时存在自依赖),需判断任务是否到达了运行的条件
用户可以在离线开发中进行调度属性的配置,主要包含调度周期配置、上下游依赖性、跨周期依赖、任务优先级配置等内容
基本原则
调度系统的基本原则分为以下2类:
判断任务运行条件
每天按计划运行周期实例时,调度系统判断一个实例是否可以运行,需满足2方面条件:
- 任务的计划时间
- 上游任务是否全部成功
若不能同时满足以上2个条件,则任务会处于等待提交状态。满足以上2个条件时,此任务在调度系统中具备了运行的条件,调度系统会将任务「提交」至计算引擎(任务进度等待运行状态),此任务还需要获得集群的计算资源,集群资源的分配由计算集群分配,用户不可干预,因此,满足时间+上下游依赖条件,并不意味着任务会立即开始运行,其可能在等待计算资源
例如,某任务A计划时间为每天02:00,上游依赖B、C、D 3个任务,那么判断A任务是否可运行,其判断逻辑为:
- 当前时间>=02:00
- 上游任务B、C、D是否全部为成功状态
寻找上游实例
在任务的周期性、上下游、跨周期等各种场景,尤其是不同周期的任务相互依赖,调度系统进行依赖判断的基本原则是:
第一条:当前实例寻找上游实例时,永远会去寻找一个与其计划时间相等,或之前的实例,不会去寻找未来时间的实例
第二条:这一原则仅在如下条件下会被打破:当均为天任务时,会在当天的实例中,寻找上游任务的实例
对这一原则的场景化描述请参考几种典型场景
任务与实例
任务与实例是一对多的关系,在「数据开发」模块中配置的SQL任务、同步任务,其中包含代码、配置信息等,这是一个任务(Job),一个任务每运行一次,被称作一个实例(Instance)。任务是固定的一个配置,将一个任务运行多次,每次的结果、数据产出都不一样,在概念上进行区分是很有必要的。在本文的描述中,「任务」和「实例」可能会混用,读者需结合上下文理解其实际含义
周期性
周期粒度
在「数据开发」模块,任意打开一个任务,可在其右侧的「调度依赖」面板中,可配置「调度周期」为天、周、月、小时、分钟、cron表达式及自定义调度周期等方式。
调度周期 | 起调时间配置 |
---|---|
日 | 天调度任务,即每天自动运行一次。新建周期任务时,默认的时间周期为每天0点运行一次,可根据需要自行指定运行时间点,配置"具体时间": - 小时:单选下拉列表,00-23,默认选中00 - 分钟:单选下拉列表,00-59,默认选中00 |
周 | 周调度任务,即每周的特定几天里每天在特定时间点自动运行一次,需配置 选择时间 和 具体时间 - 选择时间:复选下拉列表{星期一;星期二;……星期日},可复选,默认选中 星期一 - 具体时间:同 调度周期-日 相同; |
月 | 月调度任务,即每月指定的特定几天里每天在特定时间点自动运行一次,需配置 选择时间 和 具体时间 - 选择时间:复选下拉列表{每月最后一天;每月1号;每月2号;……每月31号},可复选,默认选中 星期一 - 具体时间:同 调度周期-日 相同; |
小时 | 小时调度任务,即每天指定的时间段内按N*1小时的时间间隔运行一次,比如每天1点到4点的时间段内,每1小时运行一次。当调度周期切换到非天级调度时,节点起调时间将不可选,需配置 开始时间 、 结束时间 和 间隔时间 - 开始时间、结束时间:同 调度周期-日 类似,小时可选择00-23,分钟不可选,开始时间的分钟为0,结束时间的分钟为59; - 间隔时间:单选下拉列表{1小时;2小时……23小时},默认选中1小时; - 开始时间,应早于结束时间 ; |
分钟 | 分钟调度任务,即每天指定的时间段内按N*指定分钟的时间间隔运行一次,目前能支持的最短时间间隔为每5分钟运行一次,当调度周期切换到非天级调度,节点起调时间将不可选,需配置 开始时间 、 结束时间 和 间隔时间 - 开始时间、结束时间:同 调度周期-小时 相同; - 间隔时间:单选下拉列表{5分钟;10分钟;……55分钟},默认选中5分钟; - 开始时间,应早于结束时间 |
Cron表达式 | 填写Cron表达式 |
自定义调度周期 | 选择在控制台上传的自定义调度周期 |
- 按照小时周期调度时,时间周期按左闭右闭原则计算,比如配置为从0点到2:59点的时间段内,每隔1个小时运行一次,表明时间区间为[00:00,02:59],间隔为1小时,调度系统将会每天生成3个实例,分别在0:00/1:00/2:00运行。
- 离线开发支持的最小调度间隔为5分钟,不支持更短的调度周期,若存在此种场景,可考虑实时计算等其他解决方案
- 具体时间并不表示任务的实际开始时间,系统可能会因为上游任务延迟、集群资源紧张等原因造成延迟执行
Cron表达式
Cron表达式是一个具有时间含义的字符串,字符串以5~6个空格隔开,离线仅支持6个域,格式为X X X X X X
。其中X
是一个域的占位符。代表年份的域离线获取的是生效日期的年份。单个域有多个取值时,使用半角逗号,
隔开取值。每个域可以是确定的取值,也可以是具有逻辑意义的特殊字符。每个域最多支持一个前导零。
说明 如指定每天上午8:15执行任务,Cron表达式可指定为0 15 8 ? * *
或0 15 08 ? * *
,而不能指定为0 15 008 ? * *
。
域取值
下表为Cron表达式中六个域能够取的值以及支持的特殊字符。
域 | 是否必需 | 取值范围 | 特殊字符 |
---|---|---|---|
秒 | 是 | [0, 59] | * , - / |
分钟 | 是 | [0, 59] | * , - / |
小时 | 是 | [0, 23] | * , - / |
日期 | 是 | [1, 31] | * , - / ? L W |
月份 | 是 | [1, 12]或[JAN, DEC] | * , - / |
星期 | 是 | [1, 7]或[MON, SUN]。若您使用[1, 7]表达方式,1代表星期一,7代表星期日。 | * , - / ? L # |
特殊字符
Cron表达式中的每个域都支持一定数量的特殊字符,每个特殊字符有其特殊含义。
特殊字符 | 含义 | 示例 |
---|---|---|
* | 所有可能的值。 | 在月域中,表示每个月;在星期域中,表示星期的每一天。 |
, | 列出枚举值。 | 在分钟域中,5,20表示分别在5分钟和20分钟触发一次。 |
- | 范围。 | 在分钟域中,5-20表示从5分钟到20分钟之间每隔一分钟触发一次。 |
/ | 指定数值的增量。 | 在分钟域中,0/15表示从第0分钟开始,每15分钟。在分钟域中3/20表示从第3分钟开始,每20分钟。 |
? | 不指定值,仅日期和星期域支持该字符。 | 当日期或星期域其中之一被指定了值以后,为了避免冲突,需要将另一个域的值设为?。 |
L | 单词Last的首字母,表示最后一天,仅日期和星期域支持该字符。 | 在日期域中,L表示某个月的最后一天。在星期域中,L表示一个星期的最后一天,也就是星期日(SUN)。如果在L前有具体的内容,例如,在星期域中的6L表示这个月的最后一个星期六。 |
W | 除周末以外的有效工作日,在离指定日期的最近的有效工作日触发事件。W字符寻找最近有效工作日时不会跨过当前月份,连用字符LW时表示为指定月份的最后一个工作日。 | 在日期域中5W,如果5日是星期六,则将在最近的工作日星期五,即4日触发。如果5日是星期天,则将在最近的工作日星期一,即6日触发;如果5日在星期一到星期五中的一天,则就在5日触发。 |
# | 确定每个月第几个星期几,仅星期域支持该字符。 | 在星期域中,4#2表示某月的第二个星期四。 |
取值示例
以下为Cron表达式的取值示例。
示例 | 说明 |
---|---|
0 0 10,14,16 * * ? | 每天上午10:00点、下午14:00以及下午16:00执行任务 |
0 0/30 9-17 * * ? | 每天上午09:00到下午17:00时间段内每隔半小时执行任务 |
0 14 * ? | 每天下午14:00到下午14:59时间段内每隔1分钟执行任务 |
0 0-5 14 * * ? | 每天下午14:00到下午14:05时间段内每隔1分钟执行任务 |
0 0/5 14 * * ? | 每天下午14:00到下午14:55时间段内每隔5分钟执行任务 |
0 0/5 14,18 * * ? | 每天下午14:00到下午14:55、下午18:00到下午18:55时间段内每隔5分钟执行任务 |
0 15 10 L * ? | 每月最后一日上午10:15执行任务 |
0 15 10 ? * 6L | 每月最后一个星期六上午10:15执行任务 |
0 15 10 ? * 6#3 | 每月第三个星期六上午10:15执行任务 |
0 10,44 14 ? 3 WED | 每年3月的每个星期三下午14:10和14:44执行任务 |
自定义调度周期
当调度周期无规律且复杂时,用户可使用自定义调度周期,例如在金融客户的使用场景中,可能存在计划日期是国内外的金融交易日期,交易日期无法用cron表达式等方式表示,因此可使用自定义调度日期。
自定义调度周期配置
控制台管理员可在「控制台->全局配置->自定义调度周期」中点击「添加按钮」上传自定义调度日期,如图所示
按要求上传csv格式文件并填写名称,系统将解析出上传的自定义调度日期。文件模版中标明了日期上传要求,如下图
按要求填写自定义调度周期,如下图
上传文件,弹窗展示如下图日历,可预览文件中配置的自定义调度日期
在弹窗中展示最后一次调度日期,若最后一次调度日期与当前日期时间间隔小于等于十天,则会在列表中变红提示。
点击确认后,列表末尾生成新的记录
点击「查看」按钮,可以查看自定义调度配置信息和任务使用情况,如图
点击「编辑」按钮,可以对自定义调度周期进行下载和重新上传
对自定义调度周期进行删除时,需要确认没有任务应用
自定义调度周期使用
已添加的调度周期在全平台范围内生效,在任一任务的「调度依赖->调度属性」选中「自定义调度周期」,会在下方展示自定义调度周期选择框,如图所示
自定义调度周期的选择范围是所有在控制台配置的自定义调度周期,选中自定义调度日期后,可点击右侧的「预览」按钮,可以查看自定义调度周期的生效范围,如图所示
当自定义调度周期上传模版是精确到日期时,选择该自定义调度周期,可在调度配置处设置调度时分,如下图所示
生效日期
- 指当前调度任务的调度生效日期范围,默认为100年,若有特殊需要可以修改此时间范围
- 超出生效日期后,此调度任务的实例不会再生成
出错重试
勾选出错重试,当本任务运行失败时,会进行自动重试,每次间隔2分钟,可配置重试1次至5次,默认重试3次。
建议对重点任务配置重试,例如如下几种场景:
- 数据量较大的同步任务,或运行时间较长的同步任务
- 某些重要性较高的任务
- 某些资源消耗比较大的任务
冻结任务
如果需要让某个任务停止运行一段时间,可以在「任务开发」模块打开某任务,在右侧「调度依赖」面板中勾选「冻结」,表示此任务进入冻结状态
- 处于冻结状态的任务,其周期实例依然会生成,但不会运行
- 对于存在依赖关系的多个任务,如果将上游任务A冻结,则下游任务B也会进入
冻结
状态,B任务的实例也会产生,但不会运行,在B任务的执行日志中会打印出是由于A任务被冻结才没有运行的 - 冻结是立即生效的,冻结状态的任务,生成的实例不会立即进入冻结状态,会首先进入等待提交状态,到达计划时间后,无论上游任务的状态怎样,会立即进入冻结状态,按此逻辑,若任务实例的计划时间还未到达,任务解冻后可以正常运行
- 已经进入冻结状态的实例无法解冻,若需要这些实例执行,必须先对任务解冻,解冻后再对实例进行「重跑」
对冻结状态的任务执行补数据,补数据实例会正常运行
自动跳过
由于小时/分钟任务的调度频率很高,且偶尔会存在执行时间过长的情况,为节约计算资源,让任务尽快「追赶」上当前时间,离线开发支持对小时/分钟任务配置自动跳过的逻辑
假设设置分钟任务的调度周期为10分钟一次,正常需要2分钟运行完成,且分钟任务处理的是当天全天的数据,且设置了自依赖模式,在2020-05-03,任务的计划时间点分别为:
- 2020-05-03 23:10:00 --集群发生故障,任务在23:11分启动运行,但直到23:35才运行结束
- 2020-05-03 23:20:00 --不运行实例,直接将其状态置为「自动取消」
- 2020-05-03 23:30:00 --不运行实例,直接将其状态置为「自动取消」
- 2020-05-03 23:40:00 --运行23:40的实例
- 2020-05-03 23:50:00 --无论是否跳过,一天中的最后一个实例都会运行
- 2020-05-04 00:00:00
如上文所述,在小时、分钟任务中,如果勾选了自动跳过,则离线开发可以自动跳过已经「过期」的实例,直接运行最新的一个,即可实现最新数据的产出,当然这对任务代码、自依赖模式也有要求
自动跳过只会跳过一天中间的实例,某一天最后一个实例(上例中的23:50的实例)不会跳过,以保障当天最后一个实例产出当天全天的数据
上下游依赖
为什么要配置调度依赖
一些任务的数据加工依赖其他任务的数据产出。在离线开发中支持自动依赖、手动依赖两种依赖配置方式。
任务依赖配置
自动依赖
对于SparkSQL、HiveSQL、ImpalaSQL任务,可自动解析用户SQL代码,并自动推荐上游任务,其基本逻辑如下:
假设A任务代码:
INSERT INTO ta
SELECT * FROM t
假设B任务代码:
INSERT INTO tb
SELECT * FROM ta
应该配置A为B任务的上游,则当A任务已经提交当前提下,在B任务的「调度属性」的「任务间依赖」模块选中依赖方式为「自动依赖」按钮,将在依赖列表中显示A任务。
选择自动依赖时,会根据当前IDE的代码的血缘解析结果(通过解析create as
, insert into as
, insert overwrite into as
等语句)把当前任务所有来源表的产出任务解析在列表中。
依赖生成方式从自动依赖切换成手动依赖时,自动依赖解析得到的上游任务保留,不自动删除。
依赖生成方式从手动依赖切换成自动依赖是,手动依赖添加的任务将被全量覆盖。
手动依赖
手动依赖的添加方式可选择手动添加或依赖推荐,依赖推荐目前仅支持三种任务类型的解析:SparkSQL、HiveSQL、ImpalaSQL
若某任务B必须在任务A完成后运行,则A为B的上游任务,这种依赖关系可通过如下方式配置:在「调度依赖」面板中的「任务间依赖」模块选中依赖方式为「手动依赖」按钮,点击「添加依赖」,在弹窗中选择添加方式为「手动添加」,选择相应的租户、产品(离线开发、算法开发、智能标签)、项目,并输入上游任务的名称,在搜索的下拉结果中选择需要依赖的上游任务。
是要实在一套平台内,任务可实现跨集群、租户、产品、项目实现依赖
依赖属性为非必填项,当下游任务需依赖上游任务产出数据,建议配置依赖关系
上游任务失败后,下游任务不会运行,且下游任务的状态会保持「等待提交」
当在弹窗中选择添加方式为「依赖推荐」时,和「自动依赖」的逻辑一致,会根据当前IDE的代码的血缘解析结果(通过解析create as
, insert into as
, insert overwrite into as
等语句)当前任务所有来源表的产出任务将会解析在「选择任务」列表,列表中还会展示类型、项目、产品、责任人、关联内容(当前任务与上游任务的关联表,以schema.table的方式展示)等字段。选中任务后点击「确认」,可与选中任务建立上下游依赖关系。
除SparkSQL、HiveSQL、ImpalaSQL任务外,任务间依赖方式默认为「手动依赖」,且不存在「依赖推荐」。
当选择自动依赖时,默认会和所有血缘关系的任务建立依赖关系,无法修改。
当选中推荐依赖时,可以在所有推荐的血缘关系的任务中,选择合适的任务建立依赖关系。
跨周期依赖
基本配置
跨周期依赖比较典型的场景是:昨天的实例必须先执行成功,今天的实例才可以调度起来,跨周期依赖的选项有4种,结合实例来看,假设A任务每天01:00运行,分析3月2日、3日的实例,不同的跨周期依赖的效果如下:
- 不依赖上一调度周期,无论2日的实例运行情况如何,3日的实例会正常运行
- 自依赖,等待上一调度周期成功,才能继续运行,2日的实例必须运行成功,3日的实例才具备运行的条件,若2日的实例未处于成功状态,则3日的实例会处于「等待提交」的状态
- 自依赖,等待上一调度周期结束,才能继续运行,与上一种情况类似,但不要求2日的实例必须成功,只需要2日的实例运行结束(成功、失败、取消、自动取消)即可
结束状态包括成功、失败、取消、自动取消
依赖属性配置的调度依赖是同周期依赖和跨周期依赖不冲突。任务A可以配置依赖属性依赖任务B,也可以配置跨周期依赖依赖B,如此任务A既依赖任务B,本周期也依赖任务B上周期。
高级配置
跨周期依赖配置中还有另外2种配置:
- 等待下游任务的上一周期成功,才能继续运行
- 等待下游任务的上一周期结束,才能继续运行
这2种情况使用的情况较少,主要场景是:本周期该任务是否运行,取决于下游任务上一周期的运行情况。如果下游任务的上一周期运行成功/结束,本周期的任务才能开始运行,例如:
A任务、B任务:
- 调度周期:均为10分钟
- A为B的上游
- A、B的结果表是同一个,均向表t的天分区写入数据,代码如下:
INSERT INTO t partition (ds = 20200303) SELECT * FROM ……
- 当A与B任务同时运行时,会出现二者同时向同一个分区写入数据的情况(无论A、B是否配置自依赖,均会发生2个任务向同一分区写数据的情况),会造成数据错乱或任务失败,如下图所示:
在这种场景下,需要等待B任务的上个周期实例完成运行,A任务才能启动运行,应形成如下的依赖关系:
A任务的跨周期依赖应配置为:等待下游任务的上一周期结束/成功,才能继续运行
对小时、分钟任务等高频率调度的任务,建议将依赖设置为不依赖,或者等待XX结束,防止中间某个实例失败造成下游的大面积延误或失败
几种典型场景
天任务之间的依赖
假设A、B、C任务均为天任务,依赖关系为:A→B→C,3个任务的计划时间分别为:A(01:00)、B(03:00)、C(02:00)。在资源十分充足的条件下,会产生如下的运行情况:
- A:01:00开始运行
- B:03:00开始运行
- C:在当天的天任务中寻找上游依赖,则判定依赖B任务03:00的实例,C任务会在B任务03:00运行结束后才具备运行条件,C可能会在03:10开始运行
当均为天任务时,会在当天的实例中,寻找上游任务的实例
短依赖长
以天依赖月为例,A为月任务,B、C为天任务,依赖关系为:A→B/C
3个任务的计划时间为:
- A:每月1日,01:00
- B:每天,01:30
- C:每天,03:00
下面以9月5日为例:B、C任务寻找的最近的一个A任务的实例,为9月1日 01:00(计划时间)的实例,则会形成如下图的依赖关系:
假设3个任务的计划时间变更为:
- A:每月25日,01:00
- B:每天,01:30
- C:每天,03:00
依然以9月5日为例:B、C任务寻找的最近的一个A任务的实例,为8月25日 01:00(计划时间)的实例,则会形成如下图的依赖关系:
长依赖短
以天依赖小时为例,A为小时任务,B、C为天任务,依赖关系为:A→B/C
3个任务的计划时间为:
- A:每天,每隔1小时运行一次,计划时间分别为:00:00、01:00……
- B:每天,01:00
- C:每天,03:30
依然以9月5日为例:B、C任务寻找的最近的一个A任务的实例,则会形成如下图的依赖关系:
当前实例寻找上游实例时,永远会去寻找一个与其计划时间相等,或之前的实例,不会去寻找未来时间的实例
月被依赖的特殊处理
当月任务被依赖时,会对月任务的实例做一次特殊处理,场景如下:
假设在9月5日新建了一个月任务A,计划时间是每月1日,并设置了2个天任务B、C作为A任务的下游,按上文的依赖判断原则,B、C任务应依赖于A任务9月1日的实例,但A任务5日才被创建,不存在1日的实例,这种场景下调度系统会做特殊处理:
A由于首月创建时间较晚,实例还不存在时,B、C任务依然可以运行
A任务可以通过补数据来生成9月1日的实例,但实例不会在补数据实例中寻找上游,仅会在周期实例中寻找上游
实例的生成
离线开发在每天22:00统一生成第二天所有需要的任务实例,基于以上设计,任务开发时需要注意任务的提交时间,这里以一个天周期调度任务A为例:
任务A基本信息:调度周期:1天;具体调度时间:8:00;
- 若在1月1日21:00提交A任务(时间轴上侧),离线开发会在当天22:00产生A任务的实例,并会在1月2日8:00第一次运行
- 若在1月1日23:00提交A任务(时间轴下侧),由于离线开发已经在22:00产生了1月2日的所有实例,A任务在1月2日将不会运行。A任务的实例将会在1月2日22:00产生,并在1月3日第一次运行
可以通过调整batch.job.graph.build.cron来更改实例生成时间,通过下图两个步骤进行修改
1、打开EM找到engine服务
2、在配置信息中找到batch.job.graph.build.cron修改时间
任务优先级
调度系统支持为任务手动设置优先级,在「数据开发」模块中,任务的「环境参数」中编辑优先级参数,默认参数为:
##任务优先级, 值越小,优先级越高,范围:1-1000
job.priority=10
JAVACopied!
设置了任务优先级后,可能不容易观察到效果,原因是优先级的判断是弱于任务的计划时间和上下游依赖的,只有在大量任务同时满足了计划时间+上下游依赖的前提下(即满足提交至引擎的条件),任务会大量堆积在控制台,优先级参数才会体现较为明显的效果。
各种参数的优先级判断顺序:任务计划时间 > 任务上下游 > 任务优先级参数
其他
- 任务被删除
如果此任务被其他任务依赖(是其他任务的上游任务),则此任务不能被删除,您需要先解除依赖关系再进行删除, 任务删除后,已生成的任务实例不会被删除,但会运行失败
- 想在每月的最后一天计算当月数据怎么办
离线开发在配置任务调度时,配置的都是计划时间,通常的每月最后一天计算当月数据,都是指的是每月1日凌晨计算上个月整月数据,建议计划时间选择每月的1日运行即可