如何使用python为多处理器系统建立一个基于DAG的任务调度工具

如何使用python为多处理器系统建立一个基于DAG的任务调度工具

从初创公司到大型企业,不同规模的数据驱动型公司的成功,很大程度上是基于其运营的良好实践和保持数据更新的方式,他们每天都在处理数据的种类速度数量 ,在大多数情况下,他们的战略取决于这些特征。这类公司的数据团队的一些目标是。

  • 设计和部署具有成本效益和可扩展的数据架构
  • 从他们的数据中获得洞察力
  • 保持业务和运营的正常运行

为了实现这些目标,数据团队使用工具,这些工具大多允许他们提取转换加载 数据到其他地方或目标数据源,可视化数据并将数据转换成信息。这是很常见的ETL工具。 任务调度,工作调度工作流调度工具在这些团队中很常见。值得一提的是,这些术语。 任务调度, 工作调度, 工作流调度, 任务编排, 工作协调工作流协调是相同的概念,在某些情况下,可以区分它们的是工具的目的和它的架构,其中一些工具只是为了协调ETL过程,并通过使用管道架构来指定它们何时执行,其他工具使用DAG架构,以及提供指定DAG何时执行和如何以正确的顺序协调其任务(顶点)的执行。

这最后一种架构的优点是,所有的计算都可以在执行DAG的机器上使用,优先运行DAG的一些任务(顶点),以实现并行。

图是一个顶点(任务)和边(顶点之间的连接或依赖关系)的集合。因此,一个有向无环图或DAG是一个没有循环的有向图。

DAG

管道是一种DAG,但有限制,每个顶点(任务)最多有一个上游和一个下游的依赖关系。

管线

这类工具在过去几年中蓬勃发展,提供了共同的功能。

  • 自动工作流
  • 任务/工作的调度或协调
  • 它们允许ETL或数据集成过程的创建或自动化
  • 使用DAGs或Pipelines数据结构
  • 有些是 单一的架构而其他的是 高度分布式的.
  • 有些工具不利用多处理器机器,有些则利用。
  • 有些工具带有自己的基础设施,有些则允许你使用云端或内部的任何基础设施。
  • 有些工具允许你编写与每个达格任务相关的代码或脚本,有些则是拖放式组件。

总结一下。协调调度 是一些ETL工具所具有的一些功能。

我将在这里提到其中一些。

Apache Airflow,Jamsscheduler,Pentaho,Prefect.io,argo,luigi,Dask,Matillion,Airbyte,DolphinScheduler , Stonebranch,Apache NIFI,Stitch,Dagster,metaflow,Flyte,One Virtual Source(OVS)

这些工具的使用涉及到大量的业务,包括咨询、昂贵的许可证和维护开源工具的公司,并有一个交付模式,即通过订阅计划将集中托管的软件授权给客户。

问题陈述

本文的目标是教你如何为多处理器系统设计和建立一个简单的基于DAG的任务调度工具,这可以帮助你减少这种技术在你的公司产生的账单成本,或者创建你自己的,并在这种工具的基础上开始一个盈利的业务。

功能要求

  • 它应该是一个Python模块
  • 到现在为止,该模块应该只收到一个.json或.yaml文件,其中有任务的规格和它们的依赖关系。
  • 这些任务将基于独立的脚本。
  • 该工具应该与任何云或内部供应商一起工作
  • 该工具应该在选定的云提供商中为自己建立、关闭和停止基础设施
  • 该工具应该利用运行DAG的机器上的计算,因此,一些任务可以并行执行,可以独立分配给每个处理器,从而利用机器的资源。
  • 这些任务之间不应该传输数据,也不应该传输状态。
  • 当且仅当所有任务成功运行时,DAG将显示为成功状态。
  • 该工具应该在运行时显示并给任务分配状态。

让我们一步一步地构建pyDag。

1.选择基础设施。谷歌云平台

尽管这个库是为接受任何类型的云提供商或内部基础设施而建立的,但在这种情况下,我们将使用谷歌云平台作为云提供商,我们将创建三个层。

  • 一层是作为摄取层,专门用于大数据工作负载,这样我们可以将数据从任何外部数据源转移到我们的暂存区,在这种情况下,我们将使用Apache Spark集群,使用Google Cloud Dataproc。另一个选择是使用谷歌云函数作为数据摄取层,但如果我们需要在将数据移动到中转区之前进行先前的转换呢?在这种情况下,谷歌云函数与谷歌云Dataproc相比有一个非常明显的缺点, 谷歌云Dataproc的另一个优点是它可以使用各种外部数据源。如果你选择内部基础设施,Apache livy可能是另一个选择。

1.谷歌云Dataproc,2.Apache Livy用于企业内部基础设施,谷歌云功能被抛弃用于数据摄取

  • 一层,将作为BigQuery、分布式数据存储和数据仓库的中转区,所有来自外部的数据将使用摄取层移动到这一层。一切以bigquery为准。

BigQuery

存储在bigquery之上执行的SQL脚本

存储IAC脚本

存储用于从dataproc到bigquery的数据摄入的pySpark脚本

存储启动到dataproc集群的作业的输出日志

▹将临时数据从dataproc作业中转移到bigquery中,存储在一个 temporaryGcsBucket 桶中。

pyDag的默认谷歌云平台基础设施

2.DAG数据结构

这一步包括创建一个对象类,其中包含图的结构和一些方法,如向图中添加顶点(任务),创建顶点(任务)之间的边(依赖关系),并执行基本的验证,如检测图何时产生循环。

Dag数据结构

3.拓扑排序和并行执行

这是一个有趣的部分,考虑调度任务的问题,这些任务之间有依赖关系,我们假设任务 "sendOrders "只有在任务 "getProviders "和 "getItems "成功完成后才能完成。我们可以用一个DAG来获得这种依赖关系,这个DAG包含一个边 "getProviders"->"sendOrders "和边 "getItems"->"sendOrders",因此,通过使用上述例子,拓扑排序算法会给我们一个顺序,这些任务可以在尊重它们和它们的依赖关系的正确顺序下一步一步地完成。

拓扑排序、并行处理器执行器

因此,拓扑排序算法将是pyDag类中的一个方法,它将被称为 "run",这个算法在每一步都将提供可以并行执行的下一个任务。关于这种技术有很多研究,但我将采取最快速的解决方案,即对DAG进行拓扑排序。

使用4个处理器的DAG,需要三个步骤来完成DAG

我将使用多处理技术来并行执行较少或相等数量的任务。假设最初在拓扑排序算法的第一次迭代中,有一些不相干的任务可以并行执行,这个数字可能大于计算机中可用的处理器数量,ParallelProcessor类将能够接受并执行这些任务,只用一个有可用处理器的池,其他任务在下一次迭代中执行。最方便的是向pyDag类发送它可以并行执行的任务数量,这将是可以同时执行的非独立顶点(任务)的数量。

pyDag中的任务实例在多处理器系统中的可能状态

执行者类将帮助我保持状态,并知道DAG中每个任务的当前状态是什么。

4.引擎和处理程序

每个任务都与特定类型的引擎相关联,这样一来,就可以与不同技术实现的任务以及任何云提供商进行交流,但在深入了解之前,让我们解释一下pyDag中任务的基本结构。

pyDag中的任务

正如你在表示DAG的.json文件的结构中所看到的,特别是对于一个任务,该 脚本 属性给了我们关于一个特定任务的所有信息。使用5层信息。

Location.Bucket.Folder.Engine.Script_name

  • ***位置:***它告诉我们脚本存储在什么类型的平台上,默认情况下,最好是把它放在同一类型的云提供商或位置上,不一定是这样的,一些选项可以是:GCS、S3、本地或数据库。
  • Bucket: 如果选择 "本地",那么这一层和下一层将代表文件夹,如果是 "数据库",这一层和下一层将代表数据库名称和数据库中的表。建议使用项目名称作为桶的名称。
  • 文件夹。 它是桶内的一个文件夹,或一个本地文件夹或数据库中的一个表。这里建议使用项目中的一个模块的名称。
  • 发动机。它告诉我们下一级 "Script_name"将使用什么类型的引擎,例如,如果我的引擎=spark,这意味着脚本在Dataproc集群上执行,如果我的引擎=bq**,这意味着我的脚本将在BigQuery中执行,如果我的引擎=iac,这意味着我的脚本将在选定的云平台上提供基础设施。
  • 脚本名称。 脚本名称或文件

例子

"脚本" : "gcs.project-pydag.iac_scripts.iac.dataproc_create_cluster"

被称为 "dataproc_create_cluster "的脚本被托管在GCS的 "project-pydag"桶内的文件夹中。"iac_scripts",它的引擎是。"iac",这个处理和设置云中的信息结构。

"脚本" : "gcs.project-pydag.module_name.spark.csv_gcs_to_bq"

被称为 "csv_gcs_to_bq"的脚本托管在GCS的 "module_name"文件夹下的 "project-pydag"桶中,其引擎为 "spark",这意味着该脚本将在Dataproc集群中执行。

"脚本" : "gcs.project-pydag.module_name.bq.create_table"

被称为 "create_table"的脚本托管在GCS中,位于 "module_name"文件夹下的 "project-pydag"桶中,其引擎为。"bq",这意味着该脚本将在BigQuery集群中执行。

运行任务的过程是完全动态的,并基于以下步骤。

EngineHandler, ScriptHandler, Engine

  • 每次试图运行一个任务时,pyDag 都会调用属于 "Engine"类的继承方法 "run",这个方法在内部使用另外两个继承方法,来自 "script_handler"类的 "format_script"和来自 "engine_handler"类的 "run_script"。
  • 首先,"format_script"方法从它的位置上获得脚本,并返回脚本接收好格式化的参数,以及一个引擎类的对象。
  • 一旦上一步的数据被返回,"engine_handler"的 "run_script"方法将负责动态地创建引擎,它将针对收到的引擎调用 "run_script"方法,以这种方式我们避免向库中添加更多的代码,我们只专注于设计新的引擎,我们有灵活性,一个任务可以使用云上或内部的任何技术类型。

这种方式在未来可能会引起安全问题,但在下一个版本中,我将对其进行改进。

引擎是你应该添加到pyDag中的客户端应用程序,为了给你的任务提供你想要的技术,添加一个新引擎的步骤是在config.cfg文件中添加你的引擎,并在 "clientclass.py"中添加一个名为 "run_script"的方法,负责接收脚本的名称或脚本字符串。

三个负责与基础设施互动的引擎。BQClientIACClientDPClient

默认情况下,pyDag提供三种类型的引擎。

  • BQClient:它是接收任何sql脚本并针对BigQuery执行的客户端。
  • DPClient。它是Google Cloud Dataproc客户端,它接收一个pyspark脚本及其所有相关参数,并针对你在参数中指出的集群执行它。
  • IACClient: 这是我发明的一个客户端,所以,有些任务只能作为云中基础设施的构建者,例如:创建一个dataproc集群,删除一个dataproc集群或停止一个dataproc集群。

一个很好的练习是创建一个谷歌云函数引擎,这样你可以创建只在云中执行Python代码的任务。

5.通过将流量保持在本地来减少延时

如果一个DAG有10个任务,在生产中每天运行4次,这意味着我们将在一天内获取40次字符串脚本,只是为了一个DAG,现在如果你的业务或企业运营有10个DAG,以不同的时间间隔运行,每个DAG平均有10个任务怎么办?会有很多不必要的请求到你的GCS桶,造成成本并增加任务的执行时间,不必要的请求可以使用redis进行本地缓存。script_handler"类将负责保持脚本的缓存, 使用redis进行缓存

6. pyDag在多处理器机器上的架构。

正如我们所看到的,pyDag 类的一个对象包含了上面提到的所有内容,架构几乎已经准备就绪。下面我将向你展示该架构的整体概况。

7.让我们来运行一个例子

GCP - JSON中的API凭证

  • 转到谷歌云控制台
  • 创建一个新的项目

按照本视频中的步骤,在Json中创建Api凭证。

在Json中创建Api凭证

GCP - BigQuery

  • 转到BigQuery
  • 创建一个新的数据集,称为。 数据测试

BigQuery数据集。 数据测试

GCP - Dataproc

  • 转到Dataproc
  • 现在点击启用API

本地机器

  • 安装 Windows上的Docker桌面,它也将安装Docker Compose,Docker Compose将允许你运行多个容器应用程序。
  • 安装 git-bash,用于Windows,一旦安装完毕,打开git bash并下载这个资源库,这将下载 docker-compose.yaml文件,以及其他需要的文件。
ramse@DESKTOP-K6K6E5A MINGW64 /c$ git clone https://github.com/Wittline/pyDag.git
复制代码
  • 一旦所有需要的文件都从版本库中下载,让我们来运行一切。我们将再次使用git bash工具,进入pyDag文件夹,运行Docker Compose命令。
ramse@DESKTOP-K6K6E5A MINGW64 /c$ cd pyDag
复制代码
  • 进入logs文件夹,查看输出结果

让我们解释一下这个例子

在这个例子中,有很多DAG的配置,最合适也是最短的是下图所示的第二种方法,我放弃了第一种方法,两种方法都达到了相同的目标,但是,第二种方法有更多的机会利用并行性,改善整体延迟。

DAG第一种方法

这个例子只是为了证明这个工具可以达到不同的粒度水平,这个例子可以用更少的步骤来建立,实际上是用一个针对BigQuery的单一查询,但这是一个非常简单的例子,可以看到它是如何工作的。

达格第二方法

  • startup_dataproc_1:在GCP中创建一个Dataproc集群,名称为:"cluster-dataproc-pydag-2022"。

dataproc_create_cluster.iac

  • create_table_final:在 BigQuery中创建最终表 "mytable3**"** 这里 ,我们要选择和清理的数据。

create_table.sql

  • create_table_stg_1: 在BigQuery中创建表 ***"***mytable1" 在BigQuery中。

create_table_stg.sql

  • create_table_stg_2: 在BigQuery中创建表 ***"***mytable2" 在BigQuery中。

create_table_stg.sql

  • initial_ingestion_1: 这个任务将把数据从存储在谷歌云存储的CSV文件转移到BigQuery,这个脚本将在已经创建的Dataproc集群中执行,任务是:"startup_dataproc_1"。在这种情况下,Dataproc集群将作为一个摄入层工作,目标表被称为 "table_stg"。

csv_gcs_to_bq.py

  • extract_from_stg_1:这个任务将把数据从 "table_stg"移到 "mytable1",只取具有以下条件的记录 'year':'2022'和'category':'carls'。

extract_from_stg.sql

  • extract_from_stg_2: 这个任务将把数据从 "table_stg"转移到 "mytable2",只接收具有以下条件的记录**。** 'year':'2021' and 'category':'food'.

extract_from_stg.sql

  • insert_to_fact: 这个任务将使用UNION ALL将数据从 "mytable1"和 "mytable2"填充到 "mytable3"

insert_to_fact.sql

希望得到的结果

日志

记住为这个例子添加任务,以关闭谷歌Dataproc集群,并放弃你在BigQuery中不再使用的暂存表。

接下来的步骤

为了拥有一个可接受的产品,并具有最低限度的所需功能,我将努力增加以下内容。

  1. 集中的日志
  2. 一个元数据数据库
  3. 多台机器上的分布式任务执行
  4. 分支
  5. 使用API REST将DAG作为一项服务。
  6. 带有拖放组件的GUI

结论

你可以清楚地看到,在所有情况下,有两个任务需要很长时间才能完成,"startup_dataproc_1"和 "initial_ingestion_1"都与Google DataProc的使用有关,避免使用在DataProc中创建集群的任务的一个方法是保持一个已经创建的集群,并保持它打开等待任务,与水平扩展,这是强烈建议有高工作负载的公司通过提交任务,不会有浪费和时间及资源的差距。

你可以看到执行中缓存的效果。 短任务更短 在开启缓存的情况下。

甘特图。pyDag中带有多处理器和缓存的DAG行为

尽管可以确认任务执行中的并行性*,但* 我们可以指定一个 每个DAG有固定数量的处理器,这代表了在DAG中可以并行执行的最大任务数,或 最大程度的并行化但这意味着有时会有处理器被浪费,避免这种情况的方法之一是分配一个 动态的处理器数量,它只适应于当前需要执行的任务数量,通过这种方式可以在一台机器上执行多个DAGS,并利用没有被其他DAGS使用的处理器的优势。上述图表的唯一问题是,这些结果来自于每种情况下的一次执行,应该对每种情况进行多次执行,并对每种情况下的平均时间进行计算,但我没有足够的预算来进行这种测试,代码仍然是非常非正式的,它还没有准备好用于生产,我将在这些细节上努力,以便发布一个更稳定的版本。

查看我的GitHub仓库pyDag,了解更多项目信息。

猜你喜欢

转载自juejin.im/post/7106830117220909069