Spark2.1.0——WebUI框架体系

阅读建议:阅读本文前最好先阅读《Spark2.1.0——SparkUI的实现》一文。

         Spark UI构建在WebUI的框架体系之上,因此应当首先了解WebUI。WebUI定义了一种Web界面展现的框架,并提供返回Json格式数据的Web服务。WebUI用于展示一组标签页,WebUITab定义了标签页的规范。每个标签页中包含着一组页面,WebUIPage定义了页面的规范。我们将首先了解WebUIPage和WebUITab,最后从整体来看WebUI。

WebUIPage的定义

         任何的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定义了两个方法。

  • render:渲染页面;
  • renderJson:生成JSON。

WebUIPage在WebUI框架体系中的上一级节点(也可以称为父亲)可以是WebUI或者WebUITab,其成员属性prefix将与上级节点的路径一起构成当前WebUIPage的访问路径。

WebUITab的定义

         有时候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有四个成员属性:

  • parent:上一级节点,即父亲。WebUITab的父亲只能是WebUI。
  • prefix:当前WebUITab的前缀。prefix将与上级节点的路径一起构成当前WebUITab的访问路径。
  • pages:当前WebUITab所包含的WebUIPage的缓冲数组。
  • name:当前WebUITab的名称。name实际是对prefix的首字母转换成大写字母后取得。

此外,WebUITab还有三个成员方法,下面介绍它们的作用:

  • attachPage:首先将当前WebUITab的前缀与WebUIPage的前缀拼接,作为WebUIPage的访问路径。然后向pages中添加WebUIPage。
  • headerTabs:获取父亲WebUI中的所有WebUITab。此方法实际通过调用父亲WebUI的getTabs方法实现,getTabs方法请参阅下一小节——WebUI的定义。
  • basePath:获取父亲WebUI的基本路径。此方法实际通过调用父亲WebUI的getBasePath方法实现,getBasePath方法请参阅下一小节——WebUI的定义。

WebUI的定义

         WebUI是Spark实现的用于提供Web界面展现的框架,凡是需要页面展现的地方都可以继承它来完成。WebUI定义了WebUI框架体系的规范。为便于理解,首先明确WebUI中各个成员属性的含义:

  • securityManager:SparkEnv中创建的安全管理器SecurityManager,5.2节对SecurityManager有详细介绍。
  • sslOptions:使用SecurityManager获取spark.ssl.ui属性指定的WebUI的SSL(Secure Sockets Layer 安全套接层)选项。
  • port:WebUI对外服务的端口。可以使用spark.ui.port属性进行配置。
  • conf:即SparkConf。
  • basePath:WebUI的基本路径。basePath默认为空字符串。
  • name:WebUI的名称。Spark UI的name为SparkUI。
  • tabs:WebUITab的缓冲数组。
  • handlers:ServletContextHandler的缓冲数组。ServletContextHandler是Jetty提供的API,负责对ServletContext进行处理。ServletContextHandler的使用及Jetty的更多内容可以参阅《附录C Jetty与JettyUtils》。
  • pageToHandlers:WebUIPage与ServletContextHandler缓冲数组之间的映射关系。由于WebUIPage的两个方法render和renderJson分别需要由一个对应的ServletContextHandler处理。所以一个WebUIPage对应两个ServletContextHandler。
  • serverInfo:用于缓存ServerInfo,即WebUI的Jetty服务器信息。
  • publicHostName:当前WebUI的Jetty服务的主机名。优先采用系统环境变量SPARK_PUBLIC_DNS指定的主机名,否则采用spark.driver.host属性指定的host,在没有前两个配置的时候,将默认使用工具类Utils的localHostName方法(详见《附录A Spark2.1核心工具类Utils》)返回的主机名。
  • className:过滤了$符号的当前类的简单名称。className 是通过Utils的getFormattedClassName方法得到的。getFormattedClassName方法的实现请看《附录A Spark2.1核心工具类Utils》。

了解了WebUI的成员属性,现在就可以理解其提供的各个方法了。WebUI提供的方法有:

  • getBasePath:获取basePath。
  • getTabs:获取tabs中的所有WebUITab,并以Scala的序列返回。
  • getHandlers:获取handlers中的所有ServletContextHandler,并以Scala的序列返回。
  • getSecurityManager:获取securityManager。
  • attachHandler:给handlers缓存数组中添加ServletContextHandler,并且将此ServletContextHandler通过ServerInfo的addHandler方法添加到Jetty服务器中。attachHandler的实现见代码清单3。ServerInfo的addHandler方法的请参阅《附录C Jetty与JettyUtils》。

代码清单3  attachHandler的实现

  def attachHandler(handler: ServletContextHandler) {

    handlers += handler

    serverInfo.foreach(_.addHandler(handler))

  }
  • detachHandler:从handlers缓存数组中移除ServletContextHandler,并且将此ServletContextHandler通过ServerInfo的removeHandler方法从Jetty服务器中移除。detachHandler的实现见代码清单4。ServerInfo的removeHandler方法的请参阅《附录C Jetty与JettyUtils》。

代码清单4  detachHandler的实现

  def detachHandler(handler: ServletContextHandler) {

    handlers -= handler

    serverInfo.foreach(_.removeHandler(handler))

  }
  • attachPage:首先调用工具类JettyUtils[1]的createServletHandler方法给WebUIPage创建与render和renderJson两个方法分别关联的ServletContextHandler,然后通过attachHandler方法添加到handlers缓存数组与Jetty服务器中,最后把WebUIPage与这两个ServletContextHandler的映射关系更新到pageToHandlers中。attachPage的实现见代码清单5。

代码清单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

  }
  • detachPage:作用与attachPage相反。detachPage的实现见代码清单6。

代码清单6  detachPage的实现

  def detachPage(page: WebUIPage) {

    pageToHandlers.remove(page).foreach(_.foreach(detachHandler))

  }
  • attachTab:首先向tabs中添加WebUITab,然后给WebUITab中的每个WebUIPage施加attachPage方法。attachTab的实现见代码清单7。

代码清单7  attachTab的实现

  def attachTab(tab: WebUITab) {

    tab.pages.foreach(attachPage)

    tabs += tab

  }
  • detachTab:作用与attachTab相反。detachTab的实现见代码清单8。

代码清单8  detachTab的实现

  def detachTab(tab: WebUITab) {

    tab.pages.foreach(detachPage)

    tabs -= tab

  }
  • addStaticHandler:首先调用工具类JettyUtils的createStaticHandler方法创建静态文件服务的ServletContextHandler,然后施加attachHandler方法。addStaticHandler的实现见代码清单9。JettyUtils的createStaticHandler方法的实现见《附录C Jetty与JettyUtils》。

代码清单9        addStaticHandler的实现

  def addStaticHandler(resourceBase: String, path: String): Unit = {

    attachHandler(JettyUtils.createStaticHandler(resourceBase, path))

  }
  • removeStaticHandler:作用与addStaticHandler相反。removeStaticHandler的实现见代码清单10。

代码清单10         removeStaticHandler的实现

  def removeStaticHandler(path: String): Unit = {

    handlers.find(_.getContextPath() == path).foreach(detachHandler)

  }
  • initialize:用于初始化WebUI服务中的所有组件。WebUI中此方法未实现,需要子类实现。
  • bind:启动与WebUI绑定的Jetty服务。bind方法的实现见代码清单11。

代码清单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)

    }

  }
  • webUrl:获取WebUI的Web界面的URL。webUrl的实现如下:
  def webUrl: String = shttp://$publicHostName:$boundPort
  • boundPort:获取WebUI的Jetty服务的端口。boundPort的实现如下:
  def boundPort: Int = serverInfo.map(_.boundPort).getOrElse(-1)
  • stop:停止WebUI。实际是停止WebUI底层的Jetty服务。stop方法的实现见代码清单12。

代码清单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》中找到相应的实现与说明。

你可能感兴趣的:(大数据,Spark,Scala,深入理解Spark)