Data4Strategy

——合抱之木,生于毫末;九层之台,起于累土

【转】Talend作业设计模式和最佳实践-Part I

原文地址:https://mp.weixin.qq.com/s?__biz=MzA3OTg1Mzk4Nw==&mid=2453261363&idx=2&sn=e0f42602271d4bb0415b174dfad9963b&chksm=88604cffbf17c5e9b4a48daf5ec09341f34e0d4a44bba9e31501cbb1701bd8d995a5d173e3e8&mpshare=1&scene=1&srcid=0111PXlXmg0GVBeIsITB7zl5#rd

原文作者:talend官方


作为Talend开发者,不管是入门新手还是资深人士,常常要处理同一个的问题:“在编写这项作业时,哪种方式最好?”我们知道,通常应当高效、易读易写,并且尤其(多数情况下)要易于维护。我们也知道,Talend Studio好比自由形态的“画布”,有全面而丰富的组件、存储库对象、元数据和链接选项,我们可以运用这些来“绘制”代码。那么,如何确定在创建作业设计时使用的是最佳实践?

作业设计模式

自Talend版本3.4起,在每次使用时我都体会到作业设计对我的重要性。起初我在开发作业时并未思考模式。之前我用过Microsoft SSIS和其他类似工具,所以像Talend这样的可视化编辑器对我来说并不陌生。相反,我关注的主要是基本功能、代码可重用性;其次是画布布局;最后是命名规则。现如今,针对各种用例我已经开发了数百项Talend作业,我发现代码变得更加精巧,可重用性更高,一致性也更好,此时,模式的意义才逐渐显露出来。

加入Talend后,我有很多机会看到由客户开发的作业,得以证实自己的看法:对于每位开发者,每个用例都有多种解决方案。我认为这一点是不少人的问题所在。我们作为开发者的确都这么认为,但是在开发特定作业时,往往认为自己的方式是最佳或者唯一选择,但实际上也知道“或许还有更好的方式”,这种声音反复萦绕于耳际。在此情况下,我们期待或寻觅最佳实践,也就是作业设计模式!

制定基本规范

考虑实现最佳作业代码所需元素时,通常用到一些基本法则。这些法则源于多年来从失败中汲取的教训以及积累的成功经验。它们至关重要,为构建代码奠定坚实基础。我个人认为应该引起高度重视。我认为这些法则包括(重要程度不分先后):

l 可读性:创建明白易懂的代码

l 可写性:在最短时间内创建简洁明了的代码

l 可维护性:确定适当的复杂性,同时最大限度减少变更带来的影响

l 功能性:创建满足要求的代码

l 可重用性:创建可共享对象和原子工作单元

l 符合性:创建跨团队、项目、存储库和代码的真正规则

l 易适应性:创建可以变通而不致破坏的代码

l 可扩展性:创建可根据需要调整吞吐量的弹性模块

l 一致性:确定所有内容之间的共性

l 效率:创建优化的数据流和组件利用率

l 分区:创建服务于单一目标的原子化重点模块

l 优化:使用最少代码创建最多功能

l 性能:创建提供最快吞吐量的有效模块

重中之重是如何真正平衡这些法则,特别是前三条,因为这三者总是相互矛盾,满足其中两条往往要牺牲另外一条。如果可以,尝试按重要性对这些法则进行排序。

指导原则并非硬性标准,主要是为了有章可循!

在真正深入研究作业设计模式之前,结合刚刚阐述的基本法则,我们首先要确保了解一些其他值得考虑的细节。我发现很多时候标准过于严苛,并未针对与其相悖的非预期场景留出足够余量。而另外一些时候则相反。不同开发者如出一辙地遵循刻板粗糙、有失协调的规范,更有甚者在作业设计中不连贯、缺乏规划甚至毫无章法,形成不良风气。坦率说来,我认为这样过于草率并会造成误导,其实想要避免这些并不困难。

出于上述以及其他相当明显的原因,首先要制定成文的“指南”,而非建立“标准

其中包含基本法则并随附细则。参与SDLC(软件开发生命周期)过程的所有团队创建并采用“开发指南”文档之后,这些基本法则即可为开发中的结构、定义和上下文方面提供支持。对这一环节的投入具有长远意义,日后所有相关人员都将从中获益。

以下建议要点,您可根据自身情况予以采纳(仅作指导参考,欢迎自行修改扩充)。

1. 方法:其中应详细说明希望如何构建

1. 数据建模

1. 整体/概念/逻辑/物理

2. 数据库、NoSQL、EDW、文件

2. SDLC流程控制

1. 瀑布式或敏捷式/Scrum

2. 要求和规范

3. 错误处理和审计

4. 数据治理与管理

2. 技术:其中应列出各种工具(内部工具和外部工具)以及各工具间如何相互关联

1. 操作系统和基础架构拓扑

2. 数据库管理系统

3. NoSQL系统

4. 加密和压缩

5. 第三方软件集成

6. Web服务接口

7. 外部系统接口

3. 最佳实践:其中应说明要遵循特定指南的内容和时间

1. 环境 (DEV/QA/UAT/PROD)

2. 命名规则

3. 项目、作业和小作业

4. 存储库对象

5. 记录、监测和通知

6. 作业返回代码

7. 代码 (Java) 例程

8. 上下文组和全局变量

9. 数据库和NoSQL连接

10. 源/目标数据和文件架构

11. 作业切入和退出点

12. 作业工作流程和布局

13. 组件利用率

14. 并行化

15. 数据质量

16. 父/子任务和小作业(joblet)

17. 数据交换协议

18. 持续集成和部署

1. 集成源代码控制 (SVN/GIT)

2. 发布管理和版本控制

3. 自动化测试

4. 项目存储库和提升

19. 管理与运营

1. 配置

2. 用户安全和授权

3. 角色和权限

4. 项目管理

5. 作业任务、计划和触发器

20. 存档和灾难恢复

我认为应当开发和维护的一些其他文件包括:

l 模块库:描述所有可重用的项目、方法、对象、小作业和上下文组

l 数据字典:描述所有数据模式和相关的存储过程

l 数据访问层:描述与连接和操作数据相关的所有事项

诚然,创建这样的文档需要时间,但与之在整个生命周期中发挥的价值相比,这些成本物超所值。文档应尽可能简明扼要、直截了当并与时俱进(不必具有声明性质),它将有助于大幅减少开发错误(否则日后会为此付出高昂代价),为您所有的项目取得成功大有助益。

是时候探讨作业设计模式了吧?

当然,但首先还要说明一点。我认为,每位开发者在编写代码时都可能形成或好或坏的习惯。所以培养良好的习惯至关重要。可以从一些简单的习惯开始,比如为每个组件添加标签。这会让代码更具可读性,并且便于理解(我们的基本法则之一)。人人都养成这样的习惯后,确保所有作业都有序组织到存储库文件夹中,并且使用的名称对项目有一定意义(也即符合性)。然后让每个人都采用相同的日志记录消息样式,比方说对于 System.out.PrintLn() 函数采用通用方法封装器,并且对于作业代码建立共同的切入/退出点标准(其中包含替代要求选项)。这两者都有助于快速实现多个法则。随着时间的推移,由于开发团队采用并充分利用定义明确的开发指南,项目代码将变得更易读写,并且可由团队中任何人进行维护(这也是我最期望的效果)。

作业设计模式和最佳实践

在我看来,Talend作业设计模式可为我们提供建议的模板或框架布局,其中涉及关注特定用例的重要和/或必需的元素。模式通常可重用于类似作业创建,如此一来,我们可以快速启动代码开发作业。如您预期,多个不同用例也可引入通用模式,经过正确识别和实施,能够增强整体代码库、压缩作业量并减少重复或相似的代码。我们下面就此展开探讨。

以下是要考虑的7项最佳实践:

画布工作流程和布局

可通过多种方法在作业画布上放置组件,并将这些组件相互关联。我偏好的做法大体是首先“自上而下”,然后“从左至右”,其中左侧边界流程通常是错误路径,右侧边界和/或下方边界流程则为期望的或正常的路径。理想情况下应尽可能避免连线交叉,自6.0.1版开始引入巧妙的弧线连接,很好地体现了这种策略。

我自己不太认同“之”字型模式,也就是组件按顺序“从左到右”放置,到达最右边界后即向下排列,随后再返回到左侧边界,依次类推。感觉这种模式既不方便也不好维护,不过的确容易编写。如果必须要用这种模式,执行的作业可能较之前会增多,并且可能无法得以正确组织。

原子作业模块 ~ 父/子作业

简言之,包含大量组件的大型作业很难理解和维护。要避免这种情况,建议尽可能将大型作业分解为较小作业或工作单元。之后以子作业形式执行(使用tRunJob组件),其目的即对他们加以控制和执行。这么做也便于更好地处理错误和后续事件。请记住,杂乱的作业可能难以理解,难以调试/修复,并且几乎无法维护。而简单的小型作业具有明确目的,通过画布即可确定意图,绝大多数易于调试/修复,维护也相对轻而易举。

创建嵌套的父/子作业层次结构虽然完全可以接受,但需要考虑实际限制。根据作业内存利用率、传递的参数、测试/调试问题以及并行化技术(如下所述),良好的作业设计模式不应超过3级嵌套tRunJob父/子调用。嵌套再深可能更安全,但我有充分理由认为,对于任何用例5级足矣。

tRunJob与小作业

确定子作业与使用小作业之间的简单区别在于,子作业是从作业中“调用”,而小作业“包含”在作业中。两者都提供创建可重用和/或通用代码模块的机会,在任何作业设计模式中结合使用两者都是一种非常有效的策略。

切入和退出点

所有Talend作业都需要在某个地方开始和结束。Talend提供两个基本组件,即tPreJob和tPostJob,目的是帮助控制执行作业内容前后发生的情况。在我的代码中,“初始化”和“收卷”步骤相当于这两个组件。如您预期,首先执行tPreJob,然后执行实际代码,最后执行tPostJob代码。请注意,无论代码正文中是否存在设计的退出(例如tDie组件,或者组件复选框选项“错误结束"),都将执行tPostJob代码。

关于作业切入和退出点,也应考虑使用tWarn和tDie组件。这些组件提供对作业完成位置和方式的可编程控制,还支持改进错误处理、日志记录和恢复机会。

在作业设计模式中,我常使用tPreJob初始化上下文变量,建立连接并记录重要信息。对于tPostJob,则关闭连接和其他重要清理以及更多记录。相当简单对吗?您是这么做的吧?

错误处理和日志记录

这一项非常重要,或者说至关重要,如果可以正确创建通用的作业设计模式,几乎所有项目都能建立高度可重用的机制。我的作业模式是针对任何作业可能包含的具有一致性和可维护性的记录处理器,创建“logPROCESSING”小作业,再附加包含具有符合性、可重用性和高效性的明确定义的“返回代码”。其中的附加项易于编写、易于阅读且易于维护。我相信,如果您能以自己独有的成熟方式来处理和记录项目作业的错误,则必将收获事半功倍的喜悦。建议合理借鉴,善加利用!

最新版Talend增加了对Log4j和日志服务器使用的支持。只需启用“项目设置 > Log4j”菜单选项,然后在TAC中配置Log Stash服务器即可。强烈建议您在作业中纳入这项基本功能,实践证明其效果卓越。

“OnSubJobOK /Error”与“OnComponentOK/Error”(及Run If)组件链接

有时候,Talend开发人员对于“OnSubJob”或“OnComponent”链接之间的差异心存一丝困惑。“OK”与“Error”的区别显而易见,那么这些“触发连接”差异何在,如何影响作业设计流程?

组件之间的”触发连接”可定义处理序列和数据流,其中组件之间的依赖关系存在于子作业中。子作业的特点是所用组件有一个或多个组件与其链接,从而处理当前数据流。单个作业中可能存在多个子作业,默认情况下所有相关子作业组件周围带有蓝色高亮方框(可在工具栏予以开启/关闭)。

1

子作业中的所有组件完成处理后,“OnSubJobOk /Error”触发器将继续处理下一个“链接”子作业。仅可从该子作业中的起始组件处使用。在该特定组件完成处理后,“OnComponenOK/Error”触发器将继续处理下一个“链接”组件。如果继续处理的下一个“链接”组件是需要基于可编程java表达式(true|false判定执行的情况),则“Run If”触发器会非常有用。

何为作业循环?

对于几乎每种作业设计模式,代码中的“主循环”和“辅助循环”都非常重要。这些点用于控制作业执行的潜在退出位置。“主循环”通常表现为数据流结果集的最顶层处理,完成主循环之后,作业即告完成。“辅助循环”嵌套在更高阶循环中,且通常需借助重要控制才可确保作业正确退出。我每次会确定“主循环”并确保向控制组件添加tWarn和tDie组件。tDie通常设置为立即退出JVM(但请注意,即便如此tPostJob代码也会执行)。这些顶层出口点使用简单的代码,“0”表示成功,“1”表示失败返回,不过遵循自己制定的“返回代码”指南再好不过了。“辅助循环”(以及流程中的其他关键组件)中十分适合纳入其他tWarn和tDie组件(其中tDie未设置为“立即退出 JVM”)。

上面讨论的大多数作业设计模式最佳实践如下图所示。请注意我用到了实用的组件标签,不过仍对组件放置规则稍作变通。无论如何,最终结果是作业具有高度可读性、可维护性并易于编写。

2019-01-11_101942

结语

至此,虽说并未悉数解答您关于作业设计模式的疑问(实际上也不可能),但这总归是良好的开端。我们介绍了一些基础知识,提供了方向,并作了小结。希望这些有所帮助,并且能为诸位读者带来一些有益的启示。


如果您觉得此文章对您有帮助,请点击右下方【推荐】让更多人看到,thanks!

posted @ 2019-01-11 11:29  John.Xiong  阅读(979)  评论(0编辑  收藏  举报