阅读建议:阅读本文前最好先阅读《Spark2.1.0——SparkUI的实现》一文。
Spark UI构建在WebUI的框架体系之上,因此应当首先了解WebUI。WebUI定义了一种Web界面展现的框架,并提供返回Json格式数据的Web服务。WebUI用于展示一组标签页,WebUITab定义了标签页的规范。每个标签页中包含着一组页面,WebUIPage定义了页面的规范。我们将首先了解WebUIPage和WebUITab,最后从整体来看WebUI。
任何的Web界面往往由多个页面组成,每个页面都将提供不同的内容展示。WebUIPage是WebUI框架体系的页节点,定义了所有页面应当遵循的规范。抽象类WebUIPage的定义见代码清单1。
代码清单1 WebUIPage的定义
private[spark] abstract class WebUIPage(var prefix: String) {
def render(request: HttpServletRequest): Seq[Node]
def renderJson(request: HttpServletRequest): JValue = JNothing
}
WebUIPage定义了两个方法。
WebUIPage在WebUI框架体系中的上一级节点(也可以称为父亲)可以是WebUI或者WebUITab,其成员属性prefix将与上级节点的路径一起构成当前WebUIPage的访问路径。
有时候Web界面需要将多个页面作为一组内容放置在一起,这时候标签页是常见的展现形式。标签页WebUITab定义了所有标签页的规范,并用于展现一组WebUIPage。抽象类WebUITab的定义见代码清单2。
代码清单2 WebUITab的定义
private[spark] abstract class WebUITab(parent: WebUI, val prefix: String) {
val pages = ArrayBuffer[WebUIPage]()
val name = prefix.capitalize
def attachPage(page: WebUIPage) {
page.prefix = (prefix + "/" + page.prefix).stripSuffix("/")
pages += page
}
def headerTabs: Seq[WebUITab] = parent.getTabs
def basePath: String = parent.getBasePath
}
根据代码清单2,可以看到WebUITab有四个成员属性:
此外,WebUITab还有三个成员方法,下面介绍它们的作用:
WebUI是Spark实现的用于提供Web界面展现的框架,凡是需要页面展现的地方都可以继承它来完成。WebUI定义了WebUI框架体系的规范。为便于理解,首先明确WebUI中各个成员属性的含义:
了解了WebUI的成员属性,现在就可以理解其提供的各个方法了。WebUI提供的方法有:
代码清单3 attachHandler的实现
def attachHandler(handler: ServletContextHandler) {
handlers += handler
serverInfo.foreach(_.addHandler(handler))
}
代码清单4 detachHandler的实现
def detachHandler(handler: ServletContextHandler) {
handlers -= handler
serverInfo.foreach(_.removeHandler(handler))
}
代码清单5 attachPage的实现
def attachPage(page: WebUIPage) {
val pagePath = "/" + page.prefix
val renderHandler = createServletHandler(pagePath,
(request: HttpServletRequest) => page.render(request), securityManager, conf, basePath)
val renderJsonHandler = createServletHandler(pagePath.stripSuffix("/") + "/json",
(request: HttpServletRequest) => page.renderJson(request), securityManager, conf, basePath)
attachHandler(renderHandler)
attachHandler(renderJsonHandler)
val handlers = pageToHandlers.getOrElseUpdate(page, ArrayBuffer[ServletContextHandler]())
handlers += renderHandler
}
代码清单6 detachPage的实现
def detachPage(page: WebUIPage) {
pageToHandlers.remove(page).foreach(_.foreach(detachHandler))
}
代码清单7 attachTab的实现
def attachTab(tab: WebUITab) {
tab.pages.foreach(attachPage)
tabs += tab
}
代码清单8 detachTab的实现
def detachTab(tab: WebUITab) {
tab.pages.foreach(detachPage)
tabs -= tab
}
代码清单9 addStaticHandler的实现
def addStaticHandler(resourceBase: String, path: String): Unit = {
attachHandler(JettyUtils.createStaticHandler(resourceBase, path))
}
代码清单10 removeStaticHandler的实现
def removeStaticHandler(path: String): Unit = {
handlers.find(_.getContextPath() == path).foreach(detachHandler)
}
代码清单11 bind的实现
def bind() {
assert(!serverInfo.isDefined, s"Attempted to bind $className more than once!")
try {
val host = Option(conf.getenv("SPARK_LOCAL_IP")).getOrElse("0.0.0.0")
serverInfo = Some(startJettyServer(host, port, sslOptions, handlers, conf, name))
logInfo(s"Bound $className to $host, and started at $webUrl")
} catch {
case e: Exception =>
logError(s"Failed to bind $className", e)
System.exit(1)
}
}
def webUrl: String = shttp://$publicHostName:$boundPort
def boundPort: Int = serverInfo.map(_.boundPort).getOrElse(-1)
代码清单12 stop方法的实现
def stop() {
assert(serverInfo.isDefined,
s"Attempted to stop $className before binding to a server!")
serverInfo.get.stop()
}
请继续阅读《Spark2.1.0——创建SparkUI的分析》
[1]本节内容用到JettyUtils中的很多方法,读者可以在《附录C Jetty与JettyUtils》中找到相应的实现与说明。