client-go之Informer体系
文章通过类图依赖关系图梳理了client-go tools包中的informer体系结构,并总结出了客户端调用时的注意事项以及原因。
0、Infromer类图体系概览图:
一、SharedInformer
接口主要功能:
- 给其客户端提供一个给定资源对象集合的最终一致性访问接口
1、内部维护了一个本地缓存,通过GetStore()暴露,该接口的实现sharedIndexInformer同时通过GetIndexer()暴露,接口SharedIndexInformer包含了SharedInformer;
2、本地缓存刚开始是空的,在调用Run()过程中被填充或更新,Run()保证本地缓存和ApiServer的最终一致性
3、存放在Store中的对象格式为:namespace/name,客户端可以使用MetaNamespaceKeyFunc(obj)方法获取对象的Key值,使用SplitMetaNamespaceKey(key)获得key的组成部分
4、添加对象事件监听器AddEventHandler、AddEventHandlerWithResyncPeriod
二、SharedIndexInformer
接口实现sharedIndexInformer
三大组件
1、Indexer:索引化的本地缓存
2、Controller控制器:使用ListWatcher批量List资源对象以及Watch资源对象通知事件并Push到DeltaFIFO中
3、sharedProcessor:该组件用于分发资源对象通知事件到informer的所有客户端(ResourceEventHandler)
三、SharedIndexInformer
接口实现sharedIndexInformer
构造方法
NewSharedInformer(lw ListerWatcher, exampleObject runtime.Object, defaultEventHandlerResyncPeriod time.Duration) SharedInformer
调用
NewSharedIndexInformer( ListerWatcher, runtime.Object, defaultEventHandlerResyncPeriod time.Duration, Indexers) SharedIndexInformer
四、核心SharedInformer
调用关系梳理
1、上游调用链: client-go/informers
// 1、客户端首先创建InformerFactory工厂
func NewSharedInformerFactoryWithOptions(client kubernetes.Interface, defaultResync time.Duration, options ...SharedInformerOption) SharedInformerFactory
// 2、工厂包含各个API对象的Informer,以创建Event informer举例
func (f *sharedInformerFactory) Events() events.Interface {
return events.New(f, f.namespace, f.tweakListOptions)
}
// 3、得到指定API对象的各个版本的Informer的总接口
func New(f internalinterfaces.SharedInformerFactory, namespace string, tweakListOptions internalinterfaces.TweakListOptionsFunc) Interface {
return &group{factory: f, namespace: namespace, tweakListOptions: tweakListOptions}
}
// 4、客户端指定资源组版本获得该资源组的Informer的接口
func (g *group) V1() v1.Interface {
return v1.New(g.factory, g.namespace, g.tweakListOptions)
}
// 5、客户端调用步骤4中资源组接口获取该资源组下指定资源对象的Informer
// factory.Events().V1().Events()
func (v *version) Events() EventInformer {
return &eventInformer{factory: v.factory, namespace: v.namespace, tweakListOptions: v.tweakListOptions}
}
// factory.Events().V1().Events().Informer()
func (f *eventInformer) Informer() cache.SharedIndexInformer {
// 回调工厂方法的InformerFor,给指定API对象创建SharedIndexInformer
return f.factory.InformerFor(&corev1.Pod{}, f.defaultInformer)
}
func (f *sharedInformerFactory) InformerFor(obj runtime.Object, newFunc internalinterfaces.NewInformerFunc) cache.SharedIndexInformer {
// 注册该API对象的Informer到工厂中
informerType := reflect.TypeOf(obj)
informer, exists := f.informers[informerType]
f.informers[informerType] = informer
return informer
}
// 6、步骤5中回调工厂的InformerFor方法时传入defaultInformer方法进行回调
// 调用1
func (f *eventInformer) defaultInformer(client kubernetes.Interface, resyncPeriod time.Duration) cache.SharedIndexInformer {
return NewFilteredEventInformer(client, f.namespace, resyncPeriod, cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc}, f.tweakListOptions)
}
// 使用到了 type Indexers map[string]IndexFunc 并赋值为 cache.NamespaceIndex=cache.MetaNamespaceIndexFunc
// 调用2
func NewFilteredEventInformer(kubernetes.Interface, namespace string, time.Duration, cache.Indexers, TweakListOptionsFunc) cache.SharedIndexInformer
// 7、客户端启动工厂方法:遍历所有注册的informer,开启协程进行数据同步
func (f *sharedInformerFactory) Start(stopCh <-chan struct{}) {
f.lock.Lock()
defer f.lock.Unlock()
for informerType, informer := range f.informers {
if !f.startedInformers[informerType] {
go informer.Run(stopCh)
f.startedInformers[informerType] = true
}
}
}
2、下游调用链: 主要使用到了底层cache包中的功能
调用
NewIndexer(keyFunc KeyFunc, indexers Indexers) Indexer
调用
NewThreadSafeStore(indexers Indexers, indices Indices) ThreadSafeStore
五、总结
1、客户端使用Informer方式:
根据上游调用链的分析,我们使用Informer工厂,需要保证工厂的InformerFor方法被回调,才能保证指定资源对象的Informer被创建,有以下两种常规方式启动工厂:
方式1:
// 步骤1,创建工厂
factory := informers.NewSharedInformerFactoryWithOptions(clientSet, 10*time.Second, informers.WithTweakListOptions(func(options *metaV1.ListOptions) {
options.LabelSelector = labels.Everything().String()
}))
// 步骤2,使用工厂创建指定API对象的Informer,必须调用到Informer()方法,该方法会回调到工厂方法的InformerFor
factory.Apps().V1().Deployments().Informer()
// 步骤3,使用factory启动informer,该方法是异步,里面会遍历所有的Informer,并调用informer的Run方法,Run方法核心功能就是开启资源对象的本地缓存
factory.Start(stopChan)
// 步骤4,等待该工厂里的所有启动过的informer完成数据缓存到本地
factory.WaitForCacheSync(stopChan)
方式2:
// 步骤1: 创建工厂
f := informers.NewSharedInformerFactory(clientSet, 0)
// 步骤2: 注册informer
deploymentSharedIndexInformer := f.Apps().V1().Deployments().Informer()
// 步骤3: 启动informer
go deploymentSharedIndexInformer.Run(stopInformerChan)
// 步骤4: 等待informer完成本地数据缓存
cache.WaitForNamedCacheSync("deploymentInformerController", stopInformerChan, deploymentSharedIndexInformer.HasSynced)
2、设计层面
依赖接口而不依赖实现:面向接口编程