皮皮调度(1)——从Airflow到DolphinScheduler,以及“皮皮调度”的来历

2022-10-19,,,

按照前一篇文章

《GraalVM —— 让Java变得再次强大》

末尾提到的计划,本来这篇文章是想写一下GraalVM的后续《深耕云原生的Java应用框架 —— Quarkus》。

其实介绍Quarkus的文章前几天我已经写了一大半,但是后来我觉得光介绍这些技术的文章实在是太无聊了,所以先暂停了。

我认为技术相关的文章,很容易变得非常枯燥,所以,对于技术相关的文章,最好是能融入到实际场景中,融入到故事中。不光是要讲怎么做,还要讲清为什么要这么做。

所以,我虚构了一个场景“皮皮调度”来讲接下来的故事。

本文作为《皮皮调度》系列文章的开篇,先简单介绍一下故事背景。

历程1:从BI到简单调度

之前的工作主要是在商业智能(Business Intelligence,简称BI)领域。

最早在外企工作时,对于BI的理解比较片面,认为BI主要是基于已经加工好的数据,针对业务场景,进行各种高端的建模和可视化。而对于底层数据怎么加工,则认为那是“数仓”的职责,两者分工明确、各司其职。这时候,很像是生活在“象牙塔”中。

而加入创业公司后,则深深的感觉到整个数据生态的步骤是环环相扣的。数据分析要想做的好,离不开底层的数据处理。这时候,像是回到了有烟火气息的“人间”。

所以,我们的BI系统很早就有一个“数据处理”功能(我们叫它SmartETL)。其功能主要是:通过“拖拉拽”的界面配置出数据处理的流程,等到真正执行的时候,我们的系统自动把这个流程翻译成具体的Apache Spark任务并执行。可以把它简单的理解为:把一个或者多个现有的数据集(输入数据集),通过SmartETL的流程,生成一个或者多个新的“输出数据集”。

当然,既然我们在“人间”,那么,我们就会遇到各种各样的问题等着去解决。首先,

唯一不变的,就是”变化“本身

我们的数据也是一直在变化的,首先我们就要解决一个数据更新的问题。

在我们没有SmartETL前,我们的数据集都是一个个单独的个体,其更新可以采用最简单的”定时触发“方式。具体的实现为:我们可以为每个数据集设置一个CRON表达式来代表其更新的时刻, 而服务器负责在该CRON触发的时刻执行该数据集的更新。

当我们有了SmartETL后,第一版本,我们可以使用最简单的”依赖触发“策略:即任何一个”输入数据集“更新后,自动触发SmartETL的执行,从而来更新后续”输出数据集“。

历程2:从简单调度到Airflow

”依赖触发“,以其简单易于理解得到了广泛的应用。

但是,真实的世界从来都不是这么简单的。很快我们遇到了一些问题, 这里只举一个小的场景。

我们的一个客户,他的数据是存储在”分库分表”的16个MySQL数据库中,我们要汇总这16个数据库中的数据,并进行后续的SmartETL处理。基于“依赖触发”的简单实现,我们可以有如下方式:

方式1:最直接的方式,对于16个库,分别建立不同的16个数据集,定时运行。最后再做一个SmartETL,把这16张表合并为一个结果表。但是这样做的问题是:我们的SmartETL将会被触发16次(当然我们可以优化并合并减少一些次数),系统资源浪费严重,并且更可怕的是,系统有很多中间状态,数据总是只有部分数据。

方式2:我们的系统对于SmartETL做个简单的增强,对于16个数据集,我们可以选择只有某一个特定的数据集更新后才触发SmartETL更新。但是问题是我们不知道具体哪个数据集是最后一个执行的。所以,我们又想想其它的“聪明”策略,比如:我们对于其它的15个数据集,都设置其更新时间为凌晨2点,而对于最后一个数据集,我们设置其凌晨3点更新。但是这个仍是有风险的,万一某一天前15个数据集有个更新非常慢,超过了“收尾”数据集,那么我们就会面临最终输出数据集数据错误的严重问题了。

这些方式都不是很好用。究其原因:无论是中间流程节点(SmartETL),还是各个输入数据集, 它们都只是整个数据流程的一部分,其单独是无法解决这个执行顺序问题的。所以,我们需要有个统筹的“调度”模块,来协调各个子组件的执行顺序。

当时就想我们能不能参考和利用一些成熟的开源软件呢?答案是肯定的。

首先,开源调度领域最有名的应该就是 Apache Airflow了, https://github.com/apache/airflow, 通过学习其官方文档:https://airflow.apache.org/docs/stable/, 发现确实不错。

于是,简单实现了一个上传数据到BI系统的 operator, 组装一个DAG定时任务来把上面的16个库按照一定的并发来导入到BI平台中,并只在最后触发一次SmartETL更新。完美收工!

整体体验:Airflow整体还是不错的,虽然那时有些bug:比如CPU经常100%,却长时间无人修复等, 但是不得不说:使用Python代码来作为DAG的描述的方式对于软件研发人员来说确实体验不错,也更容易结合git进行版本控制。

历程3:从Airflow到DolphinScheduler

Airflow对于相对熟悉Python的人来说确实不错(比如我),但是当我想推广给公司的顾问同事使用, 用来帮助客户构建轻型数仓时,却遇到了非常大的挑战。

具体的功能不完善等小的挑战就不再赘述了(这些都可以通过扩展增强解决的),不过最麻烦的一点是: Airflow强依赖于Python, 而在非技术人员的眼里, Python代码和很多偏技术的概念都比较难以理解。

于是,我开始寻找一个更利于非技术人员使用的调度工具。感谢这次探索,我发现了Apache DolphinScheduler(在贡献给Apache前,叫做 EasyScheduler),其吸引我的几个点:

Apache License

Process/Task的definition 和 instance 分离, 支持补数据, 概念清晰. 路走对了, 就不怕远

有还不错的图形化配置界面, 而不是什么都要写json配置, 或者python设置DAG等

基于JVM, 以后方便Java Shop来扩展

并通过一系列的贡献,有幸成为了其PPMC(孵化项目管理委员会)成员,具体的内容就不在这里赘述了, 感兴趣的朋友可以参考我之前在“DolphinScheduler第一届用户大会”上的分享:

《观点 | 观远首席架构师分享「从开源使用者到Apache PPMC之路」》

反思1:Airflow和DolphinScheduler是否有本质区别?

前些天,看到了一篇CernerWorks公司使用Airflow的文章:

《Building a Production-Level ETL Pipeline Platform Using Apache Airflow – Using Apache Airflow to Manage Data Workflows in CernerWorks》:https://towardsdatascience.com/building-a-production-level-etl-pipeline-platform-using-apache-airflow-a4cf34203fbd

有些新的体会,当我们想到Airflow,可能第一印象是,其用户需要也了解Python,但是其实不一定,比如该文中就列出了一个中间层:yaml, 用户只需要写yaml文件,然后就能调度了,而无需关注具体Airflow的Python文件DAG是怎么编写的。文中的yaml例子如下:

对于DolphinScheduler也是一个参考,也许DolphinScheduler也可以中间抽取来一层Yaml来代表工作流 (无需关注屏幕上每个节点的布局坐标):

    可以既支持 图形化拖拽方式配置,也支持手写Yaml。

    把ApiServer和MasterServer等逻辑独立出来。MasterServer等无需关注数据库等

    方便写UnitTest等

    同时,DolphinScheduler生成的工作流yaml,也可以支持:可以选择在DolphinScheduler里跑,也可以选择发布到Airflow中跑

所以有了中间的yaml层,其实,我们就可以把一个系统分为前台,中台,后台了。

前台:可以是像DolphinScheduler那样的图形化、拖拽式地配置DAG工作流, 也可以是直接在IDE中编写中台需要的yaml文件

中台:yaml文件层,把所有的操作都转为一个标准的yaml文件,作为打通前台和后台的统一标准语言

后台:具体定时调度,执行工作流的引擎,这个引擎可以是 Airflow,可以是DolphinScheduler,或者是别的单机程序

反思2:调度系统的主体程序应该只提供平台核心功能,通过插件来造就生态

之前一个人的分享说:他用Airflow多年的收获就是不用任何其它operator,而是只用kubernetes-operator,Airflow只负责串起来任务,和调度,不负责具体的执行。

所以,我也在想:是不是调度系统本身不应该直接提供太多的实现逻辑,而只是把核心的基础打好。在夯实的底层基础上,提供一套方便插件开发的SDK层。

反思3:Java和Python在调度系统中的定位

Java是企业软件开发领域的王者,Python是数据科学领域的霸主。

Java的特点:

有着各种成熟企业应用框架

强类型检查,方便提早在编译时发现问题

但是相对Python更加偏重些

Python的特点:

有着各种灵活的数据分析库:numpy、pandas、matplotlib、scikit-learn等

脚本语言,开发、修改某个小的功能非常方便,不用重新打包

弱类型语言,开发复杂的企业应用相对容易产生更多的问题

结合调度系统,我认为:比较适合用Java来搭建底层的调度平台,而Python可以用于实现各种具体功能插件。

小梦想:实现”个人智能助手“系统

我有一个小的梦想,实现辅助个人决策的”个人智能助手“系统。(注:不是某著名P8所招聘的私人生活助理,而是电脑上运行的智能程序)。

而智能系统的第一步是:需要一个协调各个未来系统的中央调度器。这个系统就是 —— ”皮皮调度“!

“皮皮调度”名字的来历:我个人非常不擅长起名字,在给我心中的个人调度系统想名字的时候,第一个冒出来的词就是我儿子的小名——皮皮。

”皮皮调度“的期望:

定位为”个人智能助手“的指挥官,需要时时刻刻都在运行

具有定时功能,不光提示我一些重要备忘时刻,还要帮我定时爬取一些数据并做自动汇总分析等

省钱,希望”皮皮调度“能跑在我的 1核CPU、1G内存的云主机上,并且使用内存小于100M

有了上面的期望和限制,(顺便说一下,在开发程序的时候,我喜欢更多的限制,只有足够的限制,才能让你的程序聚焦,以及更加极致), ”皮皮调度“的实现方向如下:

底层平台使用Java开发, 各种插件主要利用Python

对于底层用Java实现的平台,使用 GraalVM 的 native-image 编译为原生程序,只用这样才能使得其运行时内存 < 100M

用Python编写的插件,要充分利用Python的各种库,使得用非常短的时间就可以开发一款新的插件。方便我来快速的探索各个新的领域

接下来,让我们一起来进入”皮皮调度“的探索之旅吧!(背景音乐:朴树的《平凡之路》响起……)

戳原文,立刻奔向 DolphinScheduler 的官网一起玩耍~

皮皮调度(1)——从Airflow到DolphinScheduler,以及“皮皮调度”的来历的相关教程结束。

《皮皮调度(1)——从Airflow到DolphinScheduler,以及“皮皮调度”的来历.doc》

下载本文的Word格式文档,以方便收藏与打印。