大数据技术之Spark内核解析(二)

大数据技术之Spark内核解析(二)

一:Spark的通讯框架

  1. Spark通讯框架的概述

    Spark2.x 版本使用 Netty 通讯框架作为内部通讯组件。 spark 基于 netty 新的 rpc
    框架借鉴了 Akka 的中的设计,它是基于 Actor 模型, 如下图所示:

在这里插入图片描述

  1. 各组件的关系图

    ​ Spark 通讯框架中各个组件( Client/Master/Worker) 可以认为是一个个独立的实体,各个实体之间通过消息来进行通信。具体各个组件之间的关系图如下:

    在这里插入图片描述

    Endpoint( Client/Master/Worker)有 1 个 InBox 和 N 个 OutBox( N>=1, N 取决于当前 Endpoint 与多少其他的 Endpoint 进行通信,一个与其通讯的其他 Endpoint对应一个 OutBox), Endpoint 接收到的消息被写入 InBox,发送出去的消息写入OutBox 并被发送到其他 Endpoint 的 InBox 中。

  2. Spark的通讯框架解析

在这里插入图片描述

  1. RpcEndpoint: RPC 端点, Spark 针对每个节点( Client/Master/Worker)都称之为一个 Rpc 端点,且都实现 RpcEndpoint 接口,内部根据不同端点的需求,设计不同的消息和不同的业务处理,如果需要发送(询问)则调用 Dispatcher;
  2. RpcEnv: RPC 上下文环境,每个 RPC 端点运行时依赖的上下文环境称为RpcEnv;
  3. Dispatcher:消息分发器,针对于 RPC 端点需要发送消息或者从远程 RPC接收到的消息,分发至对应的指令收件箱/发件箱。如果指令接收方是自己则存入收件箱,如果指令接收方不是自己,则放入发件箱;
  4. Inbox : 指 令 消 息 收 件 箱 , 一 个 本 地 RpcEndpoint 对 应 一 个 收 件 箱 ,Dispatcher 在 每 次 向 Inbox 存 入 消 息 时 , 都 将 对 应 EndpointData 加 入 内 部ReceiverQueue 中 , 另 外 Dispatcher 创 建 时 会 启 动 一 个 单 独 线 程 进 行 轮 询ReceiverQueue,进行收件箱消息消费;
  5. RpcEndpointRef: RpcEndpointRef 是对远程 RpcEndpoint 的一个引用。当我们需要向一个具体的 RpcEndpoint 发送消息时,一般我们需要获取到该 RpcEndpoint的引用,然后通过该应用发送消息。
  6. OutBox : 指 令 消 息 发 件 箱 , 对 于 当 前 RpcEndpoint 来 说 , 一 个 目 标RpcEndpoint 对应一个发件箱,如果向多个目标 RpcEndpoint 发送信息,则有多个OutBox。当消息放入 Outbox 后,紧接着通过 TransportClient 将消息发送出去。消息放入发件箱以及发送过程是在同一个线程中进行;
  7. RpcAddress: 表示远程的 RpcEndpointRef 的地址, Host + Port。
  8. TransportClient : Netty 通 信 客 户 端 , 一 个 OutBox 对 应 一 个TransportClient, TransportClient 不断轮询 OutBox,根据 OutBox 消息的 receiver 信 息,请求对应的远程 TransportServer;
  9. TransportServer : Netty 通 信 服 务 端 , 一 个 RpcEndpoint 对 应 一 个TransportServer,接受远程消息后调用 Dispatcher 分发消息至对应收发件箱;

二:Spark任务调度机制

​ 在工厂环境下, Spark 集群的部署方式一般为 YARN-Cluster 模式, 之后的内核分析内容中我们默认集群的部署方式为 YARN-Cluster 模式

  1. Spark任务提交和运行流程时序图
    在这里插入图片描述
    ​ 提交一个 Spark 应用程序,首先通过 Client 向 ResourceManager 请求启动一个
    Application,同时检查是否有足够的资源满足 Application 的需求,如果资源条件满
    足,则准备 ApplicationMaster 的启动上下文,交给 ResourceManager,并循环监控
    Application 状态。

    ​ 当提交的资源队列中有资源时, ResourceManager 会在某个 NodeManager 上启动 ApplicationMaster 进程, ApplicationMaster 会单独启动 Driver 后台线程 ,当Driver 启 动 后 , ApplicationMaster 会 通 过 本 地 的 RPC 连 接 Driver, 并 开 始 向ResourceManager 申请 Container 资源运行 Executor 进程(一个 Executor 对应一个Container),当 ResourceManager 返回 Container 资源, ApplicationMaster 则在对应的 Container 上启动 Executor。
    ​ Driver 线程主要是初始化 SparkContext 对象,准备运行所需的上下文,然后一方面保持与 ApplicationMaster 的 RPC 连接,通过 ApplicationMaster 申请资源,另一方面根据用户业务逻辑开始调度任务,将任务下发到已有的空闲 Executor 上。
    ​ 当 ResourceManager 向 ApplicationMaster 返 回 Container 资 源 时 ,ApplicationMaster 就尝试在对应的 Container 上启动 Executor 进程, Executor 进程起
    来后,会向 Driver 反向注册,注册成功后保持与 Driver 的心跳,同时等待 Driver分发任务,当分发的任务执行完毕后,将任务状态上报给 Driver。
    ​ 从上述时序图可知, Client 只负责提交 Application 并监控 Application 的状态。对于 Spark 的任务调度主要是集中在两个方面: 资源申请和任务分发,其主要是通过 ApplicationMaster、 Driver 以及 Executor 之间来完成。

  2. Yarn集群的用户提交任务执行spark-submit源码分析时序图
    在这里插入图片描述

     当bin/Spark-Submit命令提交后,开启主进程main,main进程会创建一个SparkSubmit的函数来执行doSubmit方法,之后创建appArgs然后SparkSubmitArguments解析命令行的参数返回给SparkSubmit的appArgs,submit会启动runMain来创建一个org.apache.spark.yarn.YarnClusterApplication,通过这个类来进行执行app.start,开启app,开启后会创建YarnClusterApplication来new Client,创建一个Client执行run方法,来运行job任务,yarn.Client客户端通过run方法创建submitApplication方法,在submitApplication方法中通过createContainerLaunchContext来判断Spark运行模式,最后通过createApplicationSubmissionContext来设置AM提交的上下文。
    
  3. Yarn集群的任务的提交和运行源码时序图

在这里插入图片描述
4. Spark的任务调度概
当 Driver 起来后, Driver 则会根据用户程序逻辑准备任务,并根据 Executor 资
源情况逐步分发任务。在详细阐述任务调度前,首先说明下 Spark 里的几个概念。
一个 Spark 应用程序包括 Job、 Stage 以及 Task 三个概念:
⚫ Job 是以 Action 方法为界,遇到一个 Action 方法则触发一个 Job;
⚫ Stage 是 Job 的子集,以 RDD 宽依赖(即 Shuffle)为界,遇到 Shuffle 做一次
划分;
⚫ Task 是 Stage 的子集,以并行度(分区数)来衡量,分区数是多少,则有多少
个 task。
Spark 的任务调度总体来说包括 Stage 级的调度和 Task 级的调度,总体调度程如下图所示:
![在这里插入图片描述](https://img-blog.csdnimg.cn/20200212143324251.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl8zODI1NTQ0NA==,size_16,color_FFFFFF,t_70
​ SparkRDD通过其Transactions操作,形成了RDD血缘关系图,即DAG,最后通过Action的调用,触发Job并调度执行。DAGScheduler负责Stage级的调度,主要是将job切分成若干Stages,并将每个Stage打包成TaskSet交给TaskScheduler调度。TaskScheduler负责Task级的调度,将DAGScheduler给过来的TaskSet按照指定的调度策略分发到Executor上执行,调度过程中SchedulerBackend负责提供可用资源,其中SchedulerBackend有多种实现,分别对接不同的资源管理系统。

  1. Spark调度模块间交互

在这里插入图片描述
​ Driver初始化SparkContext过程中,会分别初始化DAGScheduler、TaskScheduler、SchedulerBackend以及HeartbeatReceiver,并启动SchedulerBackend以及HeartbeatReceiver。SchedulerBackend通过ApplicationMaster申请资源,并不断从TaskScheduler中拿到合适的Task分发到Executor执行。HeartbeatReceiver负责接收Executor的心跳信息,监控Executor的存活状况,并通知到TaskScheduler
6. Spark Stage调
在这里插入图片描述

   ​	Job由最终的RDD和Action方法封装而成,SparkContext将Job交给DAGScheduler提交,它会根据RDD的血缘关系构成的DAG进行切分,将一个Job划分为若干Stages,具体划分策略是,由最终的RDD不断通过依赖回溯判断父依赖是否是宽依赖,即以Shuffle为界,划分Stage,窄依赖的RDD之间被划分到同一个Stage中,可以进行pipeline式的计算,如上图紫色流程部分。划分的Stages分两类,一类叫做ResultStage,为DAG最下游的Stage,由Action方法决定,另一类叫做ShuffleMapStage,为下游Stage准备数据
  1. Spark Task调
    在这里插入图片描述
    ​ Spark Task调度策略:FIFO和FAIR

  2. Spark Task失败重试的黑名单机制

    ​ Task被提交到Executor启动执行后,Executor会将执行状态上报给SchedulerBackend,SchedulerBackend则告诉TaskScheduler,TaskScheduler找到该Task对应的TaskSetManager,并通知到该TaskSetManager,这样TaskSetManager就知道Task的失败与成功状态,对于失败的Task,会记录它失败的次数,如果失败次数还没有超过最大重试次数,那么就把它放回待调度的Task池子中,否则整个Application失败。

    ​ 在记录Task失败次数过程中,会记录它上一次失败所在的Executor Id和Host,这样下次再调度这个Task时,会使用黑名单机制,避免它被调度到上一次失败的节点上,起到一定的容错作用。黑名单记录Task上一次失败所在的Executor Id和Host,以及其对应的“拉黑”时间,“拉黑”时间是指这段时间内不要再往这个节点上调度这个Task了。

三:Spark Shuffle解析

  1. 核心要点

    在划分stage时,最后一个stage称为finalStage,它本质上是一个ResultStage对象,前面的所有stage被称为ShuffleMapStage。ShuffleMapStage的结束伴随着shuffle文件的写磁盘。ResultStage基本上对应代码中的action算子,即将一个函数应用在RDD的各个partition的数据集上,意味着一个job的运行结束。

    1. HashShuffle(了解)

    2. SortShuffle(了解)

    3. Spark Shuffle中的任务个数

四:Spark内存管理

​ 在执行 Spark 的应用程序时 , Spark 集群会启动 Driver 和 Executor 两种
JVM 进程,前者为主控进程,负责创建 Spark 上下文,提交 Spark 作业( Job),
并将作业转化为计算任务( Task),在各个 Executor 进程间协调任务的调度,后
者负责在工作节点上执行具体的计算任务,并将结果返回给 Driver,同时为需要
持久化的 RDD 提供存储功能。由于 Driver 的内存管理相对来说较为简单,本节
主要对 Executor 的内存管理进行分析,下文中的 Spark 内存均特指 Executor 的
内存。

  1. 堆内和堆外内存

    作为一个 JVM 进程, Executor 的内存管理建立在 JVM 的内存管理之上, Spark 对
    JVM 的堆内(On-heap)空间进行了更为详细的分配,以充分利用内存。同时, Spark 引入
    了堆外(Off-heap)内存,使之可以直接在工作节点的系统内存中开辟空间,进一步优化了
    内存的使用。
    堆内内存受到 JVM 统一管理,堆外内存是直接向操作系统进行内存的申请和释放。

  2. 空间分配

  3. 存储内存和执行内存管理

七:Spark核心组件解析

  1. BlockManager数据存储与管理

    ​ BlockManager 是整个 Spark 底层负责数据存储与管理的一个组件, Driver 和
    Executor 的所有数据都由对应的 BlockManager 进行管理。
    ​ Driver 上有 BlockManagerMaster,负责对各个节点上的 BlockManager 内部管理
    的数据的元数据进行维护, 比如 block 的增删改等操作,都会在这里维护好元数据
    的变更。
    ​ 每个节点都有一个 BlockManager,每个 BlockManager 创建之后,第一件事即
    使去向 BlockManagerMaster 进行注册,此时 BlockManagerMaster 会为其创建对应的
    BlockManagerInfo。

    ​ BlockManagerMaster 与 BlockManager 的关系非常像 NameNode 与 DataNode 的
    关系, BlockManagerMaster 中保存中 BlockManager 内部管理数据的元数据,进行维
    护,当 BlockManager 进行 Block 增删改等操作时,都会在 BlockManagerMaster 中进
    行元数据的变更,这与 NameNode 维护 DataNode 的元数据信息, DataNode 中数据
    发生变化时 NameNode 中的元数据信息也会相应变化是一致的。

    1. Spark共享变量底层实现

    Spark 一个非常重要的特性就是共享变量。
    默认情况下,如果在一个算子的函数中使用到了某个外部的变量,那么这个变量的值会被拷贝到每个 task 中,此时每个 task 只能操作自己的那份变量副本。如果多个 task 想要共享某个变量,那么这种方式是做不到的。
    Spark 为此提供了两种共享变量,一种是 Broadcast Variable( 广播变量),另一种是 Accumulator( 累加变量)。 Broadcast Variable 会将用到的变量, 仅仅为每个节点拷贝一份, 即每个 Executor 拷贝一份, 更大的用途是优化性能,减少网络传输以及内存损耗。 Accumulator 则可以让多个 task 共同操作一份变量,主要可以进行累加操作。 Broadcast Variable 是共享读变量, task 不能去修改它,而 Accumulator 可以让多个 task 操作一个变量。

    1. 广播变量
    2. 累加器

是优化性能,减少网络传输以及内存损耗。 Accumulator 则可以让多个 task 共同操作一份变量,主要可以进行累加操作。 Broadcast Variable 是共享读变量, task 不能去修改它,而 Accumulator 可以让多个 task 操作一个变量。

1. 广播变量
2. 累加器
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值