kubernetes 事件(Event)源码分析(三)---三大杀器之EventBroadcaster&broadcasterWatcher

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)源码分析—事件处理逻辑

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值