Spark(1.0)内核解析

Spark的内核部分主要从以下几个方面介绍:

  任务调度系统、I/0模块、通信控制模块、容错模块、shuffle模块

一、任务调度系统

1、作业执行流程

接下来注意几个概念:

  Application:用户自定义的Spark程序,用户提交后,Spark为App分配资源,将程序转换并执行。

  Driver Program:运行Application的main()函数并创建SparkContext

  RDD Graph:RDD是Spark的核心结构,可以通过一系列算子进行操作(主要有Transformation和Action操作)。当RDD遇到Action算子时,将之前的所有算子形成一个有向无环图(DAG),再在Spark中转化为Job,提交到集群执行。一个App中可以包含多个Job。

  Job:一个RDD Graph触发的作业,往往由Spark Action算子触发,在SparkContext中通过runJob方法向Spark提交Job。

  Stage:每个Job会根据RDD的宽依赖关系被切分成很多Stage,每个Stage中包含一组相同的Task,叫TaskSet

  Task:一个分区对应一个Task,Task执行RDD中对应Stage中包含的算子,Task封装好之后放入Executor的线程池中执行。

作业执行流程:

  1)用户启动客户端,之后客户端运行用户程序,启动Driver进程。在Driver中启动或实例化DAGScheduler等组件。客户端的Driver向Master注册。

  2)Worker向Master注册(要确保master有活节点可用,或通过心跳机制向master汇报worker节点还活着),Master命令Worker启动Exeuctor。Worker通过创建ExecutorRunner线程,在ExecutorRunner线程内部启动ExecutorBackend进程。

  3)ExecutorBackend启动后,向客户端Driver进程内的SchedulerBackend注册。而SchedulerBackend收到后,会向Worker启动LaunchTask进程,Worker开始执行任务。

  4)Driver中的SchedulerBackend进程中包含DAGScheduler进程、TaskScheduler进程。SchedulerBackend收到注册后,DAGScheduler会根据RDD DAG切分成相应的Stage,每个Stage中包含的TaskSet通过TaskScheduler分配给Executor。Executor启动线程池并行化执行Task。

 2、Spark的任务调度系统

  1)在最左边通过一系列的Transformation算子和Actions算子,形成DAG图,传递给DAGScheduler,

  2)然后DAGScheduler把其切分成一系列的Stage,即形成TaskSet任务集合,每个TaskSet中包含多个任务。然后把TaskSet传递给TaskScheduler

  3)TaskScheduler收到后,然后把具体任务分配给相应的Worker节点进行计算。

3、以wordcount为例,解析触发Job全生命周期过程

sc.textFile(hdfs://....).flatMap(line=>line.split(" ")).map(word=>(word,1)).reduceByKey(_+_).saveAsFile(hdfs://...)
化简为:
sc.textFile(hdfs://....).flatMap(_.split(" ")).map(_,1).reduceByKey(_+_).saveAsFile(hdfs://...)
因为默认得出的结果是没有序的,所以想要得到排序的结果:
sc.textFile(hdfs://....).flatMap(_.split(" ")).map(_,1).reduceByKey(_+_).map(x=>(x._2,x._1)).sortByKey(false).map(x=>(x._2,x._1)).saveAsFile(hdfs://...)

   上述代码经过了HadoopRDD-->MappedRDD-->flatMappedRDD-->MappedRDD-->PairRDDFunctions-->ShuffledRDD-->MappartitionsRDD-->MappedRDD-->SortedRDD-->MappedRDD-->HadoopRDD

HadoopRDD-->MappedRDD:  sc.textFile(hdfs://....)
MappedRDD-->flatMappedRDD: flatMap(_.split(" "))
flatMappedRDD-->MappedRDD: flatMap(_.split(" ")).map(_,1)
MappedRDD-->PairRDDFunctions-->ShuffledRD: map(_,1).reduceByKey(_+_)
ShuffledRDD-->MappartitionsRDD-->MappedRDD:reduceByKey(_+_).map(x=>(x._2,x._1))
MappedRDD-->SortedRDD: map(x=>(x._2,x._1)).sortByKey(false)
SortedRDD-->MappedRDD: sortByKey(false).map(x=>(x._2,x._1))
MappedRDD-->HadoopRDD: map(x=>(x._2,x._1)).saveAsFile(hdfs://...)

 4、流程总结

  1、首先应用程序创建SparkContext的实例,如sc

  2、利用SparkContext实例创建生成RDD

  3、经过一连串的transforation操作,原始的RDD转换成其他类型的RDD

  4、当action作用于转换之后的RDD时,会调用SparkContext的runJob方法

  5、sc.runJob的调用是后面一连串反应的起点,关键性的跃变就发生在此处

  6、sc.runJob-->DAGScheduler.runJob-->submitJob

  7、DAGScheduler::submitJob会创建JobSummitted的event发送给内嵌类eventProcessActor

  8、eventProcessActor在接收到JobSubmmitted之后调用processEvent处理函数

  9、job到stage的转换,生成finalStage并提交运行,关键是调用submitStage

  10、在submitStage中会计算stage之间的依赖(分两种,宽依赖和窄依赖)

  11、如果计算中发现当前的stage没有任何依赖或所有依赖准备完毕,则提交stage

  12、提交task是调用函数submitMissingTasks来完成

  13、task真正运行在哪个worker是由TaskSecheduler来管理,也就是上面的submitMissingTasks会调用TaskScheduler::submitTasks

  14、TaskSchedulerImpl中会根据Spark的当前运行模式来创建相应的backend,如果在单机运行则创建LocalBackend

  15、LocalBackend收到TaskSchedulerImpl传递过来的ReceiveOffers事件

  16、receiveOffers-->executor.launchTask-->TaskRunner.run

二、I/O模块

  整体的I/O管理分为两个层次:

    1)通信层:I/O模块也是采用Master-Slave结构来实现通信层的架构,Master和Slave之间传输控制信息和状态信息。

    2)存储层:Spark的块数据需要存储到内存或磁盘,有可能还需传输到远端机器,这些是由存储层完成的。

  RDD逻辑上是按照Partition分块的,可以将RDD看成一个分区作为数据项的分布式数组。在物理上RDD是以Block为单位,一个Partition对应一个Block,用Partition的ID通过元数据的映射到物理上的Block,而这个物理上的Block可以存储在内存,也可以存储在某个节点的Spark的硬盘临时目录。

  当其他模块与stroage模块交互时,BlockManager来具体实现。

三、容错机制

  容错有两种方式:

    数据检查点(checkpoint机制)、记录数据的更新(Lineage机制)

1、Lineage机制(粗粒度转换)

  原理:如果一个节点死了,而且运算Narrow Dependecy,则只需把丢失的父RDD分区重新计算即可,不依赖其他节点,不存在冗余计算。而Shuffle Dependecy需要父RDD的所有分区都存在,重算就很昂贵了(可采用Checkpoint算子来做检查点)。

2、Checkpoint机制

  在以下两种情况需要做检查点:

    1)DAG中的Lineage过长,如果重算,则开销太大(如PageRank)

    2)在Shuffle Dependecy上做检查点获得的收益更大。

  RDD采用同步方式做检查点,具体使用Synchronized保证方法的同步和线程安全

四、Shuffle机制

  Shuffle的本意是洗牌、混洗。即把一定规则的数据打乱,而这里的Shuffle是其反过程,把一组无规则的数据换成有规则的数据。Shuffle分为两个阶段:

  1)Shuffle Write:

    Spark之分为两种任务,ShuffleMapTask和ResultTask。除了最后阶段将数据输出到Spark执行空间Stage这个阶段执行ResultTask,其余阶段都是执行ShuffleMapTask任务。

    通过ShuffleMapTask中的runTask方法进入Shuffle Writer骨架。Spark支持两种流程:Shuffle和优化的Consolidate Shuffle

         

Consolidation Shuffle显著减少了shuffle文件的数量,解决了文件数量过多的问题,但是Writer Handler的Buffer开销过大依然没有减少。

  2)Shuffle Fetch

    Shuffle write阶段写到各个节点的数据,Reducer端的节点通过拉取数据而获取需要的数据,在Spark中叫Fetch。有两种方式:NIO通过Socket连接去fetch数据、OIO通过Netty去Fetch数据.

posted @ 2016-03-17 20:32  liurio  阅读(352)  评论(0编辑  收藏  举报