Spark底层原理:案例解析(第34天)

系列文章目录

一、Spark架构设计概述
二、Spark核心组件
三、Spark架构设计举例分析
四、Job调度流程详解
五、Spark交互流程详解

文章目录

  • 系列文章目录
  • 前言
  • 一、Spark架构设计概述
    • 1. 集群资源管理器(Cluster Manager)
    • 2. 工作节点(Worker Node)
    • 3. 驱动程序(Driver Program/Driver)
  • 二、Spark核心组件
    • 1. Spark Core
    • 2. Spark SQL
    • 3. Spark Streaming
    • 4. Spark MLlib
    • 5. Spark GraphX
  • 三、Spark架构设计举例分析
  • 四、Job调度流程详解
  • 五、Spark交互流程详解
    • 1、client_Spark集群
    • 2、cluster_Spark集群
    • 3、client on Yarn集群
    • 4、cluster on Yarn集群


前言

Apache Spark是一个快速、通用、基于内存的分布式计算引擎,专为大规模数据处理而设计。其架构设计体现了高度的模块化和可扩展性,支持多种数据处理模式,包括批处理、实时流处理、交互式查询、机器学习和图计算等。以下将详细介绍Spark的架构设计,并结合具体例子进行分析。


一、Spark架构设计概述

Spark的架构设计遵循主从(Master-Slave)架构模式,主要由以下几部分组成:

1. 集群资源管理器(Cluster Manager)

  • 负责集群资源的分配和管理,包括CPU、内存等资源。根据不同的部署模式,Cluster Manager可以是Spark自带的Standalone模式,也可以是YARN、Mesos等第三方资源管理器。

2. 工作节点(Worker Node)

  • 执行提交的任务,通过注册机制向Cluster Manager汇报自身的资源使用情况。在Master的指示下,Worker Node会创建并启动Executor进程,用于执行具体的计算任务。

3. 驱动程序(Driver Program/Driver)

  • 运行应用程序的main()函数,负责创建SparkContext对象,并与Cluster Manager和Executor进行通信,以协调任务的执行。
  • 执行器(Executor):运行在Worker Node上的进程,负责执行Driver分配的任务,并将结果返回给Driver。Executor是Spark中真正的计算单元,它负责Task的运行并将结果数据保存到内存或磁盘上。

二、Spark核心组件

Spark基于Spark Core建立了多个核心组件,每个组件都提供了特定的数据处理能力:

1. Spark Core

  • 基础设施:包括SparkConf(配置信息)、SparkContext(Spark上下文)、Spark RPC(远程过程调用)、ListenerBus(事件总线)、MetricsSystem(度量系统)、SparkEnv(环境变量)等,为Spark的各种组件提供基础支持。
  • 存储系统:Spark的存储系统优先考虑在内存中存储数据,当内存不足时才会将数据写入磁盘。这种内存优先的存储策略使得Spark在处理大规模数据时具有极高的性能。
  • 调度系统:由DAGScheduler和TaskScheduler组成,负责任务的调度和执行。DAGScheduler负责将用户程序转换为DAG图,并根据依赖关系划分Stage和Task;TaskScheduler则负责按照调度算法对Task进行批量调度。
  • 计算引擎:由内存管理器、任务管理器、Task Shuffle管理器等组成,负责具体的计算任务执行。

2. Spark SQL

  • 提供基于SQL的数据处理方式,支持结构化数据的查询和分析。Spark SQL可以将结构化数据(如JSON、CSV、Parquet等)转换为RDD或DataFrame,并支持使用Hive元数据和SQL查询。

3. Spark Streaming

  • 提供流处理能力,支持从Kafka、Flume、Kinesis、TCP等多种数据源实时获取数据流,并将其转换为可供分析和存储的批处理数据。Spark Streaming使用DStream(离散流)作为数据流的抽象,并支持一系列的转换操作。

4. Spark MLlib

  • 提供机器学习库,包括统计、分类、回归、聚类等多种机器学习算法的实现。Spark MLlib的分布式计算能力使得在大规模数据上进行机器学习任务成为可能。

5. Spark GraphX

  • 提供图计算库,支持对大规模图结构数据进行处理和分析。GraphX通过Pregel提供的API可以快速解决图计算中的常见问题,如社交网络分析、网络拓扑分析等。

三、Spark架构设计举例分析

  • 以Spark Standalone模式为例,我们可以详细分析Spark的架构设计如何支持数据处理任务的执行:
  1. 集群启动:
  • 在Standalone模式下,集群由一个主节点(Master)和多个工作节点(Worker)组成。主节点负责管理集群资源并分配任务给工作节点;工作节点则负责执行具体的任务。
  • 集群启动时,Master节点会启动并监听来自Worker节点的注册请求。Worker节点在启动时向Master注册,并报告自身的资源情况(如CPU、内存等)。
  1. 任务提交:
  • 用户通过Driver程序提交Spark作业到集群。Driver程序首先创建SparkContext对象,并连接到Master节点以请求资源。
  • Master节点根据集群的资源情况和作业的资源需求,为Driver分配资源,并启动Executor进程。Executor进程是运行在Worker节点上的,用于执行具体的计算任务。
  1. 任务执行:
  • Driver程序将作业划分为多个Task,并通过Executor的RPC接口将Task发送到Executor上执行。
  • Executor接收到Task后,会在本地启动线程来并行执行Task。执行过程中,Executor会从存储系统(如HDFS)中加载数据,进行计算,并将结果返回给Driver。
    Driver收集所有Executor的执行结果,并进行汇总和处理,最终将结果输出给用户。
  1. 容错与恢复:
  • Spark通过RDD的容错机制来保证数据的可靠性和作业的可恢复性。RDD具有可容错性,当某个节点发生故障

四、Job调度流程详解

在这里插入图片描述

  • 1- Driver进程启动后,底层PY4J创建SparkContext顶级对象。在创建该对象的过程中,还会创建另外两个对象,分别是: DAGScheduler和TaskScheduler
    DAGScheduler: DAG调度器。将Job任务形成DAG有向无环图和划分Stage的阶段
    TaskScheduler: Task调度器。将Task线程分配给到具体的Executor执行

  • 2- 一个Spark程序遇到一个Action算子就会触发产生一个Job任务。SparkContext将Job任务给到DAG调度器,拿到Job任务后,会将Job任务形成DAG有向无环图和划分Stage的阶段。并且会确定每个Stage阶段有多少个Task线程,会将众多的Task线程放到TaskSet的集合中。DAG调度器将TaskSet集合给到Task调度器

  • 3- Task调度器拿到TaskSet集合以后,将Task分配给到给到具体的Executor执行。底层是基于SchedulerBackend调度队列来实现的。

  • 4- Executor开始执行任务。并且Driver会监控各个Executor的执行状态,直到所有的Executor执行完成,就认为任务运行结束

  • 5- 后续过程和之前一样

五、Spark交互流程详解

1、client_Spark集群

在这里插入图片描述
driver任务: Driver进程中负责资源申请的工作并且负责创建SparkContext对象的代码映射为Java对象,进行创建任务的分配、任务的管理工作。

1- submit提交任务到主节点Master

2- 在提交任务的那个客户端上启动Driver进程

3- Driver进程启动后,执行main函数,首先创建SparkContext对象。底层是基于PY4J,将创建SparkContext对象的代码映射为Java进行创建

4- Driver进程连接到Spark集群中的Master主节点,根据资源配置要求,向主节点申请资源,用来启动Executor

5- 主节点接收到资源申请之后,进行资源分配,底层是基于FIFO(先进先出)。分配好资源资源之后,将方案返回给到Driver进程
executor1:node1 2G 2CPU
executor2:node3 2G 2CPU

6-Driver连接到对应的Worker从节点上,占用相应的资源。通知Worker启动Executor进程。启动以后会反向注册回Driver

7-Driver开始处理代码
7.1- Driver加载RDD相关的算子,根据算子间的依赖关系绘制DAG有向无环图和划分Stage阶段,并且确定每个Stage阶段有多少个Task线程。需要分配给哪些Executor进行执行。
7.2- Driver通知对应的Executor进程来执行相应的任务
7.3- Executor开始执行具体的任务。但是发现代码中有大量的Python函数,而Executor是JVM进程,无法直接执行代码。因此会调用服务器上的Python解释器,将Python函数和输入数据传输给到Python解释器,执行完以后,将结果数据返回给Executor进程
7.4- Executor在运行过程中,会判断是否需要将结果数据返回给到Driver进程。如果需要,就返回给Driver进程;如果不需要,直接输出,结束即可。
7.5- Driver会定时检查多个Executor的执行状态。直到所有的Executor执行完成,就认为任务运行结束

8- Driver调用sc.stop()代码,通知Master回收资源。整个程序运行结束。

2、cluster_Spark集群

在这里插入图片描述
driver任务: Driver进程中负责资源申请的工作并且负责创建SparkContext对象的代码映射为Java对象,进行创建任务的分配、任务的管理工作。

和client on spark集群的区别点: Driver进程不是运行在提交任务的那台机器上了,而是在Spark集群中随机选择一个Worker从节点来启动和运行Driver进程

1- submit提交任务到主节点Master

2- Master主节点接收到任务信息以后,根据Driver的资源配置要求,在集群中随机选择(在资源充沛的众多从节点中随机选择)一个Worker从节点来启动和运行Driver进程

3- Driver进程启动以后,执行main函数,首先创建SparkContext对象。底层是基于PY4J,将创建SparkContext对象的代码映射为Java进行创建

4- Driver进程连接到Spark集群中的Master主节点,根据资源配置要求,向主节点申请资源,用来启动Executor

5- 主节点接收到资源申请之后,进行资源分配,底层是基于FIFO(先进先出)。分配好资源资源之后,将方案返回给到Driver进程
executor1:node1 2G 2CPU
executor2:node3 2G 2CPU

6-Driver连接到对应的Worker从节点上,占用相应的资源。通知Worker启动Executor进程。启动以后会反向注册回Driver

7-Driver开始处理代码
7.1- Driver加载RDD相关的算子,根据算子间的依赖关系绘制DAG有向无环图和划分Stage阶段,并且确定每个Stage阶段有多少个Task线程。需要分配给哪些Executor进行执行。
7.2- Driver通知对应的Executor进程来执行相应的任务
7.3- Executor开始执行具体的任务。但是发现代码中有大量的Python函数,而Executor是JVM进程,无法直接执行代码。因此会调用服务器上的Python解释器,将Python函数和输入数据传输给到Python解释器,执行完以后,将结果数据返回给Executor进程
7.4- Executor在运行过程中,会判断是否需要将结果数据返回给到Driver进程。如果需要,就返回给Driver进程;如果不需要,直接输出,结束即可。
7.5- Driver会定时检查多个Executor的执行状态。直到所有的Executor执行完成,就认为任务运行结束

8- Driver调用sc.stop()代码,通知Master回收资源。整个程序运行结束。

3、client on Yarn集群

在这里插入图片描述
在这里插入图片描述
区别点: 将Driver进程中负责资源申请的工作,转交给到Yarn的ApplicationMaster来负责。Driver负责创建SparkContext对象的代码映射为Java对象,进行创建任务的分配、任务的管理工作。

1- 首先会在提交的节点启动一个Driver进程

2- Driver进程启动以后,执行main函数,首先创建SparkContext对象。底层是基于PY4J,将创建SparkContext对象的代码映射为Java进行创建

3- 连接Yarn集群的主节点(ResourceManager),将需要申请的资源封装为一个任务,提交给到Yarn的主节点。主节点收到任务以后,首先随机选择一个从节点(NodeManager)启动ApplicationMaster

4- 当ApplicationMaster启动之后,会和Yarn的主节点建立心跳机制,告知已经启动成功。启动成功以后,就进行资源的申请工作,将需要申请的资源通过心跳包的形式发送给到主节点。主节点接收到资源申请后,开始进行资源分配工作,底层是基于资源调度器来实现(默认为Capacity容量调度器)。当主节点将资源分配完成以后,等待ApplicationMaster来拉取资源。ApplicationMaster会定时的通过心跳的方式询问主节点是否已经准备好了资源。一旦发现准备好了,就会立即拉取对应的资源信息。

5- ApplicationMaster根据拉取到的资源信息,连接到对应的从节点。占用相应的资源,通知从节点启动Executor进程。从节点启动完Executor之后,会反向注册回Driver进程

6-Driver开始处理代码
6.1- Driver加载RDD相关的算子,根据算子间的依赖关系绘制DAG有向无环图和划分Stage阶段,并且确定每个Stage阶段有多少个Task线程。需要分配给哪些Executor进行执行。
6.2- Driver通知对应的Executor进程来执行相应的任务
6.3- Executor开始执行具体的任务。但是发现代码中有大量的Python函数,而Executor是JVM进程,无法直接执行代码。因此会调用服务器上的Python解释器,将Python函数和输入数据传输给到Python解释器,执行完以后,将结果数据返回给Executor进程
6.4- Executor在运行过程中,会判断是否需要将结果数据返回给到Driver进程。如果需要,就返回给Driver进程;如果不需要,直接输出,结束即可。
6.5- Driver会定时检查多个Executor的执行状态。直到所有的Executor执行完成,就认为任务运行结束。同时ApplicationMaster也会接收到各个节点的执行完成状态,然后通知主节点。任务执行完成了,主节点回收资源,关闭ApplicationMaster,并且通知Driver。

7- Driver执行sc.stop()代码。Driver进程退出

4、cluster on Yarn集群

在这里插入图片描述
在这里插入图片描述
区别点: 在集群模式下,Driver进程的功能和ApplicationMaster的功能(角色)合二为一了。Driver就是ApplicationMaster,ApplicationMaster就是Driver。既要负责资源申请,又要负责任务的分配和管理。

1- 首先会将任务提交给Yarn集群的主节点(ResourceManager)

2- ResourceManager接收到任务信息后,根据Driver(ApplicationMaster)的资源配置信息要求,选择一个
nodeManager节点(有资源的,如果都有随机)来启动Driver(ApplicationMaster)程序,并且占用相对应资源

3- Driver(ApplicationMaster)启动后,执行main函数。首先创建SparkContext对象(底层是基于PY4J,识
别python的构建方式,将其映射为Java代码)。创建成功后,会向ResourceManager进行建立心跳机制,告知已经
启动成功了

4- 根据executor的资源配置要求,向ResourceManager通过心跳的方式申请资源,用于启动executor(提交的任
务的时候,可以自定义资源信息)

5- ResourceManager接收到资源申请后,根据申请要求,进行分配资源。底层是基于资源调度器来资源分配(默认
为Capacity容量调度)。然后将分配好的资源准备好,等待Driver(ApplicationMaster)拉取操作
executor1: node1 2个CPU 2GB内存
executor2: node3 2个CPU 2GB内存

6- Driver(ApplicationMaster)会定时询问是否准备好资源,一旦准备好,立即获取。根据资源信息连接对应的
节点,通知nodeManager启动executor,并占用相应资源。nodeManager对应的executor启动完成后,反向注册
回给Driver(ApplicationMaster)程序(已经启动完成)

7- Driver(ApplicationMaster)开始处理代码:
7.1 首先会加载所有的RDD相关的API(算子),基于算子之间的依赖关系,形成DAG执行流程图,划分stage阶
段,并且确定每个阶段应该运行多少个线程以及每个线程应该交给哪个executor来运行(任务分配)
7.2 Driver(ApplicationMaster)程序通知对应的executor程序, 来执行具体的任务
7.3 Executor接收到任务信息后, 启动线程, 开始执行处理即可: executor在执行的时候, 由于RDD代
码中有大量的Python的函数,Executor是一个JVM程序 ,无法解析Python函数, 此时会调用Python解析器,执
行函数, 并将函数结果返回给Executor
7.4 Executor在运行过程中,如果发现最终的结果需要返回给Driver(ApplicationMaster),直接返回
Driver(ApplicationMaster),如果不需要返回,直接输出 结束即可
7.5 Driver(ApplicationMaster)程序监听这个executor执行的状态信息,当Executor都执行完成后,
Driver(ApplicationMaster)认为任务运行完成了

8- 当任务执行完成后,Driver执行sc.stop()通知ResourceManager执行完成,ResourceManager回收资源,
Driver程序退出即可

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/diannao/45748.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

从汇编层看64位程序运行——栈帧(Stack Frame)边界

大纲 RBP,RSP栈帧边界总结参考资料 在《从汇编层看64位程序运行——栈帧(Stack Frame)入门》中,我们简单介绍了栈帧的概念,以及它和函数调用之间的关系。如文中所述,栈帧是一种虚拟的概念,它表达了一个执行中的函数的栈…

如何找回误删的文件?4个常用文件恢复方法!

对于许多用户来说,误删文件是一种常见而令人懊恼的情况。恢复误删文件的重要性在于,它可以帮助用户找回宝贵的数据,避免因数据丢失带来的各种不便和损失。 如何找回不小心删除的文件? 误删数据不知道怎么恢复,会给我…

kafka部署以及常用命令详细总结

1环境准备 1.1ip规划 ip: 192.168.1.200 1.2配置主机名 #设置主机名 hostnamectl set-hostname node11.3配置hosts [rootnode1 ~]# cat >> /etc/hosts << EOF192.168.1.200 node1 EOF2部署 2.1安装包准备 将以下安装包从官网下载到本地 jdk-8u371-linux-x6…

如何使用键盘优雅地使用浏览器

来自&#x1f96c;&#x1f436;程序员 Truraly | 田园 的博客&#xff0c;最新文章首发于&#xff1a;田园幻想乡 | 原文链接 | github &#xff08;欢迎关注&#xff09; 目录 浏览器快捷键 参考资料&#xff1a; 教你如何用键盘轻松浏览网页 这两天出门旅游&#xff0c;高铁…

Vue单路由的独享守卫怎么设置

在Vue.js中&#xff0c;特别是在使用Vue Router时&#xff0c;路由守卫&#xff08;Route Guards&#xff09;是一种强大的机制&#xff0c;允许我们在路由发生变化时执行一些逻辑&#xff0c;比如检查用户是否登录、加载数据等。Vue Router提供了全局守卫、路由独享守卫和组件…

美团一面,你碰到过CPU 100%的情况吗?你是怎么处理的?

本文主要分为三部分 分析一下CPU 100%的常见原因 CPU 100%如何排查 回答这个问题的一个参考答案 CPU被打满的常见原因 1. 死循环 在实际工作中&#xff0c;可能每个开发都写过死循环的代码。 死循环有两种&#xff1a; 在 while、for、forEach 循环中的死循环。 无限递…

centos安装minio文件系统服务器(踩坑版)

centos安装minio文件系统服务器&#xff08;踩坑版&#xff09; 引安装1. 下载2. 启动3. 创建access keys4. 创建buckets 坑 引 本来安装挺简单的&#xff0c;网上的教程一大堆&#xff0c;有些写的也挺详细的。不过自己还是踩到坑了&#xff0c;耽误了个把小时&#xff0c;特…

【分库】分库的核心原则

目录 分库的核心原则 前言 分区透明性与一致性保证 弹性伸缩性与容错性设计 数据安全与访问控制机制 分库的核心原则 前言 在设计和实施分库策略时&#xff0c;遵循一系列核心原则是至关重要的&#xff0c;以确保系统不仅能够在当前规模下高效运行&#xff0c;还能够随着…

Vue的生命周期函数有哪些?

Vue的生命周期函数是指Vue实例从创建到销毁的过程中&#xff0c;会调用的一系列特殊函数&#xff0c;这些函数允许开发者在Vue的不同阶段执行特定的代码。Vue 2.x和Vue 3.x的生命周期函数有所差异&#xff0c;但总体思路是一致的。以下是Vue生命周期函数的主要分类和具体函数&a…

单目测距 单目相机测距 图片像素坐标转实际坐标的一种转换方案

需要相机位置固定 原图 红色的点是我们标注的像素点&#xff0c;这些红色的点我们知道它的像素坐标&#xff0c;以及以右下角相机位置为原点的x y 实际坐标数值 通过转换&#xff0c;可以得到整个图片内部其余像素点的实际坐标&#xff0c; 这些红色的点是通过转换关系生成的&…

Python | Leetcode Python题解之第231题2的幂

题目&#xff1a; 题解&#xff1a; class Solution:BIG 2**30def isPowerOfTwo(self, n: int) -> bool:return n > 0 and Solution.BIG % n 0

el-table 动态添加删除 -- 鼠标移入移出显隐删除图标

<el-table class"list-box" :data"replaceDataList" border><el-table-column label"原始值" prop"original" align"center" ><template slot-scope"scope"><div mouseenter"showClick…

小妙招使用sysctl hw.realmem查看实际物理内存@FreeBSD

使用sysctl hw.realmem查看实际物理内存&#xff1a;The realmem value is memory before the kernel and modules are loaded, whereas hw.physmem is what is left after they were loaded. 使用hw.physmem查看去掉kernel和模块调用后剩余的内存 sysctl hw.ncpu是机器的cpu…

Java三剑客:封装、继承、多态的魔法世界

第一章&#xff1a;封装的艺术 —— 保护你的宝藏 案例分析&#xff1a;银行账户系统 想象一下&#xff0c;你正在构建一个银行账户系统。每个账户都有一个余额&#xff0c;这个余额需要受到严格的保护&#xff0c;不能被随意修改。我们可以通过封装来实现这一目标。 示例代…

Sentinel和hystric的运用详解

Hystrix是一个由Netflix开发的开源Java库&#xff0c;用于实现延迟容忍和容错逻辑&#xff0c;以增强分布式服务之间的交互的弹性。Hystrix通过隔离服务之间的访问点&#xff0c;阻止级联故障&#xff0c;并提供后备选项来实现这一目标。Hystrix的核心功能包括服务降级、服务熔…

nvide shortcuts table

快捷键中文功能描述n nvim-tree: 打开预览n nvim-tree: 打开n -nvim-tree: 上一级目录n .nvim-tree: 运行命令n <nvim-tree: 上一个同级节点n >nvim-tree: 下一个同级节点n Bnvim-tree: 切换过滤器&#xff1a;无缓冲区n Cnvim-tree: 切换过滤器&#xff1a;Git 干净n Dn…

JavaWeb(四:Ajax与Json)

一、Ajax 1.定义 Ajax&#xff08;Asynchronous JavaScript And XML&#xff09;&#xff1a;异步的 JavaScript 和 XML AJAX 不是新的编程语言&#xff0c;指的是⼀种交互方式&#xff1a;异步加载。 客户端和服务器的数据交互更新在局部页面的技术&#xff0c;不需要刷新…

Openerstry + lua + redis根据请求参数实现动态路由转发

文章目录 一、需求分析二、准备1、软件安装2、redis-lua封装优化 三、实现1、nginx.conf2、dynamic.lua注意 3、准备两个应用4、访问nginx 一、需求分析 根据用户访问url的参数&#xff0c;将请求转发到对应指定IP的服务器上。 二、准备 1、软件安装 安装openrestyredis&am…

Database数据库 vs Data Warehouse数据仓库 vs Data Mart数据集市 vs Data Lake数据湖

1.DATABASE 数据库 数据库是一个结构化的数据集合&#xff0c;用于存储、管理和检索数据。数据库设计用于支持事务处理&#xff08;OLTP&#xff0c;Online Transaction Processing&#xff09;和日常操作。 数据库通常由数据库管理系统&#xff08;DBMS&#xff09;控制&…

golang json反序列化科学计数法的坑

问题背景 func CheckSign(c *gin.Context, signKey string, singExpire int) (string, error) {r : c.Requestvar formParams map[string]interface{}if c.Request.Body ! nil {bodyBytes, _ : io.ReadAll(c.Request.Body)defer c.Request.Body.Close()if len(bodyBytes) >…