业余草教你解读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集群的网络传输能力,为了看懂这块代码,在网上找了两本书看< ...
随机推荐
- NopCommerce(3.9)作业调度插件
NopCommerce(3.9)作业调度插件视频教程录制完成,下面是插件源码下载地址和插件视频教程下载地址:插件下载地址: http://www.nopcommerce.com/p/2752/jobs ...
- Vulkan Tutorial 05 物理设备与队列簇
操作系统:Windows8.1 显卡:Nivida GTX965M 开发工具:Visual Studio 2017 Selecting a physical device 通过VkInstance初始 ...
- C#实现SQLSERVER数据库中有序GUID生成(NewSequentialId)
GUID作为数据库主键由于其无序性所以性能不怎么好,SQL Server中有个函数NewSequentialId可以生成有序的GUID,由于在程序中需要用到,就用C#实现了一下,生成的GUID格式基本 ...
- 如何用python绘制各种图形
1.环境 系统:windows10 python版本:python3.6.1 使用的库:matplotlib,numpy 2.numpy库产生随机数几种方法 import numpy as np nu ...
- iOS中动态注入JavaScript方法。动态给html标签添加事件
项目中有这样一种需求,给html5网页中图片添加点击事件,并且弹出弹出点击的对应的图片,并且可以保持图片到本地 应对这样的需求你可能会想到很多方法来实现. 1. 最简单的方法就是在html5中添加图片 ...
- 织梦DedeCMS调用二级子栏目或者多级栏目的方法
图2 当前栏目typeid值为3,所以代码如下: {dede:channelartlist typeid='3,3'} <a href="{dede:field name='typeu ...
- javaCV开发详解之技术杂烩:javaCV能帮我们做什么?能实现什么功能?ffmpeg和openCV能实现功能,javaCV如何做到更快、更简单的实现相应的功能?等等一堆实用话题
前言: 该篇文章旨在帮助刚接触javaCV的盆友系统的认识音视频.javaCV.图像处理相关的体系知识和一些实用的知识. 序: javaCV早期因为内置了openCV库,所以常用来做图像识别应用,现在 ...
- Thinkphp 3.0-3.1版代码执行漏洞
近日360库带计划中播报的ThinkPHP扩展类库的漏洞已经查明原因:系官方扩展模式中的Lite精简模式中存在可能的漏洞(原先核心更新安全的时候 并没有更新模式扩展部分,现已更新).对于使用标准模式或 ...
- php简单的文件操作
(1)先要想好要操作哪个文件? (2)确定文件的路径? (3)要有什么文件管理功能? 一.先做一下简单的查看文件功能,文件中的文件和文件夹都显示,但是双击文件夹可以显示下一级子目录,双击"返 ...
- 在vue 中使用Stylus
概述 什么是Stylus Stylus是一个CSS预处理器. 什么是CSS预处理器 关于CSS预处理器,推荐先行阅读这篇文章:为您详细比较三个 CSS 预处理器(框架):Sass.LESS 和 Sty ...