深入理解Spark消息总线:spark listener

        spark lisenter的设计方案?目的是啥?能干什么?怎么和其它组件解耦合?

需求背景

        任务监控的需求:人对于未知的、不可见的事务总是充满恐惧。对于集群中运行的任务,希望获取运行过程中的状态信息,用户追踪、可视化运行过程中的细节;

        作业优化:程序优化是很正常的事情,例如运行时间过程我们会优化性能,消费数据堆积我们会考虑增加并行度等;运行的中间状态数据可以用于反向优化作业。

设计目标

        异步执行;可扩展;松耦合

设计方案

        无论什么问题,第一时间想到的应该是:有没有现成的方法论,前人有没有总结出什么可用的经验或者范式。在软件工程领域,经验或者范式以“设计模式”来定义和规范,打开任何优秀的框架源码,可以看到许许多多的设计模式。设计模式来自于实际生产中的总结,最终也应该应用到实际生产中去促进可扩展、松耦合软件的生产。监控系统在实现上是有通用的设计模式作为参考的,这个设计模式就是:监听者模式。

设计模式-监听者模式:

        定义了主题和观察者之间一对多依赖,当主题状态发生变化时,依赖的所有观察者将会收到通知。对于消息的通知,有两种实现方式:主题push和观察者pull。

        对于push模式,主题中有三个不可缺失的方法:register、remove、push。register方法,用于注册观察者,一个主题下可以注册N个观察者;remove用于除名注册的观察者;push方法用于将消息推送给观察者,该方法中会遍历注册上来的观察者列表,调用观察者的回调函数将消息回写给观察者。

        对于pull模式,消息的获取由观察者主动去pull,因此观察者需要有主题的引用,观察者调用主题的pull方法获取到主题中的状态数据。为了验证是否是一个合法的观察者,因此观察者也是需要向主题中进行注册的。

思考:

        spark是一个复杂的系统,可以划分出很多主题,例如:application的启动和完成,task的开始和完成,stage的提交和完成,rdd的持久化和非持久化,blockManager的增加和移除,环境变量的更新等等。这些主题如何集中管理?是否需要抽象出N个主题?怎样做才能减少混乱做到最优?

        先来看下spark中的解决方案,spark为了解决复杂类型的事件订阅和监听,采用的LisenterBus的设计,总体上还是观察者模式,是push模式的实现,LisenterBus会主动推送消息给监听者。

        整体设计如下:

        Spark默认的LisenterBus实现是LiveListenerBus,其中的queues属性即是观察者模式中的Subject主体集合,通过name对应一个具体的SyncEventQueue实现类,该类是真正的主题。即上图中的shard、appStatus、executorManagement、eventLog等。提供了addListener注册监听器、removeListener移除监听器、postToAll发送消息等方法。

        对于多主题的管理,提供了addToQueue(L,q)方法,L表示Lisenter,q表示主题(对应SyncEventQueue实现类,即Subject主题),该方法先判断是否存在主题,不存在,需要先创建,然后将L放入集合中,完成监听者在主题中的注册。

        具体的监听者是SparkLisenterInterface接口的实现类,该接口中定义了很多回调方法,当特定的事件到达Subject的消息队列时,怎样断定这个消息发给哪个监听者处理呢?必须知道,Spark的LisenterBus收到一个事件时,会调用post方法,将该事件发给所有的Subject,而监听只会处理特定的事件,怎么办呢?这必须要借助模式匹配了!!

        下面以精简的步骤来厘清主要的数据流向:

  1. 外部事件向LisenterBus发送SparkLisenterEvent事件
  2. LisenterBus调用post方法,向所有的Subject发送收到的Event事件
  3. Subject自身拥有独立线程,执行dispach方法,遍历注册到自身的Lisenters集合,调用Lisenter上的回调方法,而默认队列AsyncEventQueue实现了SparkListenerBus接口,该接口中重写了doPostEvent方法,这个方法有什么特殊之处呢?将match case模式匹配中的Event调用Lisenter的相关方法进行处理,而这些方法是我们编写Lisenter需要重写的方法。

        何时启动消息总线呢?Spark编程的总入口是SparkContext,在SparkContext中可以发现实例化的代码。

_listenerBus = new LiveListenerBus(_conf)

总线启动后,就可以轻松处理Spark内部的各种事件了!

        应用:拿到spark运行的内部数据,可以进行很多的应用了,例如:spark web上各个任务运行进度实时展示;spark sql的执行计划等。当然也可以将这些内部的数据拿到,写到自己的业务系统进行相关的应用,如:数据血缘分析。

应用:数据血缘关系管理

1.实现QueryExecutionListener接口

class Lisener extends QueryExecutionListener {
  override def onSuccess(funcName: String, qe: QueryExecution, durationNs: Long): Unit = {
     println(qe.logical.toJSON)
     println(qe.analyzed.toJSON)
     print(qe.executedPlan.toJSON)
  }

  override def onFailure(funcName: String, qe: QueryExecution, exception: Exception): Unit = {
     println(qe.logical.toJSON)
    println(qe.executedPlan.toJSON)
  }
}

2.使用

spark-submit --master yarn  --conf 'spark.sql.queryExecutionListeners=xxxx.Lisener' --class  xxxx.listener  xxx.jar

        拿到执行计划数据结构之后,通过解析数据结果,能够拿到原表、目标表、原属性和目标属性计算映射关系等,通过这中方式能够完善spark相关数据血缘管理。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

艾尔aier

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值