深度理解篇之SparkSubmit 任务提交-个人拙见

首先逻辑代码会写成一个类,那么这个类是怎么执行的呢?

会有一个主方法会有new SparkContext,new SparkConf,sc.texfile(...),这些代码是在哪执行的呢?而这点就和SparkSubmit的两种模式挂钩了,一个是client客户端模式,另一个是cluster模式,这两种模式代表的是什么呢?代表的是写的代码在Sparksubmit后是在本机执行还是将代码发送到集群中去执行,然后利用节点上的某个进程将它跑起来,那么这个代码就叫做Driver,因为代码里面最重要的第一个即使SparkContext,SparkContext就可以理解为Driver.而这两种模式的优缺点是什么呢?

Clinet模式

如果这台机子就是提交服务的,如果把Driver放在本机的话,会占用很大的内存的,应为在计算过程中不只是代码的执行会占用内存还有拉取数据,而且会产生的元数据也会在Driver端的内存上,类似于namenode ,所以说Driver的内存要大一些,因为在Spark计算框架当中,会有一个blockManager储存层,类似于HDFS,那么Driver,一个nameNode,它会在未来计算中,存储块的元数据信息,所以这时候会有一个client集群模式,

cluster模式:

cluster集群模式,Drvier会落到集群中去,与客户端没有关联了,客户端提交任务后可以退了,Driver端运行就可以了,Driver是可以由资源层去创建的,可以由资源的安全性来做他的ha,如果客户端来包括它的话,这两种模式只是Driver在启动的位置不同的差异,那么可以这样想,整个集群什么都没有,在SparkSumbit提交了之后....

提交任务执行

SparkSumbit之后,首先有的第一个角色就是Driver,在Driver启动之后 ,Driver里面会跑我们写的逻辑代码,首先new SparkContext,那么Driver中会有一个向资源层申请资源的过程,资源层可以是standalone的master和worker, Yarn nodemanager resourcemanager,无论怎么样都会申请一批executor,在申请资源的时候,由于不知道未来需要多少资源,不知道会有多少个作业要跑,所以默认情况下会使用贪婪模式,贪婪模式就是消耗集群所有的核,所有的资源,申请一大批executor,但是这个过程中我们可以在SparkSumbit提交任务时通过相关的参数来控制,可以控制所消耗的数量,而且这种申请资源的模式分为两个方向,一种是水平申请,一种是垂直申请。

什么是水平申请?

就是先遍历集群中的每个节点,每个节点都分配一个executor,当申请够了,申请结束了就不再申请了。

什么是垂直申请呢?

就是对一个节点一直申请executor,直到资源消耗干净了。

这两种模式供我们选择。资源申请完代表着一批executor产生了,那么空的集群中出现了Driver,出现了一批Executor,Executor这些进程会向Driver进行反向注册,那么Driver会持有了Executor的数量和位置等信息,反向注册完了之后,还有要做一些事情,比如说executor中blockManager的slave要向Driver注册等行为,最后当SparkContext完成了后,得到他的对象sc,Driver的中的代码是线程执行的,new SparkContext这段代码完成之后,代表着executor的反向注册成功了,下一步代码是sc.textFile(...),然后得到RDD,RDD进行转换,当转换过程中遇到action算子,那么Driver从SparkContext开始执行后,遇到action算子的时候,会进行阻塞,下面代码就不执行了,如果写的程序里面有几个action算子,那么代表就有几个job,那些job都是线性的,

就是如何让job并行起来呢 ?

可以在Driver里面抛出两个线程,一个里面有textfile(),另一个里面也有textfile(),在代码执行的过程中,各自遇到各自的action,各自的代码都会进行执行,其实就是SparkStreaming,SparkStreaming的consumer模式要接替一堆数据的话,跑起一个job,每隔一个5秒就会抛出一个job接数据,所以说有一个线程在一直启动,一个是每隔5秒发出一个job,....先不说这个多线程,对于单一一个线程,从new SparkContext开始到action算子触发,action算子一触发,会拿着前面的代码,执行DAGSchudler,它会把你action算子所在的RDD,把这个RDD传递给DAGScheduler,其实在我们的编程当中,一直new RDD的过程即RDD转换过程当中,每一个RDD都持有着前一个RDD的地址引用,其实RDD的转换过程当中,就是组建着一个linkedList,所以它传参的时候只穿的是最后一个RDD,通过最后一个RDD就可以拿到前面的一个RDD的引用,因为DAGSchulder里面触发的是递归的模式,因为最后一个action的RDD,因为DAGSchulder触发的是迭代和遍历,就是最后一个RDD往前进行回溯,shuffle之后要进行切割一下, 划分为一个Stage在找一个action方法再进行切割,所以这个递归中存的就是最后一个RDD,由于每一个RDD都是存的前面一个RDD的引用,在java序列化的知识点当中,如果想序列化一个对象,这个对象持有着另外一个对象,另外一个对象也继承了序列化的接口,那么你再序列化第一个对象的时候,另外一个序列化也随着他一起序列化了。反序列化的时候这两个对象也会序列化出来。所以向DAGSchulder传递最后一个action算子触发的RDD,在划分Stage的时候,会根据shuffle前面的一个RDD进行划分,因为这个Stage向前回溯会找到一系列的RDD,所以最终的stage变成task的时候,只需要对最后一个RDD进行序列化,那么他就会吧前面的那一系列task序列化成一个二进制文件,发出去之后,就会变成一个独立stage代码块.也就是DAGSchulder储存最后一个RDD向前面做递归,然后划分stage,然后递归触底,触底也就是说最前面一个RDD中,没有RDD了则意味着就结束了。回归的时候就第一个向后进行回归,划分一个个stage,这种回归是一种线性回归,如果前面一个stage 任务没有跑完,去跑后面,后面的是无法进行的,所以一定就是将最前面的那个stage传递给taskScheduler,那么taskScheduler收到了stage,那么间接地收到了最后的RDD,那么就会根据最后两个RDD的数量将其转换为TaskSet,构成一个任务集,这个任务集即使最后那个RDD分区的数量,那么taskSchdulder划分完毕之后,都是由blockManager进行传递的,就在Drvier里面,他自己就会把task把其中的某一个窄依赖的RDD引用,序列化成字节数组,他会把字节的这个RDD引用的序列化字节数组存到blockManager,然后通过rpc通信给需要执行任务的executor发送消息,需要去执行那个task任务,他会访问自己blockManager,由于没有blockManager,那么由他blockManager 代替他去访问Driver端blockManager,把这些东西拷回去,再反序列化就行了,一样在Driver里面做了广播变量,把数据集要发送给后续的executor,其实他也是将这些数据放到本机了,未来谁用谁考走,以 为他是多线程模型,如果这个Driver上跑的四个线程,都是针对着一个数据集,则只需要拷贝一次就可以了,应为它的blockManager是被他的本机所共享的。当任务发送完之后,任务执行过程当中是一种pipeline方式执行,其实pipeline就是一种迭代器嵌套的模型。在MapReduce当中,是两个迭代器嵌套的,Spark当中是多个窄依赖的RDD连起来的话,就是多个迭代器进行嵌套。当一个任务执行完毕之后,他不是最后那个final的任务,如果是中间的任务中间任务一定会从shuffle写出去,那么这个时候就牵扯到spark中一个相当重要的环节shuffle。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值