EventBroadcaster
EventBroadcaster 接口有以下方法:
// EventBroadcaster knows how to receive events and send them to any EventSink, watcher, or log.
type EventBroadcaster interface {
StartEventWatcher(eventHandler func(*v1.Event)) watch.Interface #启动事件监听,然后进行广播
StartRecordingToSink(sink EventSink) watch.Interface #启动记录事件到EventSink
StartLogging(logf func(format string, args ...interface{})) watch.Interface #启动记录事件到日志
StartStructuredLogging(verbosity klog.Level) watch.Interface #启动记录事件到日志,使用klog插件记录日志,可以指定日志级别
NewRecorder(scheme *runtime.Scheme, source v1.EventSource) EventRecorder #创建事件记录者
Shutdown() #停止事件广播
}
接口实现如下:
type eventBroadcasterImpl struct {
*watch.Broadcaster
sleepDuration time.Duration
options CorrelatorOptions
}
我们先看看EventBroadcaster的初始化
// Creates a new event broadcaster.
func NewBroadcaster() EventBroadcaster {
return &eventBroadcasterImpl{
Broadcaster: watch.NewBroadcaster(maxQueuedEvents, watch.DropIfChannelFull),
sleepDuration: defaultSleepDuration,
}
}
func NewBroadcasterForTests(sleepDuration time.Duration) EventBroadcaster {
return &eventBroadcasterImpl{
Broadcaster: watch.NewBroadcaster(maxQueuedEvents, watch.DropIfChannelFull),
sleepDuration: sleepDuration,
}
}
func NewBroadcasterWithCorrelatorOptions(options CorrelatorOptions) EventBroadcaster {
return &eventBroadcasterImpl{
Broadcaster: watch.NewBroadcaster(maxQueuedEvents, watch.DropIfChannelFull),
sleepDuration: defaultSleepDuration,
options: options,
}
}
系统为我们提供了3种初始化方法。3个方法都比较相近,都包含了一个watch.NewBroadcaster的过程,一会我们重点看一下这个方法,这里对其他参数简单的说明一下,sleepDuration是最终watcher在记录事件的时候报错后会重试,这个参数代表了每次重试中间的事件间隔。options这个参数在记录事件的过程中很重要,不赋值的话,系统会使用默认的一组值,用来对事件进行聚合处理,我们知道事件里面有一个count属性,表明此事件发生了多少次,这个值就是通过对事件的聚合而生成的值,k8s为了防止大量事件的产生对etcd造成冲击,就搞了这么一个聚合机制,把相似的事件聚合成一个event,后面会再详细讲解的
接下来看看Broadcaster生成方法
在这之前我先贴出Broadcaster的属性信息。
type Broadcaster struct {
// TODO: see if this lock is needed now that new watchers go through
// the incoming channel.
lock sync.Mutex
watchers map[int64]*broadcasterWatcher //注册的watcher
nextWatcher int64 // watchers的编号
distributing sync.WaitGroup
incoming chan Event //存放事件
// How large to make watcher's channel.
watchQueueLength int //watcher存储事件的缓冲管道长度
// If one of the watch channels is full, don't wait for it to become empty.
// Instead just deliver it to the watchers that do have space in their
// channels and move on to the next event.
// It's more fair to do this on a per-watcher basis than to do it on the
// "incoming" channel, which would allow one slow watcher to prevent all
// other watchers from getting new events.
fullChannelBehavior FullChannelBehavior //存放事件的缓冲通道满了之后,再来事件是否要抛弃事件
}
Broadcaster生成方法
func NewBroadcaster(queueLength int, fullChannelBehavior FullChannelBehavior) *Broadcaster {
m := &Broadcaster{
watchers: map[int64]*broadcasterWatcher{}, #观察者
incoming: make(chan Event, incomingQueueLength), #incomingQueueLength=25 事件接收缓冲,我们之前讲过,事件记录实际上是存储在了incoming里面,这个有一个25长度的缓冲
watchQueueLength: queueLength, #每个观察者broadcasterWatcher存储事件的缓冲长度1000
fullChannelBehavior: fullChannelBehavior, #broadcasterWatcher存放事件的result通道缓存满了之后再来写入的事件是否抛弃事件,这里默认都是抛弃,逻辑在下面的distribute()方法里面
}
m.distributing.Add(1) #sysc.WaitGroup
go m.loop() #死循环处理事件
return m
}
下面是loop方法
// loop receives from m.incoming and distributes to all watchers.
func (m *Broadcaster) loop() {
for event := range m.incoming { #死循环从incoming获取事件
...
m.distribute(event) #然后下发给观察者
}
m.closeAll()
m.distributing.Done() #结束后清理资源
}
可以看到loop方法,一直从m.incoming里面获取值,然后调用distribute方法,下发给watcher处理具体的事件,除非m.incoming被close,否则for循环将一直维持。
下面是distribute方法
func (m *Broadcaster) distribute(event Event) {
m.lock.Lock()
defer m.lock.Unlock()
if m.fullChannelBehavior == DropIfChannelFull {
for _, w := range m.watchers { #遍历所有的观察者
select {
case w.result <- event: #把事件分发给每一个观察者
case <-w.stopped: #如果观察者被停止
default: // Don't block if the event can't be queued. #非阻塞分发,如果w.result满了,则丢弃事件
}
}
} else {
for _, w := range m.watchers {
select {
case w.result <- event:
case <-w.stopped: #阻塞分发,如果w.result满了,则一直等待事件被处理后在放入事件
}
}
}
}
这个方法做的事情就是把从incoming里面获取的事件拿到之后,遍历所有的watcher,然后把事件放到每一个watcher接收事件的result通道里面。watcher在注册的时候,会启动一个for循环从result通道里面获取事件,执行记录逻辑,后面会看到的。
到这里初始化我们基本分析完了,启动了一个事件的记录核心逻辑,创建了一个核心结构Broadcaster。下面我们看看它所提供的这几个方法
NewRecorder方法如下:
// NewRecorder returns an EventRecorder that records events with the given event source.
func (e *eventBroadcasterImpl) NewRecorder(scheme *runtime.Scheme, source v1.EventSource) EventRecorder {
return &recorderImpl{scheme, source, e.Broadcaster, clock.RealClock{}}
}
构建了一个EventRecorder,用来记录事件,把我们的核心结构体e.Broadcaster传进去了,这样记录事件的时候,就会把事件放到我们传进去的这个Broadcaster的incoming里面,我们在这里的逻辑里面就能获取到record的事件
StartRecordingToSink()
StartLogging()
StartStructuredLogging()
这三个方法我们一起看
func (e *eventBroadcasterImpl) StartRecordingToSink(sink EventSink) watch.Interface {
eventCorrelator := NewEventCorrelatorWithOptions(e.options)
return e.StartEventWatcher(
func(event *v1.Event) {
recordToSink(sink, event, eventCorrelator, e.sleepDuration)
})
}
func (e *eventBroadcasterImpl) StartLogging(logf func(format string, args ...interface{})) watch.Interface {
return e.StartEventWatcher(
func(e *v1.Event) {
logf("Event(%#v): type: '%v' reason: '%v' %v", e.InvolvedObject, e.Type, e.Reason, e.Message)
})
}
// StartStructuredLogging starts sending events received from this EventBroadcaster to the structured logging function.
// The return value can be ignored or used to stop recording, if desired.
func (e *eventBroadcasterImpl) StartStructuredLogging(verbosity klog.Level) watch.Interface {
return e.StartEventWatcher(
func(e *v1.Event) {
klog.V(verbosity).InfoS("Event occurred", "object", klog.KRef(e.InvolvedObject.Namespace, e.InvolvedObject.Name), "kind", e.InvolvedObject.Kind, "apiVersion", e.InvolvedObject.APIVersion, "type", e.Type, "reason", e.Reason, "message", e.Message)
})
}
为什么要一起来看呢,因为他们的逻辑非常的相似,都是返回了一个e.StartEventWatcher的执行结果,只不过给传入StartEventWatcher()方法的参数不同而已。那么我们可以推断,这里的核心处理逻辑是StartEventWatcher()方法,其他3个方法是提供了一个最终处理的动作。
StartEventWatcher()
func (e *eventBroadcasterImpl) StartEventWatcher(eventHandler func(*v1.Event)) watch.Interface {
watcher := e.Watch() // 注册watcher到watchers里面
go func() {
defer utilruntime.HandleCrash()
for watchEvent := range watcher.ResultChan() {
event, ok := watchEvent.Object.(*v1.Event)
if !ok {
// This is all local, so there's no reason this should
// ever happen.
continue
}
eventHandler(event)
}
}()
return watcher
}
逻辑很简单,注册watcher到watchers里面,然后一个for循环,监听watcher的result,有事件的话,就调用传入的参数方法(eventHandler)去处理事件
我们看看e.Watch() 这个方法如何注册的,这个方法还是有点东西的
func (m *Broadcaster) Watch() Interface {
var w *broadcasterWatcher
m.blockQueue(func() {
m.lock.Lock()
defer m.lock.Unlock()
id := m.nextWatcher
m.nextWatcher++
w = &broadcasterWatcher{
result: make(chan Event, m.watchQueueLength),
stopped: make(chan struct{}),
id: id,
m: m,
}
m.watchers[id] = w
})
return w
}
func (b *Broadcaster) blockQueue(f func()) {
var wg sync.WaitGroup
wg.Add(1) //大家感受一下这块为什么要搞一个WaitGroup呢,有没有必要。
b.incoming <- Event{
Type: internalRunFunctionMarker,
Object: functionFakeRuntimeObject(func() {
defer wg.Done() //解锁的地方又在这么一个地方,迷惑!!!!!且看下面分析
f()
}),
}
wg.Wait()
}
这里我顺便把blockQueue()方法也附上了,Watch方法干了3件事,声明一个broadcasterWatcher对象,调用blockQueue方法(提供了watcher的创建方式),返回watcher。 blockQueue方法,竟然是给incoming里面写入了一个事件,而我们生成watcher的方法(入参)被放倒了Event的对象里面,也就是把注册watcher这个动作当成了一个”注册Event“,交给了事件核心处理逻辑去处理了,还记得核心逻辑loop方法吗
func (m *Broadcaster) loop() {
// Deliberately not catching crashes here. Yes, bring down the process if there's a
// bug in watch.Broadcaster.
for event := range m.incoming {
if event.Type == internalRunFunctionMarker { //聚光灯,来看这里!!!!!
event.Object.(functionFakeRuntimeObject)()
continue
}
m.distribute(event)
}
m.closeAll()
m.distributing.Done()
}
看到了吗,在接收到一个事件的时候,首先进行了一个事件类型判断,如果是internalRunFunctionMarker (”注册Event“),然后调用里面的方法,完成了watcher的注册。这样有什么好处呢?为什么要搞的这么麻烦,直接注册进去不行吗?我理解这里的意思主要是watcher不监视已经发生的历史数据,只是从注册发生起之后的事件,因为我们的事件都是按照时间顺序排队执行的,所以把注册当成一个事件排在队列里,那么它就能获取到”注册事件“发生之后所有的事件,之前已经产生的事件都不在它的处理范围之内。
前面的sync.WaitGroup问题是因为直到注册动作完成之前都不算注册成功,所以要加一个wait,直到整个注册事件被loop方法执行完成才算注册成功。避免以为注册成功,但是却没有接受到事件的问题。好了,到这里注册的逻辑我们就理清楚了,下面看看整个协程里面的动作
go func() {
defer utilruntime.HandleCrash()
for watchEvent := range watcher.ResultChan() {
event, ok := watchEvent.Object.(*v1.Event)
if !ok {
// This is all local, so there's no reason this should
// ever happen.
continue
}
eventHandler(event)
}
}()
// 下面是broadcasterWatcher 的结构,以及ResultChan方法,都比较简单,主要是用来说明watcher.ResultChan()
type broadcasterWatcher struct {
result chan Event
stopped chan struct{}
stop sync.Once
id int64
m *Broadcaster
}
// ResultChan returns a channel to use for waiting on events.
func (mw *broadcasterWatcher) ResultChan() <-chan Event {
return mw.result
}
循环遍历watcher的result管道,获取事件,然后调用入参eventHandler函数,执行对事件的处理。
还有一个Shutdown方法
Shutdown()
func (e *eventBroadcasterImpl) Shutdown() {
e.Broadcaster.Shutdown()
}
///
func (m *Broadcaster) Shutdown() {
close(m.incoming)
m.distributing.Wait()
}
关闭了incoming,从loop方法可以看出来,将会结束对incoming的遍历动作,关闭所有watcher的result通道,清空watcher,下面是代码:
func (m *Broadcaster) loop() {
// Deliberately not catching crashes here. Yes, bring down the process if there's a
// bug in watch.Broadcaster.
for event := range m.incoming {
if event.Type == internalRunFunctionMarker {
event.Object.(functionFakeRuntimeObject)()
continue
}
m.distribute(event)
}
m.closeAll()
m.distributing.Done() //结束关闭动作的等待
}
// m.closeAll()
func (m *Broadcaster) closeAll() {
m.lock.Lock()
defer m.lock.Unlock()
for _, w := range m.watchers {
close(w.result)
}
// Delete everything from the map, since presence/absence in the map is used
// by stopWatching to avoid double-closing the channel.
m.watchers = map[int64]*broadcasterWatcher{}
}
总结:EventBroadcaster接口的实现是eventBroadcasterImpl,eventBroadcasterImpl的核心方法是注册watcher,停止watcher和生成第一个大杀器EventRecorder,eventBroadcasterImpl的核心结构是Broadcaster结构体。Broadcaster包含了所有的观察者watchers,接收事件的缓冲incoming,loop()方法实现了把接收到的事件分发到所有watcher的逻辑。
broadcasterWatcher
上面已经大致讲过了broadcasterWatcher 这个组件,这里在啰嗦几句
// broadcasterWatcher handles a single watcher of a broadcaster
type broadcasterWatcher struct {
result chan Event //待处理事件存放的缓冲通道
stopped chan struct{}
stop sync.Once
id int64 //watcher编号
m *Broadcaster //核心结构体,上面有介绍,这里主要是为了停止对应的watcher
}
// ResultChan返回事件缓冲通道
func (mw *broadcasterWatcher) ResultChan() <-chan Event {
return mw.result
}
// Stop stops watching and removes mw from its list.
func (mw *broadcasterWatcher) Stop() {
mw.stop.Do(func() {
close(mw.stopped)
mw.m.stopWatching(mw.id)
})
}
这里代码都比较简单。自己看一下就可以了
事件系列最后一篇 kubernetes 事件(Events)源码分析—事件处理逻辑