k8s开发四-informer下

169 阅读3分钟

一,SharedInformer组件结构

image.png

  • 蓝色代表接口,其他颜色代表实现类

  • SharedInformer对外暴露的接口声明,sharedIndexInformer是内部实现类,最核心的组件indexer,Controller

  • controller是接口Controller实现,主要职责维护队列生命周期管理,以及processLoop不断消费队列消息

  • Relector通过client-go的listWatcher不断拉取对象变化数据,然后加入store

  • DeltaFIFOcontrollerRelector中表现为不同的接口,这样数据在Relector以存储方式加入,在controller中以队列形式处理

二,根据数据流向进行源码走读

  • 源码走读只贴了与数据流转相关的代码,其他代码用省略号...表示
  • 省略了stop相关控制代码
  • 省略了失败重试代码
  • 省略了resourceVersion相关代码
  • 其他例如日志代码等等非核心逻辑代码
2.1 Relector读取数据
func (r *Reflector) Run(stopCh <-chan struct{}) {
   ...
   if err := r.ListAndWatch(stopCh); err != nil {
         r.watchErrorHandler(r, err)
   }
   ...
}

Reflector 启动(Run)的时候会调用Reflector的ListAndWatch方法


func (r *Reflector) ListAndWatch(stopCh <-chan struct{}) error {
   ...

   if fallbackToList {
      err = r.list(stopCh)
      if err != nil {
         return err
      }
   }

   ...
   return r.watch(w, stopCh, resyncerrc)
}
  • 首先调用list方法,拉取所有数据
  • 然后调用watch,监听数据变化
  • 实际上还有watchList方法,它与list的调用互斥,有兴趣的可自行研究

func (r *Reflector) list(stopCh <-chan struct{}) error {
   ...
   //分页拉取数据封装,避免一次性拉取数据过多,对apiserver造成io压力过大
   list, paginatedResult, err = pager.List(context.Background(), options)
   ...
   if err := r.syncWith(items, resourceVersion); err != nil {
      return fmt.Errorf("unable to sync list result: %v", err)
   }
   ...
}


func (r *Reflector) syncWith(items []runtime.Object, resourceVersion string) error {
   found := make([]interface{}, 0, len(items))
   for _, item := range items {
      found = append(found, item)
   }
   return r.store.Replace(found, resourceVersion)
}

  • list拉取数据后会调用syncWith方法
  • syncWith将数据同步给store(实际上是DeltaFIFO)

func (r *Reflector) watch(w watch.Interface, stopCh <-chan struct{}, resyncerrc chan error) error {
   ...

   for {
      ...
      if w == nil {
         ...
         w, err = r.listerWatcher.Watch(options)
         ...
      }

      err = watchHandler(start, w, r.store, r.expectedType, r.expectedGVK, r.name, r.typeDescription, r.setLastSyncResourceVersion, nil, r.clock, resyncerrc, stopCh)
      w.Stop()
      w = nil
      
      //判断错误是否continue重试
      ...
   }
}


func watchHandler(start time.Time,
   w watch.Interface,
   store Store,
   expectedType reflect.Type,
   expectedGVK *schema.GroupVersionKind,
   name string,
   expectedTypeName string,
   setLastSyncResourceVersion func(string),
   exitOnInitialEventsEndBookmark *bool,
   clock clock.Clock,
   errc chan error,
   stopCh <-chan struct{},
) error {
   ...

loop:
   for {
      select {
      case <-stopCh:
         return errorStopRequested
      case err := <-errc:
         return err
      case event, ok := <-w.ResultChan():
         ...
         switch event.Type {
         case watch.Added:
            err := store.Add(event.Object)
            ...
         case watch.Modified:
            err := store.Update(event.Object)
            ...
         case watch.Deleted:
            err := store.Delete(event.Object)
            ...
         ...
      }
   }

   ...
}

watcher会根据eventType来调用store的不同方法

2.2 controller相关代码
//controller启动
func (c *controller) Run(stopCh <-chan struct{}) {
   ...
   //创建Reflector
   r := NewReflectorWithOptions(
      c.config.ListerWatcher,
      c.config.ObjectType,
      c.config.Queue,
      ReflectorOptions{
         ResyncPeriod:    c.config.FullResyncPeriod,
         TypeDescription: c.config.ObjectDescription,
         Clock:           c.clock,
      },
   )
   ...
   
   var wg wait.Group

   //启动Reflector,拉取数据
   wg.StartWithChannel(stopCh, r.Run)
   
   //速率控制调用processLoop
   wait.Until(c.processLoop, time.Second, stopCh)
   wg.Wait()
}

func (c *controller) processLoop() {
   for {
      obj, err := c.config.Queue.Pop(PopProcessFunc(c.config.Process))
      ...
   }
}
  • 创建Reflector
  • 启动Reflector,拉取数据
  • 调用processLoop
  • processLoop会调用controller中config的Process方法

2.3 sharedIndexInformer相关代码
func (s *sharedIndexInformer) Run(stopCh <-chan struct{}) {
   ...
   //用indexer构建fifo队列
   fifo := NewDeltaFIFOWithOptions(DeltaFIFOOptions{
      KnownObjects:          s.indexer,
      EmitDeltaTypeReplaced: true,
      Transformer:           s.transform,
   })

   cfg := &Config{
      Queue:             fifo,
      ListerWatcher:     s.listerWatcher,
      ...
      Process:           s.HandleDeltas,
      ...
   }

   func() {
      s.startedLock.Lock()
      defer s.startedLock.Unlock()
      
      //创建controller
      s.controller = New(cfg)
      s.controller.(*controller).clock = s.clock
      s.started = true
   }()

   ...

   //启动controller
   s.controller.Run(stopCh)
}

//被controller的processLoop调用
func (s *sharedIndexInformer) HandleDeltas(obj interface{}, isInInitialList bool) error {
   s.blockDeltas.Lock()
   defer s.blockDeltas.Unlock()

   if deltas, ok := obj.(Deltas); ok {
      return processDeltas(s, s.indexer, deltas, isInInitialList)
   }
   return errors.New("object given as Process argument is not Deltas")
}

func processDeltas(
   handler ResourceEventHandler,
   clientState Store,
   deltas Deltas,
   isInInitialList bool,
) error {
   for _, d := range deltas {
      obj := d.Object

      switch d.Type {
      case Sync, Replaced, Added, Updated:
         if old, exists, err := clientState.Get(obj); err == nil && exists {
            if err := clientState.Update(obj); err != nil {
               return err
            }
            handler.OnUpdate(old, obj)
         } else {
            if err := clientState.Add(obj); err != nil {
               return err
            }
            handler.OnAdd(obj, isInInitialList)
         }
      case Deleted:
         if err := clientState.Delete(obj); err != nil {
            return err
         }
         handler.OnDelete(obj)
      }
   }
   return nil
}
  • sharedIndexInformer启动过程中会用indexer创建fifo队列,并将队列HandleDeltas赋值给controller的config

  • controllerprocessLoop消费来自Reflector数据事件,会触发sharedIndexInformerHandleDeltas方法调用

  • HandleDeltas方法会调用clientState以及handler处理数据,clientState实际上是indexerhandler实际上是sharedIndexInformer自身,这样对象变化时可以通过事件消费触发缓存indexer更新,以及通知sharedIndexInformer数据变化