共计 3835 个字符,预计需要花费 10 分钟才能阅读完成。
Spark 简介
Apache Spark 是用于 大规模数据处理 的对立剖析引擎,基于内存计算,进步了在大数据环境下数据处理的实时性,同时保障了 高容错性 和高可伸缩性,容许用户将 Spark 部署在大量硬件之上,造成集群。
Spark 源码从 1.x 的 40w 行倒退到当初的超过 100w 行,有 1400 多位大牛奉献了代码。整个 Spark 框架源码是一个微小的工程。上面咱们一起来看下 spark 的底层执行原理。
Spark 运行流程
具体运行流程如下:
- SparkContext 向资源管理器注册并向资源管理器申请运行 Executor
- 资源管理器调配 Executor,而后资源管理器启动 Executor
- Executor 发送心跳至资源管理器
- SparkContext 构建 DAG 有向无环图
- 将 DAG 分解成 Stage(TaskSet)
- 把 Stage 发送给 TaskScheduler
- Executor 向 SparkContext 申请 Task
- TaskScheduler 将 Task 发送给 Executor 运行
- 同时 SparkContext 将利用程序代码发放给 Executor
- Task 在 Executor 上运行,运行结束开释所有资源
1. 从代码角度看 DAG 图的构建
Val lines1 = sc.textFile(inputPath1).map(...).map(...)
Val lines2 = sc.textFile(inputPath2).map(...)
Val lines3 = sc.textFile(inputPath3)
Val dtinone1 = lines2.union(lines3)
Val dtinone = lines1.join(dtinone1)
dtinone.saveAsTextFile(...)
dtinone.filter(...).foreach(...)
上述代码的 DAG 图如下所示:
Spark 内核会在须要计算产生的时刻绘制一张对于计算门路的有向无环图,也就是如上图所示的 DAG。
Spark 的计算产生在 RDD 的 Action 操作,而对 Action 之前的所有 Transformation,Spark 只是记录下 RDD 生成的轨迹,而不会触发真正的计算。
2. 将 DAG 划分为 Stage 外围算法
一个 Application 能够有多个 job 多个 Stage:
Spark Application 中能够因为不同的 Action 触发泛滥的 job,一个 Application 中能够有很多的 job,每个 job 是由一个或者多个 Stage 形成的,前面的 Stage 依赖于后面的 Stage,也就是说只有后面依赖的 Stage 计算结束后,前面的 Stage 才会运行。
划分根据:
Stage 划分的根据就是宽依赖,像 reduceByKey,groupByKey 等算子,会导致宽依赖的产生。
回顾下宽窄依赖的划分准则:
窄依赖 :父 RDD 的一个分区只会被子 RDD 的一个分区依赖。即一对一或者多对一的关系,可了解为独生子女。常见的窄依赖有:map、filter、union、mapPartitions、mapValues、join(父 RDD 是 hash-partitioned)等。
宽依赖:父 RDD 的一个分区会被子 RDD 的多个分区依赖(波及到 shuffle)。即一对多的关系,可了解为超生。常见的宽依赖有 groupByKey、partitionBy、reduceByKey、join(父 RDD 不是 hash-partitioned)等。
外围算法:回溯算法
从后往前回溯 / 反向解析,遇到窄依赖退出本 Stage,遇见宽依赖进行 Stage 切分。
Spark 内核会从触发 Action 操作的那个 RDD 开始 从后往前推 ,首先会为最初一个 RDD 创立一个 Stage,而后持续倒推,如果发现对某个 RDD 是宽依赖,那么就会将宽依赖的那个 RDD 创立一个新的 Stage,那个 RDD 就是新的 Stage 的最初一个 RDD。
而后顺次类推,持续倒推,依据窄依赖或者宽依赖进行 Stage 的划分,直到所有的 RDD 全副遍历实现为止。
3. 将 DAG 划分为 Stage 分析
一个 Spark 程序能够有多个 DAG(有几个 Action,就有几个 DAG,上图最初只有一个 Action(图中未体现), 那么就是一个 DAG)。
一个 DAG 能够有多个 Stage(依据宽依赖 /shuffle 进行划分)。
同一个 Stage 能够有多个 Task 并行执行(task 数 = 分区数,如上图,Stage1 中有三个分区 P1、P2、P3,对应的也有三个 Task)。
能够看到这个 DAG 中只 reduceByKey 操作是一个宽依赖,Spark 内核会以此为边界将其前后划分成不同的 Stage。
同时咱们能够留神到,在图中 Stage1 中,从 textFile 到 flatMap 到 map 都是窄依赖,这几步操作能够造成一个流水线操作,通过 flatMap 操作生成的 partition 能够不必期待整个 RDD 计算完结,而是持续进行 map 操作,这样大大提高了计算的效率。
4. 提交 Stages
调度阶段的提交,最终会被转换成一个工作集的提交,DAGScheduler 通过 TaskScheduler 接口提交工作集,这个工作集最终会触发 TaskScheduler 构建一个 TaskSetManager 的实例来治理这个工作集的生命周期,对于 DAGScheduler 来说,提交调度阶段的工作到此就实现了。
而 TaskScheduler 的具体实现则会在失去计算资源的时候,进一步通过 TaskSetManager 调度具体的工作到对应的 Executor 节点上进行运算。
5. 监控 Job、Task、Executor
- DAGScheduler 监控 Job 与 Task:
要保障相互依赖的作业调度阶段可能失去顺利的调度执行,DAGScheduler 须要监控以后作业调度阶段乃至工作的实现状况。
这通过对外裸露一系列的回调函数来实现的,对于 TaskScheduler 来说,这些回调函数次要包含工作的开始完结失败、工作集的失败,DAGScheduler 依据这些工作的生命周期信息进一步保护作业和调度阶段的状态信息。
- DAGScheduler 监控 Executor 的生命状态:
TaskScheduler 通过回调函数告诉 DAGScheduler 具体的 Executor 的生命状态,如果某一个 Executor 解体了,则对应的调度阶段工作集的 ShuffleMapTask 的输入后果也将标记为不可用,这将导致对应工作集状态的变更,进而从新执行相干计算工作,以获取失落的相干数据。
6. 获取工作执行后果
- 后果 DAGScheduler:
一个具体的工作在 Executor 中执行结束后,其后果须要以某种模式返回给 DAGScheduler,依据工作类型的不同,工作后果的返回形式也不同。
- 两种后果,两头后果与最终后果:
对于 FinalStage 所对应的工作,返回给 DAGScheduler 的是运算后果自身。
而对于两头调度阶段对应的工作 ShuffleMapTask,返回给 DAGScheduler 的是一个 MapStatus 里的相干存储信息,而非后果自身,这些存储地位信息将作为下一个调度阶段的工作获取输出数据的根据。
- 两种类型,DirectTaskResult 与 IndirectTaskResult:
依据工作后果大小的不同,ResultTask 返回的后果又分为两类:
如果后果足够小,则间接放在 DirectTaskResult 对象内中。
如果超过特定尺寸则在 Executor 端会将 DirectTaskResult 先序列化,再把序列化的后果作为一个数据块寄存在 BlockManager 中,而后将 BlockManager 返回的 BlockID 放在 IndirectTaskResult 对象中返回给 TaskScheduler,TaskScheduler 进而调用 TaskResultGetter 将 IndirectTaskResult 中的 BlockID 取出并通过 BlockManager 最终获得对应的 DirectTaskResult。
7. 任务调度总体诠释
一张图阐明工作总体调度:
Spark 运行架构特点
1. Executor 过程专属
每个 Application 获取专属的 Executor 过程,该过程在 Application 期间始终驻留,并以多线程形式运行 Tasks。
Spark Application 不能跨应用程序共享数据,除非将数据写入到内部存储系统。如图所示:
2. 反对多种资源管理器
Spark 与资源管理器无关,只有可能获取 Executor 过程,并能放弃互相通信就能够了。
Spark 反对资源管理器蕴含:Standalone、On Mesos、On YARN、Or On EC2。如图所示:
3. Job 提交就近准则
提交 SparkContext 的 Client 应该凑近 Worker 节点 (运行 Executor 的节点),最好是在同一个 Rack(机架) 里,因为 Spark Application 运行过程中 SparkContext 和 Executor 之间有大量的信息替换;
如果想在近程集群中运行,最好应用 RPC 将 SparkContext 提交给集群,不要远离 Worker 运行 SparkContext。
如图所示:
4. 挪动程序而非挪动数据的准则执行
挪动程序而非挪动数据的准则执行,Task 采纳了数据本地性和揣测执行的优化机制。
要害办法:taskIdToLocations、getPreferedLocations。
如图所示:
搜寻公众号:五分钟学大数据,深度钻研大数据技术!