SparkWordCount源码原理

本文介绍了Spark中RDD的生成过程及任务调度机制,详细解释了从读取HDFS文件到执行Action操作如saveAsTextFile的具体步骤,包括Stage划分、Task调度与执行等关键环节。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

def textFile(
      path: String,
      minPartitions: Int = defaultMinPartitions): RDD[String] = withScope {
    assertNotStopped()
    hadoopFile(path, classOf[TextInputFormat], classOf[LongWritable], classOf[Text],
      minPartitions).map(pair => pair._2.toString).setName(path) //这里由于偏移量没用所以用_2
  }
  这是一个从左hdfs 向右的过程
  hdfs              sc.textFile  (带偏移量)         map 结果(去掉偏移量)生成了一个RDD
                HadoopRDD(LongWriteable,Text)       MapPartitionsRDD[String]
  hello lilei       (0,"hello lilei")               “hello lilei”
  hello lucy        (1,"hello lucy")                "hello lucy"

  上面右接下面左
  flatMap(_.split(" "))             map((_,1))                  reduceByKey(_+_)
  MapPartitionsRDD[String]      MapPartitionsRDD[String,Int]    ShuffledRDD[String,Int,Int]
  "hello,lilei,hello,lucy"  ("hello",1),("lilei",1)....     两个过程,分区聚合,全局聚合,中间是shuffle过程,洗牌,分区中 这里会宽依赖

  上面右接下面左

  saveAsTextFile("hdfs://...")
  MapPartitionsRDD[String,Int]

  初始设置分区,或者文件分区时,保存默认也是这些分区
  整个过程创建了6个RDD
//hdoopFile 返回一个RDD
 def hadoopFile[K, V](
      path: String,
      inputFormatClass: Class[_ <: InputFormat[K, V]],
      keyClass: Class[K],
      valueClass: Class[V],
      minPartitions: Int = defaultMinPartitions): RDD[(K, V)] = withScope {
    assertNotStopped()
    // A Hadoop configuration can be about 10 KB, which is pretty big, so broadcast it.
    val confBroadcast = broadcast(new SerializableConfiguration(hadoopConfiguration))
    val setInputPathsFunc = (jobConf: JobConf) => FileInputFormat.setInputPaths(jobConf, path)
    new HadoopRDD(
      this,
      confBroadcast,
      Some(setInputPathsFunc),
      inputFormatClass,
      keyClass,
      valueClass,
      minPartitions).setName(path)
  }

一个项目源码执行流程(主要是sparksubmit与sparkContext)

1、SparkSubmit  执行runmain()方法 根据传入的类名找到jar中的类,执行main方法
2、执行项目(以WC为例),执行WC的main 方法
3、WC的main方法中 一开始是 new 了一个SparkConf 与SparkContext
4、SparkContext 通过createSparkEnv() 调用SparkEnv的createDriverEnv()
5、在SparkEnv中SparkEnv的createDriverEnv()调用create方法
    create方法中调用AkkaUtils
6、在AkkaUtils里通过createActorSystem来创建Actor
7、SparkContext 通过creatTaskScheduler 来new 一个TaskSchedulerImpl(任务调度器)和SparkDeploySchedulerBackend(后端调度器)
8、SparkContext 通过new来创建了DAGScheduler(划分stage的)

TaskScheduler 的流程

启动TaskScheduler 是 TaskScheduler 的start方法
在Start方法中:先启动后端调度器 也就是SparkDeploySchedulerBackend (后端调度器的一个实现方法),
在SparkDeploySchedulerBackend的start方法中,调用父类的start方法(super.start())。父类的start方法中创建了driveractor。
然后在SparkDeploySchedulerBackend的start方法中,  创建了Command(准备了执行任务线程的类名)
ApplicationDescription(封装了Excutor需要用的参数,像占用内存大小,核数),接下来创建了一个
Appclient的实例将配置信息(包括appdesc)传进去。然后调用clien.start()。
在client 中创建了client的Actor。
在后端调度器这,一共创建了2个Actor,driverActor(与excutor通信),clientActor(与master通信)
后端调度器是在driver端启动。也就是driver端启动的driverActor和clientActor




通过sparkenv调用scheduler

client端与master端通信

1、在prestart方法中 主要是registerWithMaster,向master注册,这里有高可用集群,它会遍历所有master的url,然后与活跃的master发连接
2、master 端接收到client的注册消息,将client的信息加入到hashset(去重)与ArrayBuffer中(也就是存两份),还要信息持久化。然后master
向client发送响应消息(携带appid,和masterurl)。开始任务调度
3client 接收到master返回的注册响应消息,先改masterurl,再启动一个监听器,连接appid

接上面2后的任务调度(注册成功,master返回消息后 master启动)

只要新的计算资源加进来时都会发生资源调度。
两种调度方式  spreadOutApps (尽量打散,默认) (尽量集中)
spreadOutApps : 
1、从waitingApps(client保存的Arraybuffer)过滤取出合适的apps(活跃,内存是否足够,空闲的cpu核数),用来启动executor
2、尽量打散,把任务用循环的方式调度到尽量多的节点上。(尽量集中,意思是一个节点有多少核数,有限分配多少。尽量少节点)
3、在节点(worker)上启动executor   launchExecutor

work启动executor

先在master中
1、将executor的描述信息加入workerinfo中
2、master向worker发送消息,告诉worker启动executor
3、同时向client发送消息,告诉client executor已经启动

再 worker中接收到master的启动executor的消息。
1、worker接收到master消息,
2、内部创建一个空间,new 一个 executorrunner

再的同时 client接收到master的消息  (executor已经启动)
1client接收到后,监听executor的注册

CoarseGrainedExecutorBackend 中启动的executor进程的类

在main方法中,主要是封装参数,与执行run方法
在run方法中:创建Actorsystem对象  创建actor
在生命周期方法中
prestart: 向driver发送注册消息  ,接收在CoarseGrainedSchedulerBackend 中,
在CoarseGrainedSchedulerBackend中接收到上面发送的消息后,执行makeoffers方法(就是为提交任务做准备)

RDD的生成

先从一个action的算子看起,向saveasfile
1、拿到一个输出流(writer),
2、调用sparkcontext的 runjob方法 传入 writertoFile的函数
3、调用dagscheduler 的runjob方法,进行stage的切分。
4、调用submitjob,开始提交作业
5、submitjob中,首先得到分区数 , 调用eventprocessLoop(任务处理器,循环取出数据)
6、在dagscheduler中切分stage
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值