业余草教你解读Spark源码阅读之HistoryServer
HistoryServer服务可以让用户通过Spark UI界面,查看历史应用(已经执行完的应用)的执行细节,比如job信息、stage信息、task信息等,该功能是基于spark eventlogs日志文件的,所以必须打开eventlogs日志开关,关于日志开关的打开和HistoryServer服务的启动方法这里不再讲述,下面进入正题
下面使用的spark版本是2.0.2
类结构图
Web相关
数据流相关
相关类及特质
WebUI
Web Server服务中UI层次结构的最顶层。每一个WebUI包含了一个tabs的集合,而每一个tab又包含了一个pages的集合。tabs页是可选的,而且WebUI也可以直接添加page
继承该特质的有SparkUI、MasterWebUI、WorkerWebUI和HistoryServer,在这里我们主要介绍HistoryServer
WebUITab
一个tab包含了一个pages的集合。prefix通过追加到parent的url组成一个完整的url path,而且不能包含斜杠
继承该特质有JobsTab、StagesTab、ExecutorsTab、StorageTab等(这里没有列全),对应于Spark UI界面上的Jobs、Stages、Executors、Storage等Tab页
WebUIPage
一个page表示UI层次结构中的叶子节点。WebUIPage的直接父类即可以是WebUI,也可以是WebUITab。
如果父类是WebUI,prefix追加到parent的url形成完整的url path,如果父类是WebUITab,prefix追加到parent的prefix形成一个相对url path。Prefix中不能包含斜杠
继承该特质的有JobPage、StagePage、ExecutionPage、StoragePage等,对应于Tab页中具体的Page
HistoryPage
继承至WebUIPage,通过render函数渲染生成history页面
UIRoot
该特质被根容器(HistoryServer、SparkUI)继承,用来为它们提供获取application信息的统一接口
HistoryServer
def main(argStrings: Array[String]): Unit = {
……
val providerName = conf.getOption("spark.history.provider")
.getOrElse(classOf[FsHistoryProvider].getName())
val provider = Utils.classForName(providerName)
.getConstructor(classOf[SparkConf])
.newInstance(conf)
.asInstanceOf[ApplicationHistoryProvider]
val port = conf.getInt("spark.history.ui.port", 18080)
val server = new HistoryServer(conf, provider, securityManager, port)
server.bind()
ShutdownHookManager.addShutdownHook { () => server.stop() }
// Wait until the end of the world... or if the HistoryServer process is manually stopped
while(true) { Thread.sleep(Int.MaxValue) }
}
HistoryServer继承至WebUI,启动的时候,会将环境配置以及provider作为成员变量来初始化HistoryServer实例,其中provider用来提供application的信息供web展示使用,HistoryServer实例化后执行bind()函数,启动jetty,将HTTP服务与web接口绑定,这时候historyserver web服务已经启动了,之后添加了关闭server钩子函数后进入无限循环等待
在HistoryServer实例化的过程中,会执行initialize()函数,
def initialize() {
attachPage(new HistoryPage(this))
attachHandler(ApiRootResource.getServletHandler(this))
attachHandler(createStaticHandler(SparkUI.STATIC_RESOURCE_DIR, "/static"))
val contextHandler = new ServletContextHandler
contextHandler.setContextPath(HistoryServer.UI_PATH_PREFIX)
contextHandler.addServlet(new ServletHolder(loaderServlet), "/*")
attachHandler(contextHandler)
}
在该函数中,首先通过attachPage函数在UI中添加了HistoryPage实例,该实例负责渲染生成history page,然后通过attachHandler添加了不同的handler,可以访问url路由获取对应的信息,其中ApiRootResource提供了api/vi/开头的路由,通过该路由,history page可以获取后台解析出的eventlog信息用以呈现,数据通过UIRoot提供的接口获取
到这里,HistoryServer的Web端基本构建完成
HsitoryServer数据缓存及获取
数据缓存主要通过使用google缓存机制LoadingCache实现,关于LoadingCache在Spark HistoryServer中的运用在另外一篇文章中分析
FsHistoryProvider
前面完成了web结构的构建,接下来就需要提供接口获取历史application的信息来呈现,而FsHistoryProvider就是这个接口,作为成员变量传递给HistoryServer。这个类在实例化的时候,执行了initialize()函数,在该函数中,首先会检查hdfs是否处于安全模式,如果处于安全模式,则会等待至退出安全模式,如果不处于安全模式,则走进startPolling函数,在该函数中会读取配置的eventlog路径(默认为file:/tmp/spark-events,通过spark.history.fs.logDirectory配置),然后启动一个线程不断扫描该路径下的eventlog文件,将文件解析后加载到内存中供web查询使用,相关函数如下:
private val pool = Executors.newScheduledThreadPool(1, new ThreadFactoryBuilder()
.setNameFormat("spark-history-task-%d").setDaemon(true).build())
pool.scheduleWithFixedDelay(getRunner(checkForLogs), 0, UPDATE_INTERVAL_S, TimeUnit.SECONDS)
if (conf.getBoolean("spark.history.fs.cleaner.enabled", false)) {
pool.scheduleWithFixedDelay(getRunner(cleanLogs), 0, CLEAN_INTERVAL_S, TimeUnit.SECONDS)
}
另外如果配置了清理开关(默认一天清理一次),则会清理内存中超时的application信息,并删除超时且已完成的文件,加载和清理这两个动作由同一个线程完成,以防止冲突。
for (file <- logInfos) {
tasks += replayExecutor.submit(new Runnable {
override def run(): Unit = mergeApplicationListing(file)
})
}
在checkForLogs函数中,会首先检查文件是否有更新,已经扫描过的文件保存在一个以文件名为key的映射中fileToAppInfo,如果文件不在这个映射中,或者存在这个映射中但是文件大小变大了,则将此文件加入到加载列表中,随后进行解析。解析的过程是采用一个固定线程数的线程池replayExecutor对需要加载的文件进行解析,每解析完一个文件,会将此文件的信息更新至fileToAppInfo,这个过程在mergeApplicationListing函数中完成,另外pendingReplayTasksCount中保存了等待解析的文件数目,所有文件解析完成后,更新一下解析完成时间
private def replay(
eventLog: FileStatus,
appCompleted: Boolean,
bus: ReplayListenerBus,
eventsFilter: ReplayEventsFilter = SELECT_ALL_FILTER): ApplicationEventListener = {
val logPath = eventLog.getPath()
logInfo(s"Replaying log path: $logPath")
val logInput = EventLoggingListener.openEventLog(logPath, fs)
try {
val appListener = new ApplicationEventListener
bus.addListener(appListener)
bus.replay(logInput, logPath.toString, !appCompleted, eventsFilter)
appListener
} finally {
logInput.close()
}
}
在mergeApplicationListing函数中,主要通过执行reply函数将eventlog日志文件解析出来,在该函数中,首先将ApplicationEventListener监听器加入到ReplayListenerBus实例中,ReplayListenerBus主要通过调用该实例的replay函数从eventlog记录中解析event事件,每解析一个event,都会发通知到各监听器处理event,在这里通过监听者模式将日志解析与结果处理两个过程解耦开。执行完reply函数后,也就完成了一个eventlog文件的解析,如果解析成功,则将该eventlog的信息加入到fileToAppInfo,表明已经扫描过该文件
在cleanLogs函数中,会在log directory中删除已经任务执行完成且超时的文件。欢迎关注业余草:www.xttblog.com;CODE大全:www.codedq.net;爱分享:www.ndislwf.com
业余草教你解读Spark源码阅读之HistoryServer的更多相关文章
- emacs+ensime+sbt打造spark源码阅读环境
欢迎转载,转载请注明出处,徽沪一郎. 概述 Scala越来越流行, Spark也愈来愈红火, 对spark的代码进行走读也成了一个很普遍的行为.不巧的是,当前java社区中很流行的ide如eclips ...
- Spark源码阅读之存储体系--存储体系概述与shuffle服务
一.概述 根据<深入理解Spark:核心思想与源码分析>一书,结合最新的spark源代码master分支进行源码阅读,对新版本的代码加上自己的一些理解,如有错误,希望指出. 1.块管理器B ...
- win7+idea+maven搭建spark源码阅读环境
1.参考. 利用IDEA工具编译Spark源码(1.60~2.20) https://blog.csdn.net/He11o_Liu/article/details/78739699 Maven编译打 ...
- spark源码阅读
根据spark2.2的编译顺序来确定源码阅读顺序,只阅读核心的基本部分. 1.common目录 ①Tags②Sketch③Networking④Shuffle Streaming Service⑤Un ...
- spark源码阅读---Utils.getCallSite
1 作用 当该方法在spark内部代码中调用时,会返回当前调用spark代码的用户类的名称,以及其所调用的spark方法.所谓用户类,就是我们这些用户使用spark api的类. 2 内部实现 2.1 ...
- spark源码阅读--SparkContext启动过程
##SparkContext启动过程 基于spark 2.1.0 scala 2.11.8 spark源码的体系结构实在是很庞大,从使用spark-submit脚本提交任务,到向yarn申请容器,启 ...
- spark源码阅读之network(2)
在上节的解读中发现spark的源码中大量使用netty的buffer部分的api,该节将看到netty核心的一些api,比如channel: 在Netty里,Channel是通讯的载体(网络套接字或组 ...
- Spark源码阅读(1): Stage划分
Spark中job由action动作生成,那么stage是如何划分的呢?一般的解答是根据宽窄依赖划分.那么我们深入源码看看吧 一个action 例如count,会在多次runJob中传递,最终会到一个 ...
- spark源码阅读之network(1)
spark将在1.6中替换掉akka,而采用netty实现整个集群的rpc的框架,netty的内存管理和NIO支持将有效的提高spark集群的网络传输能力,为了看懂这块代码,在网上找了两本书看< ...
随机推荐
- 微信小程序 支付功能(前端)的实现
只提供微信小程序端代码: var app = getApp(); Page({ data: {}, onLoad: function (options) { // 页面初始化 options为页面跳转 ...
- 用pickle模块实现“增删改查”的简易功能
pickle的作用: 1:pickle.dump(dict,file)把字典转为二进制存入文件. 2:pickle.load(file)把文件二进制内容转为字典 import pickle # 增 d ...
- ovs + kernel datapath 的分片与重组流程
非VXLAN的收发包调用栈 netdev_frame_hook() netdev_port_receive() ovs_vport_receive() ...
- 一天搞定CSS:初识css--01
1)CSS:层叠样式表(英文全称:Cascading Style Sheets) 是一种用来表现HTML(标准通用标记语言的一个应用)或XML(标准通用标记语言的一个子集)等文件样式的计算机语言. C ...
- cookie笔记(一)
1.浏览器存储信息的一个工具 2.容量有限,每个域名一般50个.详情 3.形式:键=值: 4.获取:document.cookie; 5.设置:document.cookie="uid=we ...
- Redis数据类型之List(三)
前言:list即链表,它是一个能维持数据先后顺序的列表,便于在表的两端追加和删除数据,中间位置的存取具有O(N)的时间复杂度,是一个双向链表. 一.内部原理 redis ...
- thinkphp中find()和select()的区别
1.find()是查找符合条件的第一条数据,返回的是一个一维数组: select()是查找符合条件的所有的数据,返回的是一个二维数组: 2.以下案例 $tech=M('techlevel','HR_C ...
- Vue-cli 记录
出自http://www.cnblogs.com/nutritious/p/6494479.html 先给出能正确安装的步骤: 1.进盘符 2,为啥不用npm,这是国外的东西,有些电脑无法FQ,会导致 ...
- git常用命令,助你快速入门
git是程序员中最常用的版本控制工具,学会git的基本使用是十分重要,特别是在公司里面的协同开发,废话不多说,下面贴出常用的命令. 1.基本配置 # 既然git是多人协同的工具,别人要看到你提交代码的 ...
- 用CSS美化checkbox复选按钮和raido单选按钮-适用于移动端
最终效果: 实现方法 index.html: <!DOCTYPE html> <html> <head> <title></title> & ...