一些名词概念

  1. AM : ApplicationMaster
  2. RM : ResourceManager
  3. NM : NodeManager
  4. Backend : 后台
  5. RpcEnv : RPC 进程和进程的通信协议
  6. RpcEndpoint 终端
  7.  
  8. constructor -> onStart -> receive* -> onStop
  9.  
  10. RpcEndpointRef :终端引用
  11.  
  12. NettyRpcEnv
  13. RpcEndpointAddress
  14. NettyRpcEndpointRef
  15.  
  16. ThreadSafeRpcEndpoint
  17.  
  18. Inbox
  19.  
  20. BIO, NIO, AIO

以yarn-cluster模式为例源码分析作业提交流程

  1. ##spark-yarn-cluster模式
    bin/spark-submit \
  2. --class org.apache.spark.examples.SparkPi \
  3. --num-executors 2 \
  4. --master yarn \
  5. --deploy-mode cluster \
  6. ./examples/jars/spark-examples_2.11-2.1.1.jar \
  7. 100

  8. ##spark-yarn-client模式
  9. bin/spark-submit \
  10. --class org.apache.spark.examples.SparkPi \
  11. --num-executors 2 \
  12. --master yarn \
  13. --deploy-mode client \
  14. ./examples/jars/spark-examples_2.11-2.1.1.jar \
  15. 100

Spark-submit提交源码解析

执行spark-submit实际上执行的是$SPARK_HOME/spark-class -->执行一个java类  java  org.apache.spark.launcher.Main调它的main方法(它就是检查参数、构建CommandBuiler指令)--->最终真正要执行 这个类 org.apache.spark.deploy.SparkSubmit xxx是命令行参数,它就会启动一个叫SparkSubmit进程

SparkSubmit类,对命令行参数进行了封装,准备提交环境,不是集群模式它会拿到指令中的--class所传的类, if (isYarnCluster)-->> org.apache.spark.deploy.yarn.Client 

  1. 利用反射通过类名获取类的信息,利用反射通过类信息获取Main方法,调用Clientmain方法: Client的调用只是一个普通方法的调用
  1. 根据yarn的配置进行初始化创建yarn客户端准备去跟yarn打交道(要有yarnserver集群), yarnClient.start()启动客户端,获取一个appId,yarn的全局id,根据id可以查询它的所有信息;
    ClientyarnRM提交应用, 集群模式通过submitApplication来完成,启动JAVA_HOME/bin/java org.apache.spark.deploy.yarn.ApplicationMaster这个进程类(主要是为了与Yarn做关联)
  1. 这些commands上边封装好的指令|类都提交给yarn,由yarn来执行,这时Client客户端就结束了;
  1. spark-submit 它实际上调用的是"${SPARK_HOME}"/bin/spark-class org.apache.spark.deploy.SparkSubmit java中的类)
  2.    spark-class中: RUNNER="${JAVA_HOME}/bin/java" ##执行java.exe-->类 -->相当于启动一个JVM -->就是启动一个进程Process(进程中有独立内存、线程)
  3.    "$RUNNER" -Xmx128m -cp "$LAUNCH_CLASSPATH"(#就是类路径) org.apache.spark.launcher.Main <=>等同于执行Main这个类,调用main方法
  4.    build_command()构建指令; 最终是要执行SparkSubmit ##className.equals("org.apache.spark.deploy.SparkSubmit")
  5. org.apache.spark.launcher.Main
  6. org.apache.spark.deploy.SparkSubmit --xx等参数
  1. SparkSubmit
  2.  
  3. // 对命令行参数进行封装
  4. -- . new SparkSubmitArguments(args) //命令行参数包含master、deployMode、executorMemory等 ;在scala中,类{ 类体or构造函数体 },参数中类体,类初始化时都会执行
  5.         parse(args.asJava)类体中;parse方法做了正则的匹配 Pattern.compile("(--[^=]+)=(.+)") ==> handle (源码326)方法对传的命令行参数做模式匹配;
              SparkSubmit类,appArgs.action.match{ SUBMIT| ... }模式匹配,actionSparkSubmitArguments做了封装  action = Option(action).getOrElse(SUBMIT)
  6. -- . submitSparkSubmit中的方法)
  7. // 准备提交环境
  8. -- 2.1 prepareSubmitEnvironment
  9.  
  10. -- 2.2 doRunMain(声明了但没有执行) isStandaloneCluster  其他模式都会走doRunMain方法
  11.  
  12. -- 2.3 runMain(childArgs, childClasspath, sysProps, childMainClass, args.verbose)参数来自提交对环境准备 <--if (args.proxyUser != null)
  13.  
  14. -- 2.3. ClassLoader : Thread.currentThread.setContextClassLoader(loader) //loader类加载器,把类放到集群的节点上去执行
  15.  
  16. // childMainClass(Client) : org.apache.spark.examples.SparkPi //不是集群模式它会拿到指令中的--class所传的类
  17. // childMainClass(Clustor) : org.apache.spark.deploy.yarn.Client // if (isYarnCluster)集群模式
  18.             // 反射:通过类名获取类的信息
  19. -- 2.3. mainClass = Utils.classForName(childMainClass) //var mainClass: Class[_] = null,Class为类的全部信息;
  20. // 反射:通过类信息获取Main方法
  21. -- 2.3. val mainMethod = mainClass.getMethod("main", new Array[String]().getClass)
  22. // 反射:调用main方法
  23. -- 2.3. mainMethod.invoke(null, childArgs.toArray)
  24. Client
  25.       Client的调用只是一个普通方法的调用;如果是进程:java Client=》JVM=》Process 如果是线程:Thread.start() 普通方法调用: method.invoke
  26. // 对命令行参数进行封装
  27. -- . new ClientArguments(argStrings) //在main方法中
  28.  
  29. -- . new Client(args, sparkConf).run() //它会去调用辅助构造方法-->一定会会去调用主构造方法
  30.       private val yarnClient = YarnClient.createYarnClient //它去创建yarn客户端准备去跟yarn打交道(要有yarn的server集群)
            private val amMemory = if (isClusterMode) //ApplicationMaster的内存
             ...等等,主要是去构造属性、方法和对象
  31. -- . client.run //创建好了对象.run
  32. -- 3.1 submitApplication //this.appId = submitApplication() 提交应用;
  33.       launcherBackend.connect() //Backend叫后台
               yarnClient.init(yarnConf) 根据yarn的配置进行初始化; yarnClient.start()启动客户端 ,目的是与yarn做交互;
               appId = newAppResponse.getApplicationId()//获取一个appId,yarn的全局id,根据id可以查询它的所有信息;
                yarnClient.submitApplication(appContext) //Client向yarn的RM提交应用,由submitApplication(抽象方法->实现类)来完成;
                rmClient.submitApplication(request); //跟RM做关联
  34. // commands(Client): JAVA_HOME/bin/java org.apache.spark.deploy.yarn.ExecutorLauncher //不是集群模式 Spark-shell只能是client模式,不能是集群!!
  35. // commands(Clustor):JAVA_HOME/bin/java org.apache.spark.deploy.yarn.ApplicationMaster//如果是集群模式,isClusterMode就把类对名字给amClass;启动一个ApplicationMaster进程
              //这些commands上边封装好的指令类都提交给yarn,由yarn来执行,这时Client客户端就结束了;
  36. -- 3.1. createContainerLaunchContext //要去封装一个指令bin/java javaOpts ++ amArgs(这个参数中Seq(amClass)) ++
  37.           启动一个进程执行ApplicationMaster
  38. -- 3.1. createApplicationSubmissionContext
    val containerContext = createContainerLaunchContext(newAppResponse)
    val appContext = createApplicationSubmissionContext(newApp, containerContext) //它调用的是上边的;
  39. // 向Yarn提交应用
  40. -- 3.1. yarnClient.submitApplication(appContext) //把上边封装好的给yarnClient(相当于这些指令发给它);在yarn中执行,Client把类提交给yarn之后就完成了

yarn的调度流程 https://www.cnblogs.com/shengyang17/p/10321228.html

  1. SparkSubmit进程(启动一个java虚拟机即启动一个进程名字就叫SparkSubmit -->调用Client方法,YarnClient去创建yarn客户端并启动, submitApplication()---launcherBackend.connect()
  1. ClientsubmitApplication(抽象方法->去找它的实现类YarnClientImplRM提交应用完成之后; rmClient.submitApplication(request)来跟RM做关联;
  2. createContainerLaunchContext启动一个进程执行ApplicationMaster
  1. yarnClient.submitApplication(appContext) //把上边封装好的给yarnClient(相当于这些指令发给它),Client把类提交给yarn之后就完成了
  1. ApplicationMaster
  2.  
  3. -- . main
  4.  
  5. -- 1.1. new ApplicationMasterArguments(args) //同上,封装了--jar, --class等
  6. -- 1.2. master = new ApplicationMaster(amArgs, new YarnRMClient)
  7.       yarnConf
              heartbeatInterval心跳周期
              RpcEnv : RPC 进程和进程的通信协议
              RpcEndpoint 终端   RpcEndpointRef:终端引用
  8. -- 1.3. master.run
  9. // 集群模式它运行的是Driver;如果不是集群模式运行的就是ExecutorLauncher
  10. -- 1.3. runDriver() //isClusterMode
  1. // 启动用户应用 就是提交的类
  2. -- 1.3.1.1 startUserApplication()--->即启动一个Driver线程
  3. // 从命令行参数中获取--class的值,然后获取main方法; 用类加载器加载类 userClass封装类名(从命令行中获取)
  4. -- val mainMethod = userClassLoader.loadClass(args.userClass).getMethod("main", classOf[Array[String]])
      
  5. -- new Thread("Driver").start() ==> --class.invoke(main)
  6.   启动之前:userThread.setContextClassLoader(userClassLoader) //类加载器放进去
                  userThread.setName("Driver") //真正的Driver是一个线程,只是把这个线程起个名字叫Driver
                  userThread.start() //启动之后肯定会运行线程的run方法 mainMethod.invoke(main);创建SparkConetext的那个类叫Driver,就是你写的那个main方法
  7.               虽然start,但什么时候执行不确定, userClassThread.join() //主线程不能结束,要等待它
                      rpcEnv = sc.env.rpcEnv构建环境对象
                  
    // 向Yarn注册AM
  8. -- 1.3.1.2 registerAM()//client.
  9.     allocator = client.register()
                  allocator.allocateResources() 它们进行注册和分配,申请资源,看看哪个资源可用 allocateResources资源列表;
                 val allocatedContainers = allocateResponse.getAllocatedContainers() //以container的形式把资源给你,allocatedContainers是列表 
  10. // 分配可用资源 (移动数据不如移动计算(计算在Driver,数据在executor,任务task发给哪个节点),本地化级别)
  11. -- handleAllocatedContainers
  12.     本地化(计算和数据在同一个进程里)本地化(进程本地化即计算和数据中同一个进程 |节点本地化即一个节点有多个executor,发给另外一个executor|机架本地化,发给另外一个nodemanager)
  13. -- runAllocatedContainers() //运行已经分配好的容器
  14.  
  15. -- launcherPool.execute(线程) <-- if(launchContainers) private val launcherPool = ThreadUtils.newDaemonCachedThreadPool //ThreadPool线程池 --> val threadPool = new ThreadPoolExecutor(),底层还是调用了jdk的线程池的操作
  16.     launcherPool.execute(new Runnable{ run(方法 new ExecutorRunnable).run( nmClient(即nodemanager的关联) ) }) //启动一个线程来执行操作,从线程池里边去执行一个线程
  17. -- ExecutorRunnable.run() //nmClient.init(conf)  nmClient.start()  startContainer() 要去连接NM
  18.  
  19. -- nmClient.start()
  20. // 启动容器
  21. -- startContainer
  22. // JAVA_HOME/bin/java org.apache.spark.executor.CoarseGrainedExecutorBackend //执行器的后台,数据对接收处理,在NM中执行;
  23. -- val commands = prepareCommand()
  24. -- nmClient.startContainer(container.get, ctx) //由NM(AM就在这里边)来启动这个容器;xtx就是commands指令
  25.  
  26. CoarseGrainedExecutorBackend 粗粒度的
  27. -- . main
  28.  
  29. -- 1.1. run //var executor: Executor = null //所谓的Executor是后台CoarseGrainedExecutorBackend的一个计算对象,就是一个属性;
  30.   SparkEnv.createExecutorEnv()   env.rpcEnv.setupEndpoint()
  31. env.rpcEnv.awaitTermination() //主线程不会结束,需要等待它的结束
               executor = new Executor() //在里边构建对象,跟CoarseGrainedExecutorBackend关系是很紧密的
                case LaunchTask(data) => executor.launchTask() //executor启动任务
             -- 1.1.1 env.rpcEnv.setupEndpoint("Executor", new CoarseGrainedExecutorBackend //把当前的Executor对象创建出来; setupEndpoint设置,它是个抽象方法=>实现类NettyRpcEnv
                    override def setupEndpoint(): RpcEndpointRef = {dispatcher.registerRpcEndpoint(name, endpoint)} //把当前对象进行封装成name把它绑定中一起,形成注册效果就可以实现消息对发送和接收;
                    registerRpcEndpoint完成注册
  1.  
  1. ApplicationMaster
    master = new ApplicationMaster.run()即runDriver()->startUserApplication()--->即启动一个Driver线程(通过反射来获取类名).join()还没启动, Yarn注册AM--registerAM
    client.register().allocateResources() 进行注册和分配,申请资源,看看哪个资源可用 allocateResources资源列表 allocateResponse.getAllocatedContainers() //以container的形式把资源给你
  1. 分配可用资源--handleAllocatedContainers--runAllocatedContainers() //运行已经分配好的容器
  1. if(launchContainers)--> launcherPool.execute(线程)->.run(ExecutorRunnable.run()),nmClient(即与nodeManager的关联)
    nmClient.init(conf)  nmClient.start() nmClient.startContainer(container.get, ctx) //由NM(AM就在这里边)来启动这个容器
  1. CoarseGrainedExecutorBackend
    所谓的Executor是后台CoarseGrainedExecutorBackend的一个计算对象,就是一个属性;
    case LaunchTask(data) => new Executor().launchTask() //executor启动任务
  1. RpcEndpointAddress
  2. NettyRpcEndpointRef
  3. new EndpointData() //往终端里传数据 --> val inbox = new Inbox(ref, endpoint)//收件箱
  4. -->inbox.synchronized {messages.add(OnStart)} //往消息里发送数据,给当前终端CoarseGrainedExecutorBackend发送OnStart消息
  5. 当前终端是可以通信的;ThreadSafeRpcEndpoint extends RpcEndpoint RpcEndpoint终端是有生命周期的:
  6.   The life-cycle of an endpoint is: constructor构造方法 -> onStart启动 -> receive* -> onStop关闭
  7. CoarseGrainedExecutorBackend接收到消息就会去调用OnStart
           -->rpcEnv.asyncSetupEndpointRefByURI(driverUrl).flatMap 通过URI异步的去安装引用,通过一个地址得到远程对象对引用
  8. 通过driverUrl地址建立了两个executor之间的通信,而且是异步发送;
  9. "Connecting to driver: " + driverUrl 找到Driver所在进程(AM),异步的这个操作等同于拿到了与ApplicationMaster的关系;
  10. driver = Some(ref) 起完driver
  11. ref.ask[Boolean](RegisterExecutor()) 发送RegisterExecutor请求,它会去反向注册
  12. driver应该接收消息,它是一个线程不能接收,通过以下方法:
  13.    SparkContext SchedulerBackend属性 它的实现类CoarseGrainedSchedulerBackend(driver这边的引用),而CoarseGrainedExecutorBackendexecutor的后台引用;
  14. 它们之间通信,刚刚ref发送了消息,它就接收到--->case RegisterExecutor PartialFunction偏函数中(所有的模式匹配的case都是);
  15. 反向注册就是建立executordriver之间的关系;
  16. if (executorDataMap.contains(executorId)) {
  17. executorRef.send(RegisterExecutorFailed("Duplicate executor ID: " + executorId))
  18. //如果executorDataMap的数据集合中包含executorId,它向executor的引用发送注册失败的消息;else就启动
  19. context.reply(true)
  20. executorRef.send(RegisteredExecutor)//向远程的executor引用发送消息说在driver中已经注册好了可以启动了;
  21. }

①脚本启动,执行SparkSubmit里面的main方法;

②反射获取Client类里边的main方法;

③封装并发送指令:bin/java ApplicationMaster

④选择一台NM机器启动AM

执行spark-submit实际上执行的是$SPARK_HOME/spark-class -->执行一个java类  java  org.apache.spark.launcher.Main调它的main方法(它就是检查参数、构建CommandBuiler指令)--->最终真正要执行 这个类 org.apache.spark.deploy.SparkSubmit xxx是命令行参数,它就会启动一个叫SparkSubmit进程

SparkSubmit类,对命令行参数进行了封装,准备提交环境,不是集群模式它会拿到指令中的--class所传的类, if (isYarnCluster)-->> org.apache.spark.deploy.yarn.Client 

  1. 利用反射通过类名获取类的信息,利用反射通过类信息获取Main方法,调用Clientmain方法: Client的调用只是一个普通方法的调用
  1. 根据yarn的配置进行初始化创建yarn客户端准备去跟yarn打交道(要有yarnserver集群), yarnClient.start()启动客户端,获取一个appId,yarn的全局id,根据id可以查询它的所有信息;
    ClientyarnRM提交应用, 集群模式通过submitApplication来完成,启动JAVA_HOME/bin/java org.apache.spark.deploy.yarn.ApplicationMaster这个进程类(主要是为了与Yarn做关联)
  1. 这些commands上边封装好的指令|类都提交给yarn,由yarn来执行,这时Client客户端就结束了;

RM找一个节点NM来启动ApplicationMaster--java command;虽然只是不是集群模式它的类是ExecutorLauncher,但底层它同样还是调用的是ApplicationMaster.main;

集群模式 集群模式它运行的是Driver,启动用户应用 就是--class提交的类(通过类加载器);

  1. new Thread("Driver").start() Driver只是ApplicationMaster进程中的一个线程起名叫Driver 之后它运行的是--class.invoke(main)就是你写的那个有sparkContextmain方法的那个类
    称为Driver类; YarnRM注册AM,并申请资源;RM会返回一个资源列表,去进行分配资源,资源是以container的方式;
  1. 分配可用资源 (移动数据不如移动计算(计算在Driver,数据在某个节点executordriver来分配任务task发给哪个节点),本地化级别);
    AM建立与NM的关联,向指定的资源NM发送指令; NM就会启动,它启动它的container容器;
  1. //启动一个进程 JAVA_HOME/bin/java org.apache.spark.executor.CoarseGrainedExecutorBackend //执行器的后台,数据对接收处理,在NM中执行,粗粒度的执行器后台
  1. ;所谓的Executor是后台CoarseGrainedExecutorBackend的一个计算对象,就是一个属性;
  1. 反向注册就是建立executordriver之间的关系,告诉driver我有一个executor已经启动了;
  1. 向远程的executor引用发送消息说在driver中已经注册好了可以启动了;

RDD中的数据变成一个个分区的数据,一个个分区变成任务

RDD(对数据计算逻辑的 抽象,里边没有数据)提交

任务的划分、分配、执行

  1. RDD
  2.  
  3. -- . collect
  4.  
  5. -- 1.1 SparkContext.runJob
  6.  
  7. -- dagScheduler.runJob() //有向无环图,有放向不能形成一个环,血缘关系形成的一个有向无环图调度器
  8.  
  9. -- submitJob()
  10.  
  11. -- eventProcessLoop.post( JobSubmitted()) //它等同往里边放了一个样例类;发送消息JobSubmitted
  12.  
  13. -- dagScheduler.handleJobSubmitted() //接收到JobSubmitted消息
  14.  
  15. DAGScheduler
  16.  
  17. -- . handleJobSubmitted
  18.  
  19. // 划分阶段-Stage
  20. -- 1.1 createResultStage //finalStage = createResultStage(finalRDD, func, partitions, jobId, callSite) val job = new ActiveJob()构建job对象即一个作业;有多个行动算子即执行了多个作业;
  21. // 提交阶段,有多少个阶段State val missing = getMissingParentStages(stage).sortBy(_.id) 这个阶段有没有上级即上一饿阶段Stage呢
  22. -- 1.2 submitStage
  23.  
  24. // 提交任务 ,如果有上一个阶段就提交上个阶段,如果还有上个阶段就继续提交上个阶段,如果没有就提交现阶段中的任务
  25. -- 1.2. submitMissingTasks提交任务,missing表没有上级了 //if (missing.isEmpty) 如果上一个阶段为空没有,则submitMissingTasks提交task //如果有阶段就把上一个阶段给提交了 submitStage(parent)
  26. // 将任务进行封装,进行调度 stage 模式匹配 ShuffleMapStage | ResultStage ,spark中只有两种模式匹配;
  27. val tasks: Seq[Task[_]] 构建任务集合{对stage进行操作}
  28. 如果上ShuffleMapStage会计算分区partitionsToCompute.map <=>new ShuffleMapTask多少个分区变成多少个任务
  29.  
  30. -- 1.2. taskScheduler.submitTasksTaskSet//任务调度器提交任务<--if (tasks.size > 0), 把任务封装成一个TaskSet集合传进来
  31. submitTasks的实现类TaskSchedulerImpl backend.reviveOffers()
  32. reviveOffers的实现类CoarseGrainedSchedulerBackend ->reviveOffers->makeOffers()-->launchTasks(scheduler.resourceOffers(workOffers))
  33.  
  34. // 启动任务
  35. -- 1.2. launchTasks //拿到了一个个任务; val serializedTask = ser.serialize(task)序列化
  36. executorData.executorEndpoint.send(LaunchTask(new SerializableBuffer(serializedTask)))从executor中取出一个终端,从当前后台去找的executor的后台
  37. 发送一个LaunchTask启动任务的消息
  38.  
  39. CoarseGrainedExecutorBackend
  40.  
  41. -- LaunchTask //receive接收到一个消息LaunchTask,表让executor执行这个任务 val taskDesc = ser.deserialize[TaskDescription](data.value)反序列化
  42.  
  43. -- executor.launchTask() //拿到任务,executor再去启动 --> val tr = new TaskRunner(context, taskId = taskId, attemptNumber = attemptNumber, taskName,serializedTask)
  44. // 运行任务 threadPool.execute(tr) //线程池在执行某个线程操作即TaskRunner extends Runnable -->run
  45. -- task.run //释放内存更新状态等;
  1. DAGScheduler
  1. 划分阶段 finalStage = createResultStage(finalRDD..);
  1. ShuffleMapStage | ResultStage spark中只有两种模式匹配;
  1. taskScheduler.submitTasksTaskSet//任务调度器提交任务<--if (tasks.size > 0), 把任务封装成一个TaskSet集合传进来;
  1.  

Spark通信架构

RPC通信:远程过程(服务)调用-(两个进程之间的通信)
RPC的实现有多种,其中有RMI,原理:
一个Process1中的client去调用它的service,要让Process2中的service去执行;通过socket,两个进程的service一定是个接口,那么client怎么去调用接口呢
通过动态代理(把接口代理为对象来使用),client通过代理对象Proxy与service去关联,代理对象会完成两个进程中socket的交互;

有封装好的框架可以使用;

Spark2.x版本使用Netty通讯框架作为内部通讯组件。spark 基于netty新的rpc框架借鉴了Akka的中的设计(随着版本升级已不再使用Akka,用Netty),它是基于Actor模型;

 Actor模型

  1. Actor模型:
  2. 有多个对象,Object1, Object2 Object3都给Object1发消息,都可以发消息(异步操作,发完该干嘛干嘛去;同步是发完之后不能做别的要等待它的返回),但消息的先后由自己决定
  3. 接收消息的一方Object1有个收件箱Inbox(消息不是马上执行的),它会去周期性的去取即轮询操作,有就去处理;
  4. Object1Inbox中查询到消息,发件箱Outbox(对应每一个对象),可以有多个发件箱可以同时发送
  5. Outbox(Object2) ---> Object2中相应的要有收件箱Inbox(query),则Object1中要有Object2中地址的引用;
  6. Actor模型中不叫Object了改叫EndPointOutbox(Object2)中的Object2引用改叫EndPointRef
  7. Ref即远程的引用,要有地址去连接RpcAddress,终端和终端的引用互相发生关联;

Endpoint(Client/Master/Worker)有1个InBox和N个OutBox,每一个引用就是一个OutBox,多个OutBox可以同时发送消息

Netty 

NETT底层还是socket

  1. IO中三种不同的处理方式:
  2. BIO(最慢,阻塞式IO,读取文件时要等着不能去做别的事),
  3. NIO(非阻塞式IO), 性能高,拿一个线程不断的去轮询,还可以去做其他事(只是在做事的过程中要不断的去问下好了没); 也叫多路IO复用,redis linux
  4. AIO(异步非阻塞式IO),读取文件时遇到阻塞可以去做别的事,没有阻塞了它会把文件直接发送给你,不需你不断的去轮询的去问
        Netty就是基于AIO ;但是linux中不能用,windows可以

NettyRpcEnv是整个通信的环境,RpcEndpoint 终端,可以send和ask消息,还可以receive接收消息,请求和接收消息都要走Dispatcher调度器,它来决定你发送的消息该往收件箱还是发件箱走;在收件箱按照队列方式排列好,周期性的去取FIFO(先进先出),消息处理完之后发往send另外一个RpcEndPoint,往发件箱OubBox里走(会有一个远程地址RpcEndpointAddress-->该给谁发消息);发消息时会有一个TransportClient,往远程去发(通过Netty网络通信去发);

远程TransportServer接收消息又回到了Inbox,处理再返回;  ===>整个过程形成8字环绕

Spark任务调度

RDD是对数据计算逻辑的抽象;真正执行(行动算子)的时候才会计算; 

  1. DAGSchedulerhandleJobSubmitted这个方法中完成了阶段的划分 finalStage = createResultStage(finalRDD, func, partitions, jobId, callSite)
  2. --->val stage = new ResultStage(id, rdd, func, partitions, parents, jobId, callSite) //结果阶段,作业执行一遍这个对象也是就创建一遍
  3. 把当前作业当作整体形成完整的阶段--称作ResultStage
  4. val parents = getOrCreateParentStages(rdd, jobId) //参数rdd是handleJobSubmitted(finalRDD,xxx)给的,即当前要执行行动算子的rdd叫做finalRDD
  5. private def getOrCreateParentStages(rdd: RDD[_], firstJobId: Int): List[Stage] = {
  6. getShuffleDependencies(rdd).map { shuffleDep =>
  7. //把rdd传进去获取shuffle的依赖关系,.map把得到的结果进行转换;getShuffleDependencies返回的结果是parents val parents = new HashSet[ShuffleDependency[_, _, _]]
  8. case shuffleDep: ShuffleDependency[_, _, _] => parents += shuffleDep
  9. 先压栈new Stack[RDD[_]].push(rdd),循环如果不为空就弹出来.pop()
  10. 判断依赖关系中有没有shuffle依赖,有就加上 parents += shuffleDep
  11. case dependency => waitingForVisit.push(dependency.rdd)//没有shuffle就把上一层的rdd取出来,再做循环,push、pop不断的取上一级做判断
  12. getOrCreateShuffleMapStage(shuffleDep, firstJobId) //它又new ShuffleMapStage()
  13. }.toList
  14. }
  15. ShuffleMapStageResultStage两个阶段,最后构建job、设定、提交submitJob

 spark任务的划分、分配和执行

RDD读取file数据(按一行一行的读取),放到3个分区;发现0号分区不是想要的比如统计wordcount,分解产生了一个新的rdd(所有的转换算子等同于产生了一个新的rdd,rdd是不可变数据集),把一行一行数据变成一个个的,flatMap;转换结构如(a, 1)这样的结构map;reduceBykey打乱重组,产生shuffle过程

有向无环图的调度器,运行并且提交job作业(会进行简单的判断,jobWaiter传到样例类一旦发生特殊事件就产生特殊的方法 handlerJobSubmit完成了整个过程的阶段划分resultStage); 把当前作业当成一个整体,形成了一个完整的阶段称为resultStage;

阶段怎么划分成一个个任务的了?

  1. map->new MapPartitionsRDD -->extends RDD[U](prev)-->def this(@transient oneParent: RDD[_]) =this(oneParent.context, List(new OneToOneDependency(oneParent)))
  2. OneToOneDependency[T](rdd: RDD[T]) extends NarrowDependency[T](rdd) ,源码中只有窄依赖,不是窄的就是宽的呗就是有shuffle
  3. 创建了两个Stage,给的是finalStage, 创建job,提交submitStage(finalStage)
  4. val missing = getMissingParentStages(stage).sortBy(_.id) //看看有没有上一级的阶段即查找ShuffleMapStage
  5. new HashSet[Stage].push(stage.rdd) 把当前的rdd传到栈里-压栈,---不为空-->弹栈.pop()
  6. -->visit() rdd.dependencies取出它的依赖关系,找到shuffle的依赖关系,val mapStage = getOrCreateShuffleMapStage(shufDep, stage.firstJobId)有就拿来没有就创建新的
  7. missing += mapStage //放到hashSet里 missing.toList
  8. missing不为空 --> for (parent <- missing) {submitStage(parent)}循环遍历提交每一个Stage
  9. 准备提交ResultStage时把它ShuffleMapStage取出来; submitStage(parent)}又自己调自己,找它ShuffleMapStage的上一级,为空-->submitMissingTasks(stage, jobId.get)提交任务
  10. 提交的是shuffleStage找的时候是一层层往上找,提交时是从最开始那一层层往下submitWaitingChildStages(stage)
  11. 提交任务:
  12. 只有两种类型的Stage
  13. case s: ShuffleMapStage =>
  14. case s: ResultStage =>
  15. taskIdToLocations,最优位置决定计算在哪里算,task和位置进行关联来决定在哪个executor中执行 getPreferredLocs(stage.rdd, p)
  16.  
  17. case stage: ShuffleMapStage => partitionsToCompute.map计算分区 locs part ShuffleMapTask //一个阶段中多个分区map转换一个分区形成一个任务
  18. case stage: ResultStage => p locs part ResultTask
  19. shuffle来划分stage,因为shuffle是要落盘的;flatmapmap处理数据中间不停留,直接处理往下走,RDD之间每个分区的数据都是连贯的,但是
  20. shuffle阶段它就不连贯了,在这个阶段它要等待所有的数据都处理完了,shuffle是要全落盘的;这也是划分阶段的原因;
  21. 任务跟阶段有关系,有多少个分区就有多少个任务
  22. taskScheduler.submitTasks(new TaskSet()) //任务的调度器它来提交任务,封装成任务集
  23. val manager = createTaskSetManager(taskSet, maxTaskFailures)//任务集管理器
  24. schedulableBuilder.addTaskSetManager(manager, manager.taskSet.properties) //manager会放到任务池中
  25. --->FIFOSchedulableBuilder 可以往里边放任务addTaskSetManager{ rootPool.addSchedulable(manager)} //任务池,TaskPool把任务划分好了来不及执行,可能划分好任务时资源还没申请到,
  26. backend.reviveOffers() --> launchTasks(scheduler.resourceOffers(workOffers))查找任务集
  27. resourceOffers 看看有没有可用的资源executor 拿到之后 launchTasks()[TaskDescription] ser.serialize(task)把任务序列化,executorDataexecutorData.executorEndpoint.send(LaunchTask(new SerializableBuffer(serializedTask)))
  28. 找到executor向它的终端发送消息--启动task,把得到的任务发送过去;
  29. 反向注册目的是启动一个Executor之后告诉Driver我准备好了-->可以发任务了;
  30. 启动:
  31. case LaunchTask(data) => if (executor == null) -->val taskDesc = ser.deserialize[TaskDescription](data.value)反序列化得到任务对象
  32. executor.launchTask() -->run() task.run() //只有两种task
  33. ResultTask为例:
  34. runTask(),shuffle前把数据写到文件里-->rdd.iterator读取数据func(context, rdd.iterator(partition, context))
  35. -->ShuffleRDD compute .getReader .read() 读取的是shuffle之前往里写的数据
  36. -->ShuffleMapTask阶段的最后一个RDD runTask() .getWriter 由它来写
  1. taskScheduler.submitTasks(new TaskSet()) //任务的调度器来提交任务把任务封装成一个TaskSet任务集
    val manager = createTaskSetManager(taskSet..)//任务集管理器
    schedulableBuilder.addTaskSetManager(manager, manager.taskSet.properties) //manager会放到任务池中
    -->FIFOSchedulableBuilder 可以往里边放任务addTaskSetManager{ rootPool.addSchedulable(manager)} //任务池
  1. backend.reviveOffers(), launchTasks(scheduler.resourceOffers(workOffers))查找任务集, 拿到之后launchTasks(), ser.serialize(task)把任务序列化
  1. 特质SchedulerBackend--实现类--> CoarseGrainedSchedulerBackend它是(driver这边的引用);
  1.  

Spark内核的更多相关文章

  1. 大数据计算平台Spark内核解读

    1.Spark介绍 Spark是起源于美国加州大学伯克利分校AMPLab的大数据计算平台,在2010年开源,目前是Apache软件基金会的顶级项目.随着 Spark在大数据计算领域的暂露头角,越来越多 ...

  2. (升级版)Spark从入门到精通(Scala编程、案例实战、高级特性、Spark内核源码剖析、Hadoop高端)

    本课程主要讲解目前大数据领域最热门.最火爆.最有前景的技术——Spark.在本课程中,会从浅入深,基于大量案例实战,深度剖析和讲解Spark,并且会包含完全从企业真实复杂业务需求中抽取出的案例实战.课 ...

  3. 大数据计算平台Spark内核全面解读

    1.Spark介绍 Spark是起源于美国加州大学伯克利分校AMPLab的大数据计算平台,在2010年开源,目前是Apache软件基金会的顶级项目.随着Spark在大数据计算领域的暂露头角,越来越多的 ...

  4. 【大数据】Spark内核解析

    1. Spark 内核概述 Spark内核泛指Spark的核心运行机制,包括Spark核心组件的运行机制.Spark任务调度机制.Spark内存管理机制.Spark核心功能的运行原理等,熟练掌握Spa ...

  5. Spark内核源码解析

    1.spark内核架构常用术语 Application:基于spark程序,包含一个driver program(客户端程序)和多个executeor(线程) Driver Progrom:代表着sp ...

  6. 【Spark 内核】 Spark 内核解析-上

    Spark内核泛指Spark的核心运行机制,包括Spark核心组件的运行机制.Spark任务调度机制.Spark内存管理机制.Spark核心功能的运行原理等,熟练掌握Spark内核原理,能够帮助我们更 ...

  7. 【Spark 内核】 Spark 内核解析-下

    Spark内核泛指Spark的核心运行机制,包括Spark核心组件的运行机制.Spark任务调度机制.Spark内存管理机制.Spark核心功能的运行原理等,熟练掌握Spark内核原理,能够帮助我们更 ...

  8. Spark内核解析

    Spark内核概述 Spark内核泛指Spark的核心运行机制,包括Spark核心组件的运行机制.Spark任务调度机制.Spark内存管理机制.Spark核心功能的运行原理等,熟练掌握Spark内核 ...

  9. 11、spark内核架构剖析与宽窄依赖

    一.内核剖析 1.内核模块 1.Application 2.spark-submit 3.Driver 4.SparkContext 5.Master 6.Worker 7.Executor 8.Jo ...

  10. [Spark内核] 第28课:Spark天堂之门解密

    本課主題 什么是 Spark 的天堂之门 Spark 天堂之门到底在那里 Spark 天堂之门源码鉴赏 引言 我说的 Spark 天堂之门就是SparkContext,这篇文章会从 SparkCont ...

随机推荐

  1. window.location.href 传参中文乱码问题!!!

    不是所有地方都会用Ajax  当你使用window.location.href 来传中文参数的时候 如何避免乱码问题 js 是这样写的    下面代码中  方式 封装编码  参数 username  ...

  2. netty的基本介绍

    一.什么是netty?为什么要用netty netty是jboss提供的一个java开源框架,netty提供异步的.事件驱动的网络应用程序框架和工具,用以快速开发高性能.高可用性的网络服务器和客户端程 ...

  3. Redis非关系型数据库

    1.简介 Redis是一个基于内存的Key-Value非关系型数据库,由C语言进行编写. Redis一般作为分布式缓存框架.分布式下的SESSION分离.分布式锁的实现等等. Redis速度快的原因: ...

  4. Thunk

    Thunk https://en.wikipedia.org/wiki/Thunk In computer programming, a thunk is a subroutine used to i ...

  5. [NLP] 酒店名归类

    目标: 我们内部系统里记录的酒店名字是由很多人输入的,每个人输入的可能不完全一样,比如,‘成都凯宾斯基大酒店’, ‘凯宾斯基酒店’, ‘凯宾斯基’, 我们的初步想法是能不能把大量的记录归类,把很多相似 ...

  6. Hadoop yarn任务调度策略介绍(转)

    理想情况下,我们应用对Yarn资源的请求应该立刻得到满足,但现实情况资源往往是有限的,特别是在一个很繁忙的集群,一个应用资源的请求经常需要等待一段时间才能的到相应的资源.在Yarn中,负责给应用分配资 ...

  7. 关于ElementUI中MessageBox弹框的取消键盘触发事件(enter,esc)关闭弹窗(执行事件)的解决方法

    好久没见了 在项目中遇到一个小小的需求,总结了一下! 详细我就不介绍了,相信大家用过的话,很了解.详见文档-----------> http://element-cn.eleme.io/#/zh ...

  8. jQuery的一些基本的函数和用jQuery做一些动画操作

    jQuery是对js的封装,因为js有一些不方便的地方.所以,jQuery才会去对js进行封装. jQuery对于标签元素的获取 $('div')或$('li') <!DOCTYPE html& ...

  9. 【干货】提取图片元数据之exiftool

    知识源:UC3Mx: INF.2x网络安全基础:实践方法 课程  第1周.讲座2.计算机取证  常见的法医痕迹  2.2.1.元数据 exiftool是一种查看,更新或删除元数据的工具.是Window ...

  10. windows 安装docker报错:Error checking TLS connection: ssh command error: command : ip addr show

    今天安装docker部署的时候总是再报这个错误. 报错的原因是初始化的时候出错了. 在docker 安装目录下有一个文件,如下图所示 将它复制到你电脑用户名目录下生成.docker 的文件夹中,如下图 ...