Spark的AppStatusStore类简介及原理梳理
本文还是按照提问式学习方式来一起学习AppStatusStore这个类
AppStatusStore的作用是什么?
AppStatusStores是用来存储Application的状态数据,Spark Web UI及REST API需要的数据都取自它。之前在写度量系统时
AppStatusStore在什么时候初始化?如何被创建的?
在SparkContext初始化的时候,有这样一句代码,这就是AppStatusStore的初始化代码
1 _statusStore = AppStatusStore.createLiveStore(conf)
并且创建的代码是这样的
1 def createLiveStore(conf: SparkConf): AppStatusStore = { 2 val store = new ElementTrackingStore(new InMemoryStore(), conf) 3 val listener = new AppStatusListener(store, conf, true) 4 new AppStatusStore(store, listener = Some(listener)) 5 }
这时候看到store、listener肯定是一脸懵,没关系,后面我们会讲这两个的作用
AppStatusStore如何使用呢?
其实很简单,通过它提供的几个方法,就能看出该如何使用它
1 def applicationInfo(): v1.ApplicationInfo //获取app的信息 2 def jobsList(statuses: JList[JobExecutionStatus]): Seq[v1.JobData] //获取job的集合 3 def job(jobId: Int): v1.JobData //获取job 4 def activeStages(): Seq[v1.StageData] //获取激活状态的stages 5 def taskCount(stageId: Int, stageAttemptId: Int): Long //获取task数量 6 .....
通过上面的方法,可以看出 AppStatusStore 能获取到各种Job、app相关的信息,这些信息能提供给Spark UI进行展示
看完 AppStatusStore 提供的这些方法后,这时候大家都会冒出好几个问题:
1 AppStatusStore 如何存储这些数据的?或者说这些数据存在哪里? 2 AppStatusStore 存储的数据结构是怎么样的? 3 AppStatusStore 什么时候存储这些数据的?
现在我们一个个问题来解答
AppStatusStore 如何存储这些数据的?或者说这些数据存在哪里?
如何存储,就要从 AppStatusStore 的结构说起
这里,我用简单的伪代码来解答这个问题
1 private[spark] class AppStatusStore(val store: KVStore, val listener: Option[AppStatusListener] = None) { 2 //获取环境信息 3 def environmentInfo(): v1.ApplicationEnvironmentInfo = { 4 val klass = classOf[ApplicationEnvironmentInfoWrapper] 5 store.read(klass, klass.getName()).info 6 } 7 8 //获取job信息 9 def job(jobId: Int): v1.JobData = { 10 store.read(classOf[JobDataWrapper], jobId).info 11 } 12 13 def activeStages(): Seq[v1.StageData] = { 14 listener.map(_.activeStages()).getOrElse(Nil) 15 } 16 }
这时候我们可以看到,AppStatusStore的environmentInfo()、job()、activeStages()方法展示的数据,都是来源于外部传入的 store、listener,这些都是在 AppStatusStore 被创建的时候传进来的,答案就在 上面的问题:AppStatusStore在什么时候初始化?如何被创建的? 这里
1 def createLiveStore(conf: SparkConf): AppStatusStore = { 2 val store = new ElementTrackingStore(new InMemoryStore(), conf) 3 val listener = new AppStatusListener(store, conf, true) 4 new AppStatusStore(store, listener = Some(listener)) 5 }
AppStatusStore 存储的数据结构是怎么样的?
通过上面的伪代码,我们可以知道,AppStatusStore 的显示数据主要存储在 store: ElementTrackingStore 和 listener:AppStatusListener 两个元素中,现在就来分析一下
ElementTrackingStore 伪代码如下
1 private[spark] class ElementTrackingStore(store: KVStore, conf: SparkConf) extends KVStore { 2 //写入数据 3 override def write(value: Any): Unit = store.write(value) 4 5 //删除store中的数据 6 override def delete(klass: Class[_], naturalKey: Any): Unit = store.delete(klass, naturalKey) 7 8 //获取store中的view 9 override def view[T](klass: Class[T]): KVStoreView[T] = store.view(klass) 10 11 //获取store的元素个数 12 override def count(klass: Class[_]): Long = store.count(klass) 13 }
从这段代码可以看出,ElementTrackingStore 的写、读取、删除等操作,都是基于 store: KVStore 之上,这个store是什么呢?回顾上面的代码,store来源于 InMemoryStore,所以 ElementTrackingStore 就是对 InMemoryStore 进行了一次封装
1 val store = new ElementTrackingStore(new InMemoryStore(), conf)
所以这样看来 ElementTrackingStore 也很简单,主要基于 InMemoryStore 的读写删操作(当然还包括定时清理数据的触发器,这个自己研究即可,放入进来讲容易打乱思路)
那么大家是不是冒出一个问题:store 什么时候会被写入数据?
其实这时候我们本应该讲 listener:AppStatusListener 的数据存储结构,但是在我看来,这两个问题都是对 listener:AppStatusListener 做好解释,因为 listener:AppStatusListener 的作用就是:
1、当监听被触发的时候,将数据写入到 store
2、listener:AppStatusListener 自身内部也保存了一点状态数据,并提供给 AppStatusStore 用于展示
我们来看 listener:AppStatusListener 是如何被创建的,在问题:AppStatusStore在什么时候初始化?如何被创建的?中就已经描述了
1 val listener = new AppStatusListener(store, conf, true)
将store传入,待会Listener被触发,就会把相应数据存入到store中,可以看下面 AppStatusListener 的伪代码
1 private[spark] class AppStatusListener( 2 kvstore: ElementTrackingStore, 3 conf: SparkConf, 4 live: Boolean, 5 lastUpdateTime: Option[Long] = None) extends SparkListener with Logging { 6 7 //application结束时触发 8 override def onApplicationEnd(event: SparkListenerApplicationEnd): Unit = { 9 ... 10 kvstore.write(new ApplicationInfoWrapper(appInfo)) 11 } 12 13 //Job启动时触发 14 override def onJobStart(event: SparkListenerJobStart): Unit = { 15 ... 16 kvstore.write(uigraph) 17 } 18 } 19 20 private val liveStages = new ConcurrentHashMap[(Int, Int), LiveStage]() 21 private def getOrCreateStage(info: StageInfo): LiveStage = { 22 val stage = liveStages.computeIfAbsent((info.stageId, info.attemptNumber), 23 new Function[(Int, Int), LiveStage]() { 24 override def apply(key: (Int, Int)): LiveStage = new LiveStage() 25 }) 26 stage.info = info 27 stage 28 } 29 30 def activeStages(): Seq[v1.StageData] = { 31 liveStages.values.asScala 32 .filter(_.info.submissionTime.isDefined) 33 .map(_.toApi()) 34 .toList 35 .sortBy(_.stageId) 36 } 37 }
通过 onApplicationEnd() 、onJobStart() 方法可以看出,一旦Lisntener被触发,就会将信息写入到store中
而通过 getOrCreateStage() 、activeStages() 方法可以看出,Listener内部也会保存一些成员变量 来 记录一些 状态信息,并通过 activeStages() 方法提供给 AppStatusStore 对外展示
所以在这里就解答了:AppStatusStore在什么时候初始化?如何被创建的?问下中,大家对 store、listener作用和两者之间的关系弄明白了
看到这里,有人应该会冒出这样的问题:什么时候会调用到Listenr的这些方法?或者说,什么时候Listener注册到Spark系统中呢?接下来来解答这个问题
什么时候会调用到Listenr的这些方法?或者说,什么时候Listener注册到Spark系统中呢?
我们知道在SparkContext中,初始化了 AppStatusStore
1 _statusStore = AppStatusStore.createLiveStore(conf)
紧接着,就在下一句,将Listener注册给Spark的listenerBus, listenerBus是负责管理Spark的监听器的组件
1 listenerBus.addToStatusQueue(_statusStore.listener.get)
对 listenerBus 有兴趣的同学,可以参考我的listenerBus文章: