本期内容:
1,JobScheduler内幕实现
2,JobScheduler深度思考
摘要:JobScheduler是Spark Streaming整个调度的核心,其地位相当于Spark Core上的调度中心中的DAGScheduler!
一、JobScheduler内幕实现
首先看看源码中对jobScheduler的解释:
/** * This class schedules jobs to be run on Spark. It uses the JobGenerator to generate * the jobs and runs them using a thread pool. */
我们在看看源码对JobGenerator的解释:
/** * This class generates jobs from DStreams as well as drives checkpointing and cleaning * up DStream metadata. */
问:JobScheduler是在什么地方生成的?
答:JobScheduler是在StreamingContext实例化时产生的,从StreamingContext的源码第183行中可以看出:
private[streaming] val scheduler = new JobScheduler(this)
问:Spark Streaming为啥要设置两条线程?
答:setMaster指定的两条线程是指程序运行的时候至少需要两条线程。一条线程用于接收数据,需要不断的循环。另一条是处理线程,是我们自己指定的线程数用于作业处理。如StreamingContext的start()方法所示:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 |
|
进入JobScheduler源码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 |
|
二、JobScheduler深度思考
下面从应用程序的输出方法print()入手,反推Job的生成过程:
1.点击应用程序中的print()方法后,跳入DStream的print():
1 2 3 4 5 6 7 |
|
2.再次点击上面红线标记的print()方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
|
从图中红色标记的代码可以得出:SparkStreaming最终执行的时候还是对RDD进行各种逻辑级别的操作!
3.再次点击图上的foreachRDD进入foreachRDD方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
|
4.点击上图的ForEachDStream进入ForEachDStream类并找到了generateJob方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 |
|
5.上一讲中我们得出了如下的流程:
streamingcontext.start-->jobscheduler.start-->receiverTracker.start()-->JobGenterator.start()-->EventLoop-->processEvent()-->generateJobs()-->jobScheduler.receiverTracker.allocateBlocksToBatch(time)-->graph.generateJobs(time)
其中最后的graph.generateJobs是DSTreamGraph的方法,进入之:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
|
private val outputStreams = new ArrayBuffer[DStream[_]]()通过查看DStream的子类继承结构和上面的ForEachDStream的generateJob方法,得出DStream的子类中只有ForEachDStream override了DStream的generateJob!最终得出结论:
真正Job的生成是通过ForeachDStream的generateJob来生成的,此时的job是逻辑级别的,真正被物理级别的调用是在JobGenerator中
generateJob方法中:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
|
进入jobScheduler.submitJobSet方法:
1 2 3 |
|
1 2 3 4 5 6 7 8 9 10 |
|
至此,整个job的生成、执行就非常清晰了,最后总结如下:
ReceiverTracker启动后会导致运行在Executor端的Receiver启动并且接收数据,ReceiverTracker会记录Receiver接收到的数据meta信息,
JobGenerator的启动导致每隔BatchDuration,就调用DStreamGraph生成RDD Graph,并生成Job,
JobScheduler中的线程池来提交封装的JobSet对象(时间值,Job,数据源的meta)。Job中封装了业务逻辑,导致最后一个RDD的action被触发,
被DAGScheduler真正调度在Spark集群上执行该Job。