【转】Spark启动Job的简单流程分析

http://www.ylzx8.cn/gaoxingnenkaifa/cloud/1003859.html

从WordCount开始分析

编写一个例子程序

编写一个从HDFS中读取并计算wordcount的例子程序:

packageorg.apache.spark.examples

 

importorg.apache.spark.SparkContext

importorg.apache.spark.SparkContext._

 

objectWordCount{

 

defmain(args : Array[String]) {

valsc = newSparkContext(args(0),"wordcount by hdfs",

System.getenv("SPARK_HOME"),SparkContext.jarOfClass(this.getClass()))

//从hadoophdfs的根路径下得到一个文件

valfile =sc.textFile("/hadoop-test.txt")

valcounts =file.flatMap(line=> line.split(" "))

.map(word => (word,1)).reduceByKey(_+ _)

 

counts.saveAsTextFile("/newtest.txt")

}

 

}

 

生成SparkContext实例

在上面例子中,要执行map/reduce操作,首先需要一个SparkContext,因此看看SparkContext的实例生成

defthis(

master: String,

appName: String,

sparkHome: String = null,

jars: Seq[String] = Nil,

environment: Map[String, String]= Map(),

preferredNodeLocationData:Map[String, Set[SplitInfo]] = Map()) =

{

this(SparkContext.updatedConf(newSparkConf(), master, appName, sparkHome, jars, environment),

preferredNodeLocationData)

}

 

编写WordCount例子时使用了上面列出的构造函数,后面两个environment与preferredNodeLocationData传入为默认值。

调用updatedConf的单例函数,生成或更新当前的SparkConf实例。

调用SparkContext的默认构造函数。

1.生成并启动监控的Jettyui,SparkUI.

2.生成TaskScheduler实例,并启动。

此函数会根据不同的mastername生成不同的TaskScheduler实例。,yarn-cluster为YarnClusterScheduler。

主要用来启动/停止task,监控task的运行状态。

private[spark]vartaskScheduler= SparkContext.createTaskScheduler(this,master,appName)

taskScheduler.start()

3.生成DAGScheduler实例,并启动。

 

@volatileprivate[spark]vardagScheduler= newDAGScheduler(taskScheduler)

dagScheduler.start()

 

在scheduler进行start操作后,通过调用postStartHook来把SparkContext添加到appmaster中。

生成WorkerRunnable线程,通过nmclient启动worker对应的container。此container线程CoarseGrainedExecutorBackend的实例,此实例通过Executor实例来加载相关的task。

SparkContext.textFile生成RDD

此方法用来生成RDD的实例,通常读取文本文件的方式通过textFile来进行,并其调用hadoopFile来执行。

通过hadoopFile得到一个HadoopRDD<K,V>的实例后,通过.map得到V的值。并生成RDD返回。

deftextFile(path: String, minSplits: Int = defaultMinSplits):RDD[String] = {

hadoopFile(path,classOf[TextInputFormat], classOf[LongWritable], classOf[Text],

minSplits).map(pair=> pair._2.toString)

}

最终通过hadoopFile函数生成一个HadoopRDD实例。

defhadoopFile[K, V](

path: String,

inputFormatClass: Class[_ <:InputFormat[K, V]],

keyClass: Class[K],

valueClass: Class[V],

minSplits: Int = defaultMinSplits

): RDD[(K, V)] = {

//A Hadoopconfiguration can be about 10 KB, which is pretty big, so broadcastit.

valconfBroadcast= broadcast(newSerializableWritable(hadoopConfiguration))

valsetInputPathsFunc= (jobConf: JobConf) => FileInputFormat.setInputPaths(jobConf,path)

newHadoopRDD(

this,

confBroadcast,

Some(setInputPathsFunc),

inputFormatClass,

keyClass,

valueClass,

minSplits)

}

 

RDD函数的抽象执行

reduceByKey需要执行shuffle的reduce,也就是需要多个map中的数据集合到相同的reduce中运行,生成相关的DAG任务

valfile =sc.textFile("/hadoop-test.txt")

valcounts =file.flatMap(line=> line.split(" "))

.map(word => (word,1)).reduceByKey(_+ _)

counts.saveAsTextFile("/newtest.txt")

 

在以上代码中,textFile,flatMap,map,reduceByKey都是spark中RDD的transformation,

而saveAsTextFile才是RDD中进行执行操作的action.

以下引用http://my-oschina-net/hanzhankang/blog/200275的相关说明:

具体可参见:http://spark.apache.org/docs/0.9.0/scala-programming-guide.html。

1,transformation是得到一个新的RDD,方式很多,比如从数据源生成一个新的RDD,从RDD生成一个新的RDD

2,action是得到一个值,或者一个结果(直接将RDDcache到内存中)

所有的transformation都是采用的懒策略,就是如果只是将transformation提交是不会执行计算的,计算只有在action被提交的时候才被触发。

 

transformation操作:

map(func):对调用map的RDD数据集中的每个element都使用func,然后返回一个新的RDD,这个返回的数据集是分布式的数据集

filter(func): 对调用filter的RDD数据集中的每个元素都使用func,然后返回一个包含使func为true的元素构成的RDD

flatMap(func):和map差不多,但是flatMap生成的是多个结果

mapPartitions(func):和map很像,但是map是每个element,而mapPartitions是每个partition

mapPartitionsWithSplit(func):和mapPartitions很像,但是func作用的是其中一个split上,所以func中应该有index

sample(withReplacement,faction,seed):抽样

union(otherDataset):返回一个新的dataset,包含源dataset和给定dataset的元素的集合

distinct([numTasks]):返回一个新的dataset,这个dataset含有的是源dataset中的distinct的element

groupByKey(numTasks):返回(K,Seq[V]),也就是hadoop中reduce函数接受的key-valuelist

reduceByKey(func,[numTasks]):就是用一个给定的reducefunc再作用在groupByKey产生的(K,Seq[V]),比如求和,求平均数

sortByKey([ascending],[numTasks]):按照key来进行排序,是升序还是降序,ascending是boolean类型

join(otherDataset,[numTasks]):当有两个KV的dataset(K,V)和(K,W),返回的是(K,(V,W))的dataset,numTasks为并发的任务数

cogroup(otherDataset,[numTasks]):当有两个KV的dataset(K,V)和(K,W),返回的是(K,Seq[V],Seq[W])的dataset,numTasks为并发的任务数

cartesian(otherDataset):笛卡尔积就是m*n,大家懂的

 

action操作:

reduce(func):说白了就是聚集,但是传入的函数是两个参数输入返回一个值,这个函数必须是满足交换律和结合律的

collect():一般在filter或者足够小的结果的时候,再用collect封装返回一个数组

count():返回的是dataset中的element的个数

first():返回的是dataset中的第一个元素

take(n):返回前n个elements,这个士driverprogram返回的

takeSample(withReplacement,num,seed):抽样返回一个dataset中的num个元素,随机种子seed

saveAsTextFile(path):把dataset写到一个textfile中,或者hdfs,或者hdfs支持的文件系统中,spark把每条记录都转换为一行记录,然后写到file中

saveAsSequenceFile(path):只能用在key-value对上,然后生成SequenceFile写到本地或者hadoop文件系统

countByKey():返回的是key对应的个数的一个map,作用于一个RDD

foreach(func):对dataset中的每个元素都使用func

 

RDD的action中提交Job

在执行RDD的saveAsTextFile时调用SparkContext.runJob方法

saveAsTextFile方法,-->saveAsHadoopFile,最终调用SparkContext.runJob方法

defsaveAsTextFile(path: String) {

this.map(x=> (NullWritable.get(), newText(x.toString)))

.saveAsHadoopFile[TextOutputFormat[NullWritable,Text]](path)

}

......以下一行代码就是在saveASTextFile函数嵌套调用中最终调用的函数,调用SparkContext.runJob

self.context.runJob(self,writeToFile _)

SparkContext.runJob的定义:

defrunJob[T, U: ClassTag](rdd: RDD[T], func: (TaskContext, Iterator[T])=> U): Array[U] = {

runJob(rdd, func, 0until rdd.partitions.size,false)

}

SparkContext的最终执行runJob函数定义

defrunJob[T, U: ClassTag](

rdd: RDD[T],//此处是具体的RDD实例值

func: (TaskContext, Iterator[T])=> U,//具体的执行的action的逻辑,如reduceByKey

partitions:Seq[Int],//分区数组,一个数值从0到partitions.size-1

allowLocal: Boolean,//是否可以在本地执行

//result的处理逻辑,每一个Task的处理

resultHandler: (Int, U) =>Unit) {

valcallSite =getCallSite

valcleanedFunc= clean(func)

logInfo("Startingjob: " + callSite)

valstart =System.nanoTime

通过DAGScheduler.runJob去执行job的运行操作,请看下面的DAGScheduler处理job提交。

dagScheduler.runJob(rdd,cleanedFunc,partitions, callSite,allowLocal,

resultHandler,localProperties.get)

logInfo("Jobfinished: " + callSite+ ", took "+ (System.nanoTime - start)/ 1e9 + "s")

rdd.doCheckpoint()

}

posted @ 2015-07-07 09:15  MERRU  阅读(391)  评论(0)    收藏  举报