2021SC@SDUSC
目录
3.给Sinks增加Jetty的ServletContextHandler
启动测量系统MetricsSystem
MetricsSystem使用codahale提供的第三方测量仓库Metrics。MetricsSystem中有三个概念:
- Instance:指定了谁在使用测量系统
- Source:指定了从哪里收集测量数据
- Sink:指定了往哪里输出测量数据、
Spark按照Instance的不同,区分为Master,Worker,Application,Driver和Executor。
Spark目前提供的Sink有ConsoleSink、CsvSink、JmxSink、MetricsServlet、GraphiteSink等。
Spark中使用MetricsServlet作为默认的Sink。
MetricsSystem的启动代码如下:
val metricsSystem = env.metricsSystem
metricsSystem.start()
Metrics的启动过程包括如下步骤:
- 注册Sources
- 注册Sinks
- 给Sinks增加Jetty的ServletContextHandler
MetricsSystem启动完毕后,会遍历与Sinks有关的ServletContextHandler,并调用attachHandler将它们绑定到SparkUI上。
metricsSystem.getServletHandlers.foreach(handler => ui.foreach(_.attachHandler(handler)))
1.注册Sources
registerSources方法用于注册Sources,告诉测量系统从哪里收集测量数据,它的实现见代码 3-45。注册Sources的过程分为以下步骤:
- 从metricsConfig获取Driver的Properties,默认为创建MetricsSystem的过程中解析的{sink.servlet.class = org.apache.spark.metrics.sink.MetricsServlet, sink.servlet.path=/metrics/json}。
- 用正则匹配Driver的Properties中以source.开头的属性。然后将属性中的Source反射得到的实例加入ArrayBuffer[Source]。
- 将每个source的metricRegistry(也是MetricSet的子类型)注册到ConcurrentMap<String, Metric> metrics。
// code 3-45
private def registerSources() {
val insConfig = metricsConfig.getinstance(instance)
val sourceConfigs = metricsConfig.subProperties(instConfig,MetricsSystem.SOURCE_REGEX)
// Registerall the sources related to instance
sourceConfigs.foreach { kv =>
val classPath = kv._2.getProperty("class")
try {
val source = Class.forName(classPath).newinstance()
registerSource(source.aslnstanceOf[Source])
} catch {
case e: Exception=>logError("Source class "+class Path+" cannot be
instantiated", e)
}
}
}
2.注册Sinks
registerSinks方法用于注册Sinks,即告诉测量系统MetricsSystem往哪里输出测量数据, 它的实现见代码3-46,注册Sinks的步骤如下:
- 从Driver的Properties中用正则匹配以sink开头的属性,如{sink.servlet.class=org. apache.spark.metrics.sink.MetricsServlet, sink.servlet.path=/metrics/json},将其转换为Map(servlet -> {class=org.apache.spark.metrics.sink.MetricsServlet, path=/metrics/json})。
- 将子属性 class对应的类metricsServlet反射得到MetricsServlet 实例。 如果属性的key是servlet,将其设牲为metricsServlet;如果是Sink,则加入到ArrayBuffer[Sink]中。
// code 3-46
private def registerSinks () {
val instConfig = metricsConfig.getinstance(instance)
val sinkConfigs = metricsConfig.subProperties(instConfig, MetricsSystem.SINK_REGEX)
sinkConfigs.foreach { kv =>
val classPath = kv._2.getProperty("class")
if (null != classPath) {
try {
val sink = Class.forName(classPath)
.getConstructor(classOf [Properties], classOf[MetricRegistry],
classOf[SecurityManager])
.newinstance(kv._2, registry, securityMgr)
if (kv._l == "servlet” ) {
metricsServlet = Some(sink.asinstanceOf[MetricsServlet])
} else {
sinks += sink.asInstanceOf(Sink)
}
} catch {
case e: Exception => logError("Sink class "+ classPath + " cannot
be instantialized", e)
}
}
}
}
3.给Sinks增加Jetty的ServletContextHandler
为了能够在SparkUI(网页)访问到测量数据, 所以需要给Sinks增加Jetty的ServletContextHandler,这里主要用到MetricsSystem的getServletHandlers方法实现如下:
def getServletHandlers = {
require(running, "Can only call getServletHandlers on a running MetricsSystem")
metricsServlet.map(_.getHandlers).getOrElse(Array())
}
可以看到调用了metricsServlet的getHandlers, 其实现如下:
def getHandlers = Array [ServletContextHandler] (
createServletHandler(servletPath,
new ServletParams(request => getMetricsSnapshot(request), "text/json"),
securityMgr)
)
最终生成处理metrics/json请求的ServletConlextHandler,而请求的真正处理由getMetricsSnapshot方法,利用fastjson解析。生成的ServletContextHandler通过SparkUI的 attachHandler方法也被绑定到SparkUI。