Spark学习笔记(6)源码解读之Job动态生成和深度思考
来源:互联网 发布:nodejs怎么运行js文件 编辑:程序博客网 时间:2024/05/29 18:35
本期内容:
1 Spark Streaming Job生成深度思考
2 Spark Streaming Job生成源码解析
1 Spark Streaming Job生成深度思考
前面的课程中已经讲了,Spark Streaming的Job是通过JobGenerator生成。这里说的Job和Spark Core中的Job不是一回事。Spark Streaming中的Job相当于Java中线程要处理的Runnable的业务逻辑的封装。Spark Core上的Job是一个运行的作业。
JobGenerator会利用DStream来生成Job。从流程上看,DStream一般分三种类型:
1、输入的DStreams。
2、输出的DStreams,是一个逻辑级别的Action,它是SparkStreaming框架提出的,它的底层还是会被翻译成物理级别的Action。所谓物理级别的Action是RDD的Action。
3、中间的业务处理的通过Transformation(转换)产生的DStream。也就是业务处理逻辑的中间过程。
产生DStream也可以说有两类:一类是根据数据源产生DStream,另一类是通过前面的DStream转换后生成DStream。
所以说,JobGenerator是根据DStream的依赖关系,或者说根据DStreamGraph来产生Job。
当然从时间维度上讲,JobGenarator会不断地产生Job。我们做连续不断的大数据任务时,如果不采用流式处理,一般会用定时任务。其实我们也是变相地在做流处理。不管是定时1分钟、1小时,甚至是1天,都类似在做流处理。应该更深刻的理解此前说过的一句话吧:一切没有经过流处理或近似流处理的数据,都将是没有价值的数据。原来的定时任务实际上是在做变相的流处理。我们相信,一切处理终将会被流处理完全统一。
2 Spark Streaming Job生成源码解析
其实,Spark Streaming的流处理实际上就是以时间作为触发器的。这和基于事件触发的Storm不同。
看看Spark Streaming应用程序代码的最开始的共同之处:定义SparkStreamingContext。给个例子:
val ssc = new StreamingContext(conf, Seconds(5))
这个5秒钟的Batch Duration(批处理时间间隔)就是用来设置定时器的时间长度。
系统翻译基于DStream的依赖关系,成为了RDD之间的依赖关系,而最后的处理是一个action,但由于这个处理是定义在一个方法中,而此方法没有被马上调用,也就没有马上执行。这是为了便于管理,要形成队列来依次处理。
Spark Streaming应用程序代码的最后面的共同之处:运行SparkStreamingContext的start():
ssc.start()
这里才是真正启动流处理执行的地方。启动SparkStreamingContext,会启动JobScheduler。
Spark Streaming作业的三大核心是:
1. JobGenerator:负责Job的生成。
2. ReceiverTracker:负责数据的接收。
3. JobScheduler:负责Job的调度执行。
JobGenerator、ReceiverTracker其实是在JobScheduler的start()中被启动。
先提供Job生成的总体轮廓。本期剖析的Job生成的主流程图如下所示:
先从JobScheduler.start开始剖析。
JobScheduler.start的代码片段:
class JobScheduler(val ssc: StreamingContext) extends Logging {
...
private val jobGenerator = new JobGenerator(this)
...
def start(): Unit = synchronized {
...
receiverTracker = new ReceiverTracker(ssc)
...
receiverTracker.start()
jobGenerator.start()
...
}
...
}
我们先看JobGenerator的start():
/** Start generation of jobs */
def start(): Unit = synchronized {
...
eventLoop = new EventLoop[JobGeneratorEvent]("JobGenerator") {
override protected def onReceive(event: JobGeneratorEvent): Unit = processEvent(event)
...
}
}
eventLoop.start()
if (ssc.isCheckpointPresent) {
restart()
} else {
startFirstTime()
}
}
注释也说明这是生成jobs。
代码中生成了EventLoop,并启动它。EventLoop是个消息接收器,其中有个消息队列,用于接收消息;它启动后,其中会有个线程不断地从该队列中取消息进行处理。
代码中覆盖了EventLoop的onReceive方法,OnReceive()里,一般不要做复杂耗时的操作,应该交给别的线程去处理。按照定义,OnReceive也就是调用了processEvent方法。
JobGenerator.processEvent的代码:
/** Processes all events */
private def processEvent(event: JobGeneratorEvent) {
...
event match {
case GenerateJobs(time) => generateJobs(time)
...
}
}
一般情况下,会执行 GenerateJobs(time)这种类型的事件消息。其相应的会执行generateJobs(time):
JobGenerator.generateJobs的代码:
/** Generate jobs and perform checkpoint for the given `time`. */
private def generateJobs(time: Time) {
...
Try {
jobScheduler.receiverTracker.allocateBlocksToBatch(time) // allocate received blocks to batch
graph.generateJobs(time) // generate jobs using allocated block
} match {
case Success(jobs) =>
val streamIdToInputInfos = jobScheduler.inputInfoTracker.getInfo(time)
jobScheduler.submitJobSet(JobSet(time, jobs, streamIdToInputInfos))
...
}
...
}
主要是先获取数据,然后对这些数据来生成相应的jobs,生成工作是调用DStreamGraph的generateJobs。
DStreamGraph.generateJobs的代码:
def generateJobs(time: Time): Seq[Job] = {
logDebug("Generating jobs for time " + time)
val jobs = this.synchronized {
outputStreams.flatMap { outputStream =>
val jobOption = outputStream.generateJob(time)
jobOption.foreach(_.setCallSite(outputStream.creationSite))
jobOption
}
}
logDebug("Generated " + jobs.length + " jobs for time " + time)
jobs
}
这里的outputDStream是DStream中最后处理的Dstream,根据DStreamGraph的最后面的outputDStream,从后往前推,
DStream.generateJob的代码:
/**
* Generate a SparkStreaming job for the given time. This is an internal method that
* should not be called directly. This default implementation creates a job
* that materializes the corresponding RDD. Subclasses of DStream may override this
* to generate their own jobs.
*/
private[streaming] def generateJob(time: Time): Option[Job] = {
getOrCompute(time) match {
case Some(rdd) => {
val jobFunc = () => {
val emptyFunc = { (iterator: Iterator[T]) => {} }
context.sparkContext.runJob(rdd, emptyFunc)
}
Some(new Job(time, jobFunc))
}
case None => None
}
}
代码的注释也说明了DStream间的操作是逻辑级别的,RDD之间的操作是物理的。
getOrCompute是获得指定时间的RDD。
其中的Job是Spark Streaming中定义的。这里并不是真正执行了Spark Core的作业。而是用函数jobFunc封装了Spark作业的执行本身。
这个Job类其实就是相当于Java中的Bean,并没有做Spark的作业的执行。
至此,JobGenarator.start中的消息处理的定义就剖析完了。再看其中的后续代码。
JobGenarator.start的代码:
/** Start generation of jobs */
def start(): Unit = synchronized {
...
eventLoop = new EventLoop[JobGeneratorEvent]("JobGenerator") {
override protected def onReceive(event: JobGeneratorEvent): Unit = processEvent(event)
...
}
}
eventLoop.start()
if (ssc.isCheckpointPresent) {
restart()
} else {
startFirstTime()
}
}
restart是利用checkPoint重新启动JobGenerator。我们主要看看startFirstTime。
JobGenerator.startFirstTime代码:
/** Starts the generator for the first time */
private def startFirstTime() {
val startTime = new Time(timer.getStartTime())
graph.start(startTime - graph.batchDuration)
timer.start(startTime.milliseconds)
logInfo("Started JobGenerator at " + startTime)
}
其中启动了DStreamGraph和RecurringTimer。
DStreamGraph.start对DStreamGraph中的各DStream做初始化。
RecurringTimer.start代码:
/**
* Start at the given start time.
*/
def start(startTime: Long): Long = synchronized {
nextTime = startTime
thread.start()
logInfo("Started timer for " + name + " at time " + nextTime)
nextTime
}
实际上是启动一个线程来定时调用回调函数。再回过头看timer的定义:
private val timer = new RecurringTimer(clock, ssc.graph.batchDuration.milliseconds,
longTime => eventLoop.post(GenerateJobs(new Time(longTime))), "JobGenerator")
其中的匿名的回调函数是发送GenerateJobs消息给eventLoop。
原来JobGenerator.processEvent中获得的消息是RecurringTimer定时触发的。
JobGenerator中的generateJobs剖析完了,再看后面的代码。
JobGenerator.generateJobs的代码:
/** Generate jobs and perform checkpoint for the given `time`. */
private def generateJobs(time: Time) {
...
Try {
jobScheduler.receiverTracker.allocateBlocksToBatch(time) // allocate received blocks to batch
graph.generateJobs(time) // generate jobs using allocated block
} match {
case Success(jobs) =>
val streamIdToInputInfos = jobScheduler.inputInfoTracker.getInfo(time)
jobScheduler.submitJobSet(JobSet(time, jobs, streamIdToInputInfos))
...
}
...
}
JobScheduler.submitJobSet的代码:
def submitJobSet(jobSet: JobSet) {
if (jobSet.jobs.isEmpty) {
logInfo("No jobs added for time " + jobSet.time)
} else {
listenerBus.post(StreamingListenerBatchSubmitted(jobSet.toBatchInfo))
jobSets.put(jobSet.time, jobSet)
jobSet.jobs.foreach(job => jobExecutor.execute(new JobHandler(job)))
logInfo("Added jobs for time " + jobSet.time)
}
}
jobExecutor是线程池,JobHandler是Runnable的子类。
JobHandler.run的代码:
private class JobHandler(job: Job) extends Runnable with Logging {
import JobScheduler._
def run() {
try {
...
if (_eventLoop != null) {
...
PairRDDFunctions.disableOutputSpecValidation.withValue(true) {
job.run()
}
...
} else {
// JobScheduler has been stopped.
}
} finally {
ssc.sc.setLocalProperty(JobScheduler.BATCH_TIME_PROPERTY_KEY, null)
ssc.sc.setLocalProperty(JobScheduler.OUTPUT_OP_ID_PROPERTY_KEY, null)
}
}
}
Job.run的代码:
def run() {
_result = Try(func())
}
此处,执行了Job中封装的业务逻辑。
阅读全文
0 0
- Spark学习笔记(6)源码解读之Job动态生成和深度思考
- 第6课:Spark Streaming源码解读之Job动态生成和深度思考
- 第6课:Spark Streaming源码解读之Job动态生成和深度思考
- 第6课:Spark Streaming源码解读之Job动态生成和深度思考
- 第6课:Spark Streaming源码解读之Job动态生成和深度思考
- 第6课:Spark Streaming源码解读之Job动态生成和深度思考
- Spark 定制版:006~Spark Streaming源码解读之Job动态生成和深度思考
- Spark定制班第6课:Spark Streaming源码解读之Job动态生成和深度思考
- Spark定制班第6课:Spark Streaming源码解读之Job动态生成和深度思考
- Spark Streaming源码解读之Job动态生成和深度思考
- Spark Streaming源码解读之Job动态生成和深度思考
- Spark Streaming源码解读之Job动态生成和深度思考
- 第6课:SparkStreaming源码解读之Job动态生成和深度思考
- Spark 定制版:007~Spark Streaming源码解读之JobScheduler内幕实现和深度思考
- Job动态生成和深度思考(第六篇)
- 第7课:Spark Streaming源码解读之JobScheduler内幕实现和深度思考
- 第7课:Spark Streaming源码解读之JobScheduler内幕实现和深度思考
- 第7课:Spark Streaming源码解读之JobScheduler内幕实现和深度思考
- centos 系统下 用vps搭建ss服务
- 51nod 1610 路径计数
- 余弦定理判断字符串相似度
- [Android UI]ConstraintLayout-约束性布局的使用和注意点
- 【Machine Learning】笔记:主成份分析 PCA
- Spark学习笔记(6)源码解读之Job动态生成和深度思考
- 基于CAS的单点登录SSO[4]: 加入两个CAS客户端测试单点登录
- C++ 类与对象
- vtk中的图片传给opencv的mat操作
- react native 开发笔记(二)
- Javascript之BOM与DOM讲解
- retrofit源碼分析
- 将Eclipse中的项目上传至oschina
- JAVA的jdbc向MySql表中插入各种数据类型