转载自:https://yq.aliyun.com/articles/60194

摘要: 这篇文章的主旨在于让你了解Spark UI体系,并且能够让你有能力对UI进行一些定制化增强。在分析过程中,你也会深深的感受到Scala语言的魅力。

前言

有时候我们希望能对Spark UI进行一些定制化增强。并且我们希望尽可能不更改Spark的源码。为了达到此目标,我们会从如下三个方面进行阐述:
  1. 理解Spark UI的处理流程
  2. 现有Executors页面分析
  3. 自己编写一个HelloWord页面

Spark UI 处理流程

Spark UI 在SparkContext 对象中进行初始化,对应的代码:
_ui =  if (conf.getBoolean("spark.ui.enabled", true)) {
Some(SparkUI.createLiveUI(this, _conf, listenerBus, _jobProgressListener, _env.securityManager, appName, startTime = startTime))
} else
{
// For tests, do not enable the UI None
}// Bind the UI before starting the task scheduler to communicate
// the bound port to the cluster manager properly
_ui.foreach(_.bind())
这里做了两个动作,
  1. 通过SparkUI.createLiveUI 创建一个SparkUI实例 _ui
  2. 通过 _ui.foreach(_.bind())启动jetty。bind 方法是继承自WebUI,该类负责和真实的Jetty Server API打交道。
和传统的Web服务不一样,Spark并没有使用什么页面模板引擎,而是自己定义了一套页面体系。我们把这些对象分成两类:
  1. 框架类,就是维护各个页面关系,和Jetty API有关联,负责管理的相关类。
  2. 页面类,比如页面的Tab,页面渲染的内容等
框架类有:
  • SparkUI,该类继承子WebUI,中枢类,负责启动jetty,保存页面和URL Path之间的关系等。
  • WebUI
页面类:
  • SparkUITab(继承自WebUITab) ,就是首页的标签栏
  • WebUIPage,这个是具体的页面。
SparkUI 负责整个Spark UI构建是,同时它是一切页面的根对象。
对应的层级结构为:
parkUI -> WebUITab ->  WebUIPage

在SparkContext初始化的过程中,SparkUI会启动一个Jetty。而建立起Jetty 和WebUIPage的桥梁是org.apache.spark.ui.WebUI类,该类有个变量如下:

protected val handlers = ArrayBuffer[ServletContextHandler]()

这个org.eclipse.jetty.servlet.ServletContextHandler是标准的jetty容器的handler,而

 protected val pageToHandlers = new HashMap[WebUIPage,   ArrayBuffer[ServletContextHandler]]
pageToHandlers 则维护了WebUIPage到ServletContextHandler的对应关系。
这样,我们就得到了WebUIPage 和 Jetty Handler的对应关系了。一个Http请求就能够被对应的WebUIPage给承接。
从 MVC的角度而言,WebUIPage 更像是一个Controller(Action)。内部实现是WebUIPage被包括进了一个匿名的Servlet. 所以实际上Spark 实现了一个对Servlet非常Mini的封装。如果你感兴趣的话,可以到org.apache.spark.ui.JettyUtils 详细看看。
目前spark 支持三种形态的http渲染结果:
  • text/json
  • text/html
  • text/plain
一般而言一个WebUIPage会对应两个Handler,
val renderHandler = createServletHandler(
pagePath,
(request: HttpServletRequest) => page.render(request),
securityManager,
basePath) val renderJsonHandler = createServletHandler(pagePath.stripSuffix("/") + "/json", (request: HttpServletRequest) => page.renderJson(request), securityManager, basePath)
在页面路径上,html和json的区别就是html的url path 多加了一个"/json"后缀。 这里可以看到,一般一个page最好实现
  • render
  • renderJson
两个方法,以方便使用。
另外值得一提的是,上面的代码也展示了URL Path和对应的处理逻辑(Controller/Action)是如何关联起来的。其实就是pagePath -> Page的render函数。

Executors页面分析

我们以 Executors 显示列表页 为例子,来讲述怎么自定义开发一个Page。
首先你需要定义个Tab,也就是ExecutorsTab,如下:
 private[ui] class ExecutorsTab(parent: SparkUI) extends SparkUITab(parent, "executors")

ExecutorsTab会作为一个标签显示在Spark首页上。接着定义一个ExecutorsPage,作为标签页的呈现内容,并且通过

attachPage(new ExecutorsPage(this, threadDumpEnabled))
关联上 ExecutorsTab 和  ExecutorsPage。
ExecutorsPage 的定义如下:
private[ui] class ExecutorsPage(    parent: ExecutorsTab,    threadDumpEnabled: Boolean)
extends WebUIPage("")

实现ExecutorsPage.render方法:

 def render(request: HttpServletRequest): Seq[Node]

最后一步调用

 SparkUIUtils.headerSparkPage("Executors (" + execInfo.size + ")",     content, parent)
输出设置页面头并且输出content页面内容。
这里比较有意思的是,Spark 并没有使用类似Freemarker或者Velocity等模板引擎,而是直接利用了Scala对html/xml的语法支持。类似这样,写起来也蛮爽的。
val execTable =  <table class={UIUtils.TABLE_CLASS_STRIPED}>
<thead>
<th>Executor ID</th>
<th>Address</th>
<th>RDD Blocks</th>
<th><span data-toggle="tooltip" title={ToolTips.STORAGE_MEMORY}>Storage Memory</span>
</th>
<th>Disk Used</th>
<th>Active Tasks</th>
如果想使用变量,使用{}即可。
那最终这个Tag是怎么添加到页面上的呢?如果你去翻看了源码,会比较心疼,他是在SparkUI的initialize方法里定义的:
def initialize() {
attachTab(new JobsTab(this)) attachTab(stagesTab)
attachTab(new StorageTab(this))
attachTab(new EnvironmentTab(this))
attachTab(new ExecutorsTab(this))

那我们新增的该怎么办?其实也很简单啦,通过sparkContext获取到 sparkUI对象,然后调用attachTab方法即可完成,具体如下:

sc.ui.getOrElse {  throw new SparkException("Parent SparkUI to attach this tab to not found!")}
.attachTab(new ExecutorsTab)

如果你是在spark-streaming里,则简单通过如下代码就能把你的页面页面添加进去:

ssc.start()
new KKTab(ssc).attach()
ssc.awaitTermination(

添加新的Tab可能会报错,scala报的错误比较让人困惑,可以试试加入下面依赖:

<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-servlet</artifactId> <version>9.3..v20151106</version>
</dependency>
实现新增一个HelloWord页面
我们的例子很简单,类似下面的图:

按前文的描述,我们需要一个Tab页,以及一个展示Tab对应内容的Page页。其实就下面两个类。
org.apache.spark.streaming.ui2.KKTab:
package org.apache.spark.streaming.ui2

import org.apache.spark.streaming.StreamingContext
import org.apache.spark.streaming.ui2.KKTab._
import org.apache.spark.ui.{SparkUI, SparkUITab}
import org.apache.spark.{Logging, SparkException} /**
* 1/1/16 WilliamZhu(allwefantasy@gmail.com)
*/
class KKTab(val ssc: StreamingContext)
extends SparkUITab(getSparkUI(ssc), "streaming2") with Logging {
private val STATIC_RESOURCE_DIR = "org/apache/spark/streaming/ui/static"
attachPage(new TTPage(this)) def attach() {
getSparkUI(ssc).attachTab(this)
getSparkUI(ssc).addStaticHandler(STATIC_RESOURCE_DIR, "/static/streaming")
} def detach() {
getSparkUI(ssc).detachTab(this)
getSparkUI(ssc).removeStaticHandler("/static/streaming")
}
} private[spark] object KKTab {
def getSparkUI(ssc: StreamingContext): SparkUI = {
ssc.sc.ui.getOrElse {
throw new SparkException("Parent SparkUI to attach this tab to not found!")
}
}
}

org.apache.spark.streaming.ui2.TTPage 如下

import org.apache.spark.Logging
import org.apache.spark.ui.{UIUtils => SparkUIUtils, WebUIPage}
import org.json4s.JsonAST.{JNothing, JValue} import scala.xml.Node /**
* 1/1/16 WilliamZhu(allwefantasy@gmail.com)
*/
private[spark] class TTPage(parent: KKTab)
extends WebUIPage("") with Logging { override def render(request: HttpServletRequest): Seq[Node] = {
val content = <p>TTPAGE</p>
SparkUIUtils.headerSparkPage("TT", content, parent, Some())
}
override def renderJson(request: HttpServletRequest): JValue = JNothing
}

记得添加上面提到的jetty依赖。

Spark UI (基于Yarn) 分析与定制的更多相关文章

  1. [看图说话] 基于Spark UI性能优化与调试——初级篇

    Spark有几种部署的模式,单机版.集群版等等,平时单机版在数据量不大的时候可以跟传统的java程序一样进行断电调试.但是在集群上调试就比较麻烦了...远程断点不太方便,只能通过Log的形式,进行分析 ...

  2. 基于 Spark 的文本情感分析

    转载自:https://www.ibm.com/developerworks/cn/cognitive/library/cc-1606-spark-seniment-analysis/index.ht ...

  3. spark基于yarn的两种提交模式

    一.spark的三种提交模式 1.第一种,Spark内核架构,即standalone模式,基于Spark自己的Master-Worker集群. 2.第二种,基于YARN的yarn-cluster模式. ...

  4. Spark剖析-宽依赖与窄依赖、基于yarn的两种提交模式、sparkcontext原理剖析

    Spark剖析-宽依赖与窄依赖.基于yarn的两种提交模式.sparkcontext原理剖析 一.宽依赖与窄依赖 二.基于yarn的两种提交模式深度剖析 2.1 Standalne-client 2. ...

  5. Spark运行模式_基于YARN的Resource Manager的Custer模式(集群)

    使用如下命令执行应用程序: 和"基于YARN的Resource Manager的Client模式(集群)"运行模式,区别如下: 在Resource Manager端提交应用程序,会 ...

  6. Spark UI界面原理

    当Spark程序在运行时,会提供一个Web页面查看Application运行状态信息.是否开启UI界面由参数spark.ui.enabled(默认为true)来确定.下面列出Spark UI一些相关配 ...

  7. Spark(四十七):Spark UI 数据可视化

    导入: 1)Spark Web UI主要依赖于流行的Servlet容器Jetty实现: 2)Spark Web UI(Spark2.3之前)是展示运行状况.资源状态和监控指标的前端,而这些数据都是由度 ...

  8. Spark配置&启动脚本分析

    本文档基于Spark2.0,对spark启动脚本进行分析. date:2016/8/3 author:wangxl Spark配置&启动脚本分析 我们主要关注3类文件,配置文件,启动脚本文件以 ...

  9. 12、基于yarn的提交模式

    一.三种提交模式 1.Spark内核架构,其实就是第一种模式,standalone模式,基于Spark自己的Master-Worker集群. 2.第二种,是基于YARN的yarn-cluster模式. ...

随机推荐

  1. leetCode 33.Search in Rotated Sorted Array(排序旋转数组的查找) 解题思路和方法

    Search in Rotated Sorted Array Suppose a sorted array is rotated at some pivot unknown to you before ...

  2. quick-cocos2d-x游戏开发【10】——触摸捕获事件 cc.NODE_TOUCH_CAPTURE_EVENT

    假设看过sample中touch的代码,你会发现演示样例中有一个cc.NODE_TOUCH_CAPTURE_EVENT事件.它和cc.NODE_TOUCH_EVENT触摸事件一样,是引擎级别的事件,我 ...

  3. day27<反射&JDK5新特性>

    反射(类的加载概述和加载时机) 反射(类加载器的概述和分类) 反射(反射概述) 反射(Class.forName()读取配置文件举例) 反射(通过反射获取带参构造方法并使用) 反射(通过反射获取成员变 ...

  4. apache MINA之心跳协议运用

    摘要 心跳协议,对基于CS模式的系统开发来说是一种比较常见与有效的连接检测方式,最近在用MINA框架,原本自己写了一个心跳协议实现,后来突然发现MINA本身带有这样一个心跳实现,感于对框架的小小崇拜, ...

  5. 打开Linux ftp服务,如:vsftpd: unrecognized service

    打开Linux ftp服务,如:vsftpd: unrecognized service   [root@BZXXDBS02 ~]# service vsftpd start vsftpd: unre ...

  6. Nutch 使用总结

    Nutch 是一个开源Java 实现的搜索引擎.它提供了我们运行自己的搜索引擎所需的全部工具.包括全文搜索和Web爬虫. Nutch使用方法简介: http://blog.csdn.net/pengp ...

  7. IDEA Intellij 打开springboot项目 配置文件无法出现输入提示

    需要将java代码和资源文件进行标记

  8. Linux 启动文件、设置环境变量的位置

    系统级启动文件  ==================================== 1./etc/rc  主启动文件,不要修改它 2./etc/rc.conf  决定启动哪些系统自带的守护进程 ...

  9. POJ 3461 Oulipo[附KMP算法详细流程讲解]

      E - Oulipo Time Limit:1000MS     Memory Limit:65536KB     64bit IO Format:%I64d & %I64u Submit ...

  10. PHP pdf转化为图片(PNG)

    /** * 将pdf文件转化为多张png图片 * @param string $pdf pdf所在路径 (/www/pdf/abc.pdf pdf所在的绝对路径) * @param string $p ...