了解Spark 应用如何执行,看懂这篇就够了

核心概念讲解

Spark 应用的架构

Driver

Driver 是一个Spark Application架构中最重要的一个进程。为了便于理解,我们可以将Driver理解为是一个建筑工地的包工头,他了解整个项目的实施步骤与实施内容。它主要负责分派任务,监督任务的进程,同时还需要时刻关心项目需要的建筑材料是否充分。它需要监视的情况也会随着Spark Application所跑的模式的不同而有所不同。不同的模式的区别我们会在后面进行讲解,现在只要了解Driver Process是Spark 应用中最为重要的核心主线程就可以了。

Executor

Executor 是另外的执行任务的进程。这些任务都由Driver来指定。Executor可以被理解为包工头下面的工人,他们不需要太多思考,而是专注于完成指定的任务。他们只需要接受任务,执行,并将执行后的状态与结果返回即可。

Cluster Manager

Cluster Manager的概念比以上的概念稍微较难理解一些,Cluster Manager搭建了Spark Application与具体的物理机器的桥梁。如果将Spark Application理解为一个施工项目的话,那么Cluster Mangager可以理解为是一个建筑材料管理者。一个施工项目的完成需要提供足够的建材支持执行,而当需要这些资源的时候,便需要与Cluster Manager交流。当然这只是为了便于理解而做的类比。真正的Cluster Manager中也会有自己的结构,也是一个Master/Workor的结构。这样的结构是为了更好的管理分布式的集群的物理机器。我们可以通过下图对Cluster Mangager有一个了解。现在Spark支持Standalone, Mesos, Yarn 三种方式,K8s会在Spark3之后支持。


Cluster Manager 结构

Spark 执行模式

在理解了一个Spark Application里面的组成部分之后,我们来看看Spark Application能够执行的模式

Cluster Mode

Cluster Mode是Spark Application最常用的模式。在生产环境除非特殊原因都会采用Cluster的模式来执行。在Cluster mode下,用户提交一个Jar或者pyton script,R sciprt给Cluster Manager。之后Cluster Manager在某一个Workor节点上启动Driver的进程。在Cluster Mode下,Cluster manager负责维持所有Spark应用进程。下图便解释后Cluster mode执行时的状态。Cluster Manager选取一个Workor节点跑Driver,之后再分配其他Worker跑其余的Executor。


Cluster Mode

Client Mode

Client Mode与Cluster Mode基本一致除了Driver进程并非由Cluster Manager来管理,而是继续跑在提交任务的机器上。这也就意味着,Driver进程将由提交任务的客户端来维持状态。Cluster Manager只是负责维持Executor的状态。这些维持Driver的节点,也被叫做Edge Node或者gateway Machines。下图便解释了Client Mode的运行机制。

Client Mode

Local Mode

Local Mode是一种为了测试与开发Spark应用而存在的模式。它与前两种截然不同。Local模式在一台物理机器上运行,同时Executor与Driver也从进程变为线程。在生产环境中几乎没有不会使用local 模式。

Spark 应用的执行过程

了解了Spark系统的组成部分,我们来看一个在Cluster Mode下跑的Spark Application的过程。

任务请求

我们一般通过Spark-submit来提交一个Spark 应用,提交的时候需要提供已经编译好的Jar包。或者是python script等这取决于不同的语言而不同。


任务请求

通过上图可以看到,执行这项任务的进程还是在你自己本地的机器上,它需要负责给Cluster Manager发送请求,表明需要的资源大小。我们现在假设我们有足够的资源,因此Cluster Manager接受了我们的请求,并寻找了一个Worker Node,将Driver跑起来。如果一切顺利,那么此本地进程就会结束退出。
以下是一个任务提交的示例

./bin/spark-submit \ 
--class  \ 
--master  \ 
--deploy-mode cluster \ 
--conf = \ 
... # other options 
 \ 
[application-arguments]

任务启动

当Driver进程开始之后,Driver会拥有一个SparkSession的对象。SparkSession定义了运行这个Spark Application所需要的环境信息。这包含了用多少资源,与Cluster Manager的沟通方式等。这些大部分都是在Spark-submit的时候可以进行设置的内容。


任务启动

因此在这个过程中,Driver通过SparkSession这个对象,向Cluster Manager请求资源并建立起这个任务需要的集群,这样一个可以用于执行Spark Application的集群遍准备好了。这里需要注意,当Cluster Manager在分配了资源之后,它会将这些信息给到Driver,之后Driver便可以直接与这些Executor交流。之后除非有新的资源请求,不然就不用再找Cluster Manager了。

任务执行

执行阶段我们之后会将其中涉及的概念与过程详细展开。现在我们只需要知道这时候Driver已经掌握了足够的信息来执行应用了。所以会开始分配executor到不同的Worker node上去。


任务执行

我们需要注意这时候整体的控制权都是Driver手里,Cluster Manager依然维持着Driver的进程的状态。

任务完成

Driver的退出便标志着应用的完成,Driver退出时的状态也会同时传给Cluster Manager。这时候Cluster Manager会负责将之前分配的资源回收,并将Driver返回的状态返回。


任务完成

Spark 应用内部的过程

接下来我们仔细看看Spark Application在执行的阶段内部都在发生着什么。为了能够清楚的讲清楚其中的过程,我们需要先交代一些Spark Application中的新的概念。

Spark Session

在Spark应用的一开始便是需要获取一个Spark Session.如果是使用Spark-Shell,或者Zepplion等方式的,你可能没有意识到这个Session已经在你启动Shell的时候已经获得了。由于Spark版本的不同,初始化Spark Session的方式也经历了很多的变化,由于我们旨在讲明白Spark Application的内部运行流程,就不在Spark Session的方面进行过多的描述。我们只需要了解我们需要获取SparkSession,之后才能获得不同的Context对象来进行Spark Application的开发。我们可以调用SparkSQL的服务,也可以是底层的RDD的服务。这些都由不同的业务需求而确定。

Logical Instructions

无论你使用什么语言进行Spark Application的开发,最终都会转化为一个Logical Instruction的计划,然后再变为一个物理执行计划。如果你想要更加直观的了解执行计划,你可以打开Spark的UI。这时候我们便需要介绍一下Spark Job的组成了。
在介绍Spark的Job组成之前,我们需要再引入两个概念,一个是transformation,一个是shuffle。这对应着在Spark中两种不同的数据变换逻辑。如果对Map-Reduce有了解的话那么就不难理解,Transformation可以理解为Mapper能够完成的逻辑,而Shuffle就是需要Reducer才能完成的逻辑。我们之后会将为什么这两类运算对于Spark的计划来说非常的重要。

Spark Job

Spark Job在Spark Application中对应的就是一个Action的操作。这时又需要引入Spark的Lazy Evaluation的机制。Spark只有在需要获取结果的时候才会进行运算,在这之前只会保留执行的计划而不会调用任何计算的逻辑。
一个Job是有很多的Stage组成的,而一个Stage会包含若干个Task。

Spark Stage

一个Stage可以理解为是一组能够被并行执行的Task的集合,一个Shuffle的操作对应一个Stage。
Spark之所以执行的速度比Map-reduce要优秀,就是因为它能够做良好的优化,能够合理的将一个计算任务进行整合。举个例子如果你的操作都是select, filter等,那么他们会被整合在一个Stage当中去完成。而整合的极限便是如果你需要做不同的Shuffle的操作,比如Sort, Grouping。那么Spark便会分开不同的Stage来执行。

Spark Tasks

一个Task就是Spark执行中的最小单元,它跑在一台机器上。有多少的Task需要被执行,是有多少的partition来决定的。这里不做过多的赘述。我们只要理解Task是最小的执行单元便可以。

一些执行的细节

  1. Pipelining
    Pipeling是Spark Application中最具特色的部分,大部分select, filter的操作都会通过pipeling的方式放在一个Stage中执行。在这样的执行中会尽量的使用内存而非存储,也正是因为这样的优化使得Spark在执行很多相同的任务中有着更好的表现。

  2. Shuffle Persistence
    当Spark需要做一个Shuffle的作业时,Spark会先将输入落盘。这样的选择不仅是为了以后做shuffle时的方便同时它还带来了一个额外的好处,那便是因为shuffle之前的内容已经落盘,所以如果重新执行这个任务的话,已经落盘的部分会自动的跳过。

总结

我们分别讲解了Spark集群的架构,一个Spark Application如何利用分布式的架构执行,以及在执行的过程中Spark是如何处理内部的应用的。略显粗浅,但基本涵盖了Spark 应用的方方面面。可以算是Spark应用理解的启蒙读物吧。

Reference

Spark 权威指南

你可能感兴趣的:(了解Spark 应用如何执行,看懂这篇就够了)