天天看点

《深入理解Spark:核心思想与源码分析》——3.4节SparkUI详解

本节书摘来自华章社区《深入理解spark:核心思想与源码分析》一书中的第3章,第3.4节sparkui详解,作者耿嘉安,更多章节内容可以访问云栖社区“华章社区”公众号查看

3.4 sparkui详解

任何系统都需要提供监控功能,用浏览器能访问具有样式及布局并提供丰富监控数据的页面无疑是一种简单、高效的方式。sparkui就是这样的服务,它的架构如图3-1所示。

在大型分布式系统中,采用事件监听机制是最常见的。为什么要使用事件监听机制?假如sparkui采用scala的函数调用方式,那么随着整个集群规模的增加,对函数的调用会越来越多,最终会受到driver所在jvm的线程数量限制而影响监控数据的更新,甚至出现监控数据无法及时显示给用户的情况。由于函数调用多数情况下是同步调用,这就导致线程被阻塞,在分布式环境中,还可能因为网络问题,导致线程被长时间占用。将函数调用更换为发送事件,事件的处理是异步的,当前线程可以继续执行后续逻辑,线程池中的线程还可以被重用,这样整个系统的并发度会大大增加。发送的事件会存入缓存,由定时调度器取出后,分配给监听此事件的监听器对监控数据进行更新。

《深入理解Spark:核心思想与源码分析》——3.4节SparkUI详解

我们先简单介绍图3-1中的各个组件:dagscheduler是主要的产生各类sparklistener-event的源头,它将各种sparklistenerevent发送到listenerbus的事件队列中,listenerbus通过定时器将sparklistenerevent事件匹配到具体的sparklistener,改变sparklistener中的统计监控数据,最终由sparkui的界面展示。从图3-1中还可以看到spark里定义了很多监听器sparklistener的实现,包括jobprogresslistener、environmentlistener、storagelistener、executorslistener,它们的类继承体系如图3-2所示。

《深入理解Spark:核心思想与源码分析》——3.4节SparkUI详解

3.4.1 listenerbus详解

listenerbus的类型是livelistenerbus。livelistenerbus实现了监听器模型,通过监听事件触发对各种监听器监听状态信息的修改,达到ui界面的数据刷新效果。livelistenerbus由以下部分组成:

事件阻塞队列:类型为linkedblockingqueue[sparklistenerevent],固定大小是10 000;

监听器数组:类型为arraybuffer[sparklistener],存放各类监听器sparklistener。

事件匹配监听器的线程:此thread不断拉取linkedblockingqueue中的事件,遍历监听器,调用监听器的方法。任何事件都会在linkedblockingqueue中存在一段时间,然后thread处理了此事件后,会将其清除。因此使用listenerbus这个名字再合适不过了,到站就下车。listenerbus的实现见代码清单3-15。

代码清单3-15 livelistenerbus的事件处理实现

3.4.2 构造jobprogresslistener

我们以jobprogresslistener为例来讲解sparklistener。jobprogresslistener是sparkcontext中一个重要的组成部分,通过监听listenerbus中的事件更新任务进度。sparkstatustracker和sparkui实际上也是通过jobprogressli

stener来实现任务状态跟踪的。创建jobprogresslistener的代码如下。

jobprogresslistener 实现了onjobstart、onjobend、onstagecompleted、onstagesubmitted、ontaskstart、ontaskend等方法,这些方法正是在listenerbus的驱动下,改变jobprogress-listener中的各种job、stage相关的数据。

3.4.3 sparkui的创建与初始化

sparkui的创建,见代码清单3-18。

代码清单3-18 sparkui的声明

3.4.4 spark ui的页面布局与展示

sparkui究竟是如何实现页面布局及展示的?jobstab展示所有job的进度、状态信息,这里我们以它为例来说明。jobstab会复用sparkui的killenabled、sparkcontext、job-progresslistener,包括alljobspage和jobpage两个页面,见代码清单3-22。

代码清单3-22 jobstab的实现

alljobspage由render方法渲染,利用jobprogresslistener中的统计监控数据生成激活、完成、失败等状态的job摘要信息,并调用jobstable方法生成表格等html元素,最终使用uiutils的headersparkpage封装好css、js、header及页面布局等,见代码清单3-23。

代码清单3-23 alljobspage的实现

由于代码清单3-27所在的类中使用import org.apache.spark.ui.jettyutils._导入了jettyutils的静态方法,所以createservlethandler方法实际是jettyutils 的静态方法createservlethandler。createservlethandler实际创建了javax.servlet.http.httpservlet的匿名内部类实例,此实例实际使用(request: httpservletrequest) => page.render(request)函数参数来处理请求,进而渲染页面呈现给用户。有关createservlethandler的实现及jetty的相关信息,请参阅附录c。

3.4.5 sparkui的启动

sparkui创建好后,需要调用父类webui的bind方法,绑定服务和端口,bind方法中主要的代码实现如下。

serverinfo = some(startjettyserver("0.0.0.0", port, handlers, conf, name))

jettyutils的静态方法startjettyserver的实现请参阅附录c。最终启动了jetty提供的服务,默认端口是4040。