一、事件总线介绍
Spark 定义了一个特质 ListenerBus,可以接受事件并且将事件提交到对应事件的监听器。
该特征主要有一个 listenersPlusTimers成员,用于维护所有注册的监听器,其数据结构是一个线程安全的CopyOnWriteArrayList[(L, Option[Timer])]。
该特征还有几个主要的函数:
- listener:返回listenersPlusTimers中所有的监听器
- getTimer:返回一个Timer用于统计事件处理时间
- addListener::添加 listener
- removeListener:删除 listener
- removeListenerOnError:内部调用 removeListener,可由子类覆盖
- postToAll: 把事件发送给所有的 listener,虽然CopyOnWriteArrayList 是线程安全的,但 postAll 引入了“先检查后运行”的逻辑,因此该方法不是线程安全的。
- doPostEvent:给特定 listener 发送事件,该方法具体需要子类实
- findListenersByClass:根据类型查找 listener 列表
二、ListenerBus 继承体系
上图是 spark 2.3.0 版本事件总线的继承关系,版本不同,会略有不同。
每个 ListenerBus 用于将不同的 Event 投递到不同的 Listener 中,下面以主要分析下 LiveListenerBus。
三、LiveListenerBus 详解
LiveListenerBus 不再继承 SparkListenerBus,和其他 ListenerBus 不同的是, LiveListenerBus 是将事件广播给事件队列集合中的AsyncEventQueue,然后每个AsyncEventQueue中都有一个线程不断从自身成员变量eventQueue中获取事件,将事件异步投递给监听器,达到实时刷新UI界面数据的效果。
3.1、LiveListenerBus 中的属性:
- sparkContext:spark上下文,维护者好多组件
- metrics:度量系统的数据源
- started:标记 LiveListenerBus 的启动状态的 AtomicBoolean 类型的变量
- stopped:标记LiveListenerBus的停止状态的 AtomicBoolean 类型的变量
- droppedEventsCounter:使用 AtomicLong 类型对删除的事件进行计数,每当日志打印了 droppedEventsCounter 后,会将droppedEventsCounter 重置为0
- lastReportTimestamp:记录最后一次日志打印 droppedEventsCounter 的时间戳
- queues:事件队列集合包括shared、sppStatus、eventLog和executorManagement
- queuedEvents:sparkListenerEvent事件集合
3.2、启动LiveListenerBus(start)
开始向附加的监听器发送事件。 在事件总线启动之前发送所有缓冲的事件,然后在事件总线运行时异步监听任何其他事件。 此方法只调用一次。
代码清单3-1
-
- 查看是否已经启动,若已启动则抛出异常。
- 遍历事件队列集合。
- 启动事件队列,调用了AsyncEventQueue的start(代码清单4-1)方法,主要是启动一个异步处理事件的线程。
- 遍历事件集合。
- 将事件发送给事件队列下注册的所有监听器,调用了AsyncEventQueue的post(代码清单4-2)方法。
- 将LiveListenerBusMetrics注册到度量系统。
3.3、事件发送(post,postToAll)
所有维护着LivelistenerBus的组件,通过post方法向事件队列发送事件。
代码清单3-2
-
- 度量系统记录事件数+1
- 如果事件缓存为null,则表示事件总线已经启动(因为在启动时会先将缓存中的事件发送给事件队列处理),所以不需要同步,可以直接将事件发送给事件队列(调用postToQueues方法,代码清单3-3),结束后直接返回。
- 如果事件总线未启动,则需要同步向时间缓存quevedEvents中放入事件,结束后直接返回。
- 如果不符合2个3中的情况,则直接将事件发送给事件队列。
代码清单3-3
四、AsyncEventQueue详解
AsyncEventQueue继承了SparkListenerBus,实现了将事件异步投递给监听器,达到实时刷新UI界面的效果。
4.1、AsyncEventQueue的属性:
- eventQueue:事件缓存,liveListenerBus post的事件,会放到这个缓存中。
- eventCount: 事件计数器,waitUntilEmpty()中用到,使得该方法仅在队列中的事件被完全处理完时返回。
- droppedEventsCounter:使用 AtomicLong 类型对删除的事件进行计数,每当日志打印了 droppedEventsCounter 后,会将droppedEventsCounter 重置为0
- lastReportTimestamp:记录最后一次带引droppedEventsCounter的时间戳
- logDroppedEvent:使用AtomicBollean标识是否记录删除事件
- sc:sparkContext上下文
- started:标记 LiveListenerBus 的启动状态的 AtomicBoolean 类型的变量
- stopped:标记LiveListenerBus的停止状态的 AtomicBoolean 类型的变量
- droppedEvents:度量系统中的时间删除计数器(counter)
- processingTime:度量系统中的时间处理计时器(timer)
- dispatchThread:异步事件处理线程
4.2、AsyncEventQueue启动(start)
启动异步线程以将事件分派给监听器
代码清单4-1
此处只分析异步线程的工作流程dispatch
-
- 从eventQueue中获取事件
- 调用超类ListenerBus的postToAll(对监听器进行遍历,并调用SparkListenerBus的doPostEvent方法对事件进行匹配后执行监听器的对应方法)方法。
代码清单4-2
负责将事件放到事件缓冲队列中
五、流程总结
Spark 的事件总线大致的流程: