Spark内核架构

来源:互联网 发布:win10电脑软件 编辑:程序博客网 时间:2024/06/05 05:41
转载自:http://blog.sina.com.cn/s/blog_4a7854d90102wrv6.html
              http://blog.csdn.net/slq1023/article/details/50816669

Driver

       Driver是运行程序时具有main方法并创建了SparkContext的环境对象。SparkContext是整个程序运行调度的核心(是程序运行调度的核心而不是资源调度的核心)。
       SparkContext里有高层调度器、低层调度器、SchedulerBackend。高层调度器就是把整个作业划分成几个阶段,底层调度器是每个阶段里的任务该如何处理,SchedulerBackend是管理整个集群中为当前程序分配的计算资源(Executor)。
       SparkContext创建的过程中做了很多内容,包括DAG Scheduler、TaskScheduler、SchedulerBackend,SparkContext在创建这些对象的同时会向Master注册当前程序,注册成功后会分配资源,根据Action触发的JOB,JOB里有RDD,从后往前推,如果有宽依赖的话,就划分成不同的Stage。Stage划分完成后提交给底层调度器TaskScheduler。
       TaskScheduler拿到任务的集合,就会根据数据的本地性把任务发到Executor执行。Executor在出问题时会向Driver汇报。运行完后SparkContext会关闭。当然创建的对象也都会关闭。

       Driver是应用程序运行调度的核心,因为它负责了整个作业的调度,并且会向Master申请资源来完成具体作业的工作过程。Driver是驱动Executor工作的。
       Executor是运行在Worker节点上的为当前应用程序而开启的一个进程里面的处理对象,这个对象负责了Task的运行,通过线程池中的线程并发执行和线程复用的方式,线程池中的每一个线程可以运行一个任务,任务完成后回收到线程池中进行线程复用。
       Executor就是进程里的对象,默认情况下Driver运行在当前提交任务的机器上。一个worker默认会为当前的应用程序只开启一个executor,当然可以配置为多个。Executor靠线程池中的线程来运行Task时,Task肯定要从磁盘或内存中读写数据,每个APPlication都有自己独立的一批executor。
       Hadoop中每个mapper都开启一个jvm进程,没法复用,JVM是重量级的,而Spark在一个节点上在默认情况下只为当前程序开启一个JVM。在JVM中是以线程池的方式,通过线程来处理Task任务。

Worker

       Worker不能运行当前程序代码,它是管理当前Node的资源(主要是CPU和内存)使用状况,并接受Master的指令来分配具体的计算资源Executor(在新的进程中分配),Worker通过代理(ExecutorRunner)管理新分配的进程Executor,并监控Executor运行状况。实质上就是在ExecutorRunner中创建出Executor进程的。
       Worker并不会向Master报告当前节点的内存和cpu资源,那master怎么知道各节点的资源信息?应用程序在向Master注册时,注册成功后master就会分配资源,分配时就会记录资源,所有的资源都是Master分配的,所以Master已经各节点的资源信息了。只有当Worker出现故障时才会向Master汇报资源情况。但是worker会不断向Master发的心跳,但内容只有worker ID,是用来判断Worker是否活着。

Job

       JOB就是包含了一系列Task的并行计算。JOB一般由应用程序的Action操作触发,比如saveAsTextFile。JOB都是由Action触发的,触发时前面有一系列的RDD。
       action不会产生RDD,只会导致RunJOB。action之前的RDD是transformation级别的,是lazy级别的执行方式。如果后面的RDD对前面的RDD进行回溯时是窄依赖的话,就会在内存中进行迭代,这是Spark快的一个很重要的原因。(注意:Spark快不仅是因为基于内存。调度,容错才是Spark的精髓的基本点。)
       依赖构成DAG,依赖包含窄依赖和宽依赖。窄依赖有一个Range级别,即依赖固定个数的父RDD,所谓固定个数是说不会随着数据规模的扩大而改变。如果是宽依赖DAG Scheduler就会划分Stage,Stage是基于内存迭代的,同一个Stage内部的计算逻辑完全一样,只是计算的数据不同罢了。(不同stage的计算逻辑是不一样的,一个stage可能跨多个executor执行)。
       任务(Task)就是计算一个数据分片的,例如从HDFS上读取数据时默认数据分片就是128MB。那么一个数据分片是否精准地等于一个Block的大小(默认128MB)?一般情况下都不等于,因为最后一个分片会跨两个Block。
       一个Application里面可以有多个Job,一个Job可能包含多个Stage,一般一个action对应一个job。
 

       master接收用户提交的程序,并发指令给worker为当前程序分配计算资源。每个worker所在节点默认为当前程序分配一个executor,在excutor中通过线程池并发执行,master通知worker按照要求启动executor,默认启动一个Executor。
       WorkerNode上Worker进程通过一个Proxy为ExecutorRunner的对象实例来启动ExecutorBackend运行。Executor里有线程池。实际在工作时Executor接收一个Task会通过TaskRunner来封装task,然后从ThreadPool中获取一条线程执行task,执行完后线程被回收复用,TaskRunner在封装时(具体运行时)会把代码反序列化然后执行具体内容。
       一般情况当通过action触发job时,sparkContext会通过DAGscheduler来把job中的RDD构成的DAG划分成不同的 stage(宽依赖),每个stage内部是一系列业务逻辑完全相同,但处理数据不同的tasks,构成了task set(任务集合)。
       Task Set底层交给task scheduler。task scheduler和schedulerbackend负责具体任务的运行(遵循数据本地性)。数据本地性是在什么时候确定的?就是说Task运行在哪台机器上是什么时候确定的?答案是在DAG Scheduler划分Stage时确定的。
       TaskScheduler会把每个Stage内部的一系列的Task发送给Executor。TaskScheduler就是根据数据本地性来确定把哪些任务发给哪个Executor的。大数据一句精髓的话就是数据不动代码动。

执行时运行的Task有两种类型:
       1. 最后一个stage中的task称为ResultTask,产生job的结果,其它前面的stage中的task都是shuffleMapTask,为下一阶段的stage做准备,相当于MapReduce的Mapper。
       2. 整个spark程序的运行,就是DAGScheduler把JOB分成不同的stage,提交taskset给taskScheduler,进而提交给executor执行(符合数据本地性)。
0 0