kubernetes 事件反射器

mos管结电容等效模型_MOS管的防冲击防反接电路 MOS管的反接和抑制冲击一:原理分析MOS管由于生产工艺等因素,会产生相应的寄生电容,体二极管等。Mos管的寄生电容如图1所示,可以看到其具有三个寄生电容:G和S的电容Cgs;G和D的电容:Cgd,也称为反向传输电容、米勒电容、Crss;D和S的电容Cds。D和S之间的体二极管D1.图 1:MOS管等效模型寄生电容的数值可以从元器件的datasheet中得到,如图2,图3:Ciss(输入电容)=C... 阅读详情

kubernetes 事件反射器

Reflector 是 Kubernetes 的事件反射器,controller、Informer 使用它 List 和 Watch apiserver 中特定类型资源对象变化,并更新到 DeltaFIFO 和对象缓存中,这样客户端后续可以从本地缓存获取对象,而不需要每次都和 apiserver 交互。

在介绍 Relfector 前,先介绍 Reflector 使用的 ListerWatcher 接口。

ListWatcher 接口

ListWatcher 接口定义了 List 和 Watch 特定资源类型的方法:

  1. List(): 从 apiserver 获取一批特定类型的对象;
  2. Watch(): 从上面 List() 获取的 ResourceVersion 开始,通过 apiserver Watch etcd 中对象的变化;

ListWatcher 需要通过 RESTClient 的 Get 方法和 apiserver 通信,Getter 接口定义了该方法。

// 来源于 k8s.io/client-go/tools/cache/listwatch.go`
type ListerWatcher interface {
	// List() 返回对象列表;从这些对象中提取 ResourceVersion,用户后续的 Watch() 方法参数;
	List(options metav1.ListOptions) (runtime.Object, error)
	// Watch() 方法从指定的版本(options 中指定)开始 Watch。
	Watch(options metav1.ListOptions) (watch.Interface, error)
}

// ListFunc knows how to list resources
type ListFunc func(options metav1.ListOptions) (runtime.Object, error)

// WatchFunc knows how to watch resources
type WatchFunc func(options metav1.ListOptions) (watch.Interface, error)

// Getter interface knows how to access Get method from RESTClient.
type Getter interface {
	Get() *restclient.Request
}

实现 ListWatcher 接口的 ListWatch

ListWatch 类型实现了 ListWatcher 接口,被 NewReflector() 函数和各 K8S 内置资源对象的 Informer 创建函数(如 NewFilteredDeploymentInformer())使用:

// 来源于 k8s.io/client-go/tools/cache/listwatch.go
type ListWatch struct {
	ListFunc  ListFunc
	WatchFunc WatchFunc
	DisableChunking bool
}

函数 NewListWatchFromClient() 和 NewFilteredListWatchFromClient() 返回 ListWatch 对象。

传入的 Getter 参数是已经配置的 K8S 特定 API Group/Version 的 RESTClient,这里的“已经配置” 指创建 RESTClient 的 rest.Config 已经配置了 GroupVersion、APIPath、NegotiatedSerializer 等参数:

对于 K8S 内置对象,已经配置的 RESTClient 位于 k8s.io/client-go/kubernetes/<group>/<version>/<group>_client.go 文件中,如
ExtensionsV1beta1Client

对于自定义类型对象,已经配置的 RESTClient 位于 pkg/client/clientset/versioned/typed/<group>/<version>/<group>_client.go 文件中。

这个已配置的 RESTClient 适用于特定 API Group/Version 下的所有资源类型,在发送 Get() 请求时,还需要给Resource() 方法传入具体的资源类型名称,如 Resource("deployments"),这样才能唯一确定资源类型:/<APIPath>/<group>/<version>/namespaces/<resource>/<name>,如 /apis/apps/v1beta1/namespaces/default/deployment/my-nginx-111。

// 来源于 k8s.io/client-go/tools/cache/listwatch.go
func NewListWatchFromClient(c Getter, resource string, namespace string, fieldSelector fields.Selector) *ListWatch {
	optionsModifier := func(options *metav1.ListOptions) {
		options.FieldSelector = fieldSelector.String()
	}
	return NewFilteredListWatchFromClient(c, resource, namespace, optionsModifier)
}

// 调用 RESTClient 的 Get() 方法 List 和 Watch 对应的资源类型对象
func NewFilteredListWatchFromClient(c Getter, resource string, namespace string, optionsModifier func(options *metav1.ListOptions)) *ListWatch {
	listFunc := func(options metav1.ListOptions) (runtime.Object, error) {
		optionsModifier(&options)
		// c 是某一个 API Group/Version 的 RESTClient
		return c.Get().
			Namespace(namespace).
			Resource(resource). // 指定某一个资源类型名称,如 "deployments"
			VersionedParams(&options, metav1.ParameterCodec).
			Do().
			Get()
	}
	// 实际调用 WatchFunc 函数时,options 中包含上次 ListFunc 放回的对象列表的 ResourceVersion 值
	watchFunc := func(options metav1.ListOptions) (watch.Interface, error) {
		options.Watch = true
		optionsModifier(&options)
		return c.Get().
			Namespace(namespace).
			Resource(resource).
			VersionedParams(&options, metav1.ParameterCodec).
			Watch()
	}
	return &ListWatch{ListFunc: listFunc, WatchFunc: watchFunc}
}

ListWatch 的 List() 和 Watch() 方法是直接调用内部的 ListFunc() 或 WatchFunc() 函数来实现的,比较直接和简单,故不再分析。

使用 ListWatch 的 Informer

后文会介绍,各资源类型都有自己特定的 Informer(codegen 工具自动生成),如 DeploymentInformer,它们使用自己资源类型的 ClientSet 来初始化 ListWatch,只返回对应类型的对象:

// 来源于 k8s.io/client-go/informers/extensions/v1beta1/deployment.go
func NewFilteredDeploymentInformer(client kubernetes.Interface, namespace string, resyncPeriod time.Duration, indexers cache.Indexers, tweakListOptions internalinterfaces.TweakListOptionsFunc) cache.SharedIndexInformer {
	return cache.NewSharedIndexInformer(
		// 使用特定资源类型的 RESTClient 创建 ListWatch
		&cache.ListWatch{
			ListFunc: func(options v1.ListOptions) (runtime.Object, error) {
				if tweakListOptions != nil {
					tweakListOptions(&options)
				}
				return client.ExtensionsV1beta1().Deployments(namespace).List(options)
			},
			WatchFunc: func(options v1.ListOptions) (watch.Interface, error) {
				if tweakListOptions != nil {
					tweakListOptions(&options)
				}
				return client.ExtensionsV1beta1().Deployments(namespace).Watch(options)
			},
		},
		&extensionsv1beta1.Deployment{},
		resyncPeriod,
		indexers,
	)
}

Reflector

在解析完 ListWatch 垫后,我们终于可以开始解析 Reflector 的实现了!

Reflector 类型定义

Reflector 使用 ListerWatcher 从 apiserver 同步 expectedType 类型的对象及其事件,缓存到 store(DeltaFIFO 类型)中。

// 来源于:k8s.io/client-go/tools/cache/reflector.go
type Reflector struct {
	// Reflector 的名称,缺省值是 file:line
	name string
	
	// metrics tracks basic metric information about the reflector
	metrics *reflectorMetrics

	// 一个 Reflector 指定监控一种对象类型,该 field 指定对象类型
	expectedType reflect.Type
	
	// store 用于缓存对象变化事件,一般是 DeltaFIFO,可以参考 NewInformer/NewIndexerInformer 函数
	store Store

	// listerWatcher 用于 List 和 Watch 资源对象
	listerWatcher ListerWatcher

	// 当 ListAndWatch() 方法出错(超时)返回后,等待 period 时间后再次执行 ListAndWatch() 方法
	period       time.Duration

	// Reflector 周期调用 store 的 Resync() 方法,
	// 对于 DeltaFIFO 而言,是将 knownObjects 对象缓存中的所有对象同步到 DeltaFIFO 中
	resyncPeriod time.Duration
	
	// 在执行 resync 时额外的判断函数
	ShouldResync func() bool

	// clock allows tests to manipulate time
	clock clock.Clock
	
	// lastSyncResourceVersion is the resource version token last
	// observed when doing a sync with the underlying store
	// it is thread safe, but not synchronized with the underlying store
	lastSyncResourceVersion string
	
	// lastSyncResourceVersionMutex guards read/write access to lastSyncResourceVersion
	lastSyncResourceVersionMutex sync.RWMutex
}

创建 Reflector 对象的函数

函数 NewNamespaceKeyedIndexerAndReflector()、NewReflector()、NewNamedReflector() 返回 Reflector 对象,其中 NewNamedReflector() 是其它两个方法的基础:

// 来源于:k8s.io/client-go/tools/cache/reflector.go
var internalPackages = []string{"client-go/tools/cache/"}

func NewNamespaceKeyedIndexerAndReflector(lw ListerWatcher, expectedType interface{}, resyncPeriod time.Duration) (indexer Indexer, reflector *Reflector) {
	// 使用 Namespace 作为 IndexFunc 和 KeyFunc
	indexer = NewIndexer(MetaNamespaceKeyFunc, Indexers{"namespace": MetaNamespaceIndexFunc})
	reflector = NewReflector(lw, expectedType, indexer, resyncPeriod)
	return indexer, reflector
}

func NewReflector(lw ListerWatcher, expectedType interface{}, store Store, resyncPeriod time.Duration) *Reflector {
	// 使用 Reflector 所在的目录文件创建 Reflector 名称,格式为 file:linenum
	return NewNamedReflector(naming.GetNameFromCallsite(internalPackages...), lw, expectedType, store, resyncPeriod)
}

func NewNamedReflector(name string, lw ListerWatcher, expectedType interface{}, store Store, resyncPeriod time.Duration) *Reflector {
	reflectorSuffix := atomic.AddInt64(&reflectorDisambiguator, 1)
	r := &Reflector{
		name: name,
		// we need this to be unique per process (some names are still the same) but obvious who it belongs to
		metrics:       newReflectorMetrics(makeValidPrometheusMetricLabel(fmt.Sprintf("reflector_"+name+"_%d", reflectorSuffix))),
		listerWatcher: lw,
		store:         store,
		expectedType:  reflect.TypeOf(expectedType),
		period:        time.Second, // Run() 方法 wait.Until() 的重新执行时间间隔
		resyncPeriod:  resyncPeriod, // 周期调用 store 的 Resync() 方法的时间间隔
		clock:         &clock.RealClock{},
	}
	return r
}

Relectore 对象一般是 Informer 的 controller 创建的,例如(具体参考:4.controller-informer.md):

// 来源于:k8s.io/client-go/tools/cache/controller.go
func (c *controller) Run(stopCh <-chan struct{}) {
	...
	// 使用 controller 的 Config 参数创建 Reflector
	r := NewReflector(
		c.config.ListerWatcher,
		c.config.ObjectType,
		c.config.Queue, // DeltaFIFO
		c.config.FullResyncPeriod,
	)
	r.ShouldResync = c.config.ShouldResync
	r.clock = c.clock
	...
	// 运行 Reflector 的 Run 方法
	wg.StartWithChannel(stopCh, r.Run)
	...
}

Run() 方法

Run() 方法一直运行 ListAndWatch() 方法,如果出错,则等待 r.period 时间后再次执行 ListAndWatch() 方法方法,所以 Run() 方法是不会返回的,直到 stopCh 被关闭。

// 来源于:k8s.io/client-go/tools/cache/reflector.go
func (r *Reflector) Run(stopCh <-chan struct{}) {
	klog.V(3).Infof("Starting reflector %v (%s) from %s", r.expectedType, r.resyncPeriod, r.name)
	wait.Until(func() {
		if err := r.ListAndWatch(stopCh); err != nil {
			utilruntime.HandleError(err)
		}
	}, r.period, stopCh)
}

ListAndWatch() 方法

该方法是 Refelector 的核心方法,实现了:

  1. 从 apiserver List 资源类型的所有对象(ResourceVersion 为 0);
  2. 从对象列表中获取该类型对象的 resourceVersion;
  3. 调用内部 Store(DeltaFIFO)的 Replace() 方法,将 List 的对象更新到内部的 DeltaFIFO 中(生成 Sync 事件,或 DeletedFinalStateUnknown 类型的 Deleted 事件);
  4. 周期(resyncPeriod)调用 DeltaFIFO 的 Resycn() 方法,将 knownObjects 缓存中的对象同步到 DeltaFIFO 中(SYNC 事件),从而实现周期处理所有资源类型对象的功能;
  5. 从 List 获取的 resourceVersion 开始阻塞式 Watch apiserver,根据收到的事件类型更新 DeltaFIFO;
// 来源于:k8s.io/client-go/tools/cache/reflector.go

// Watch 的最小 timeout 时间,实际是 [minWatchTimeout, 2*minWatchTimeout] 之间的随机值
var minWatchTimeout = 5 * time.Minute

func (r *Reflector) ListAndWatch(stopCh <-chan struct{}) error {
	...
	// ResourceVersion: "0" 表示 etcd 中对象的当前值,这里列出 etcd 中当前该类型的所有对象
	options := metav1.ListOptions{ResourceVersion: "0"}
	...
	list, err := r.listerWatcher.List(options)
	...
	listMetaInterface, err := meta.ListAccessor(list)
	...
	// 从对象列表中获取该类型对象的 resourceVersion,后续 Watch 的时候使用
	resourceVersion = listMetaInterface.GetResourceVersion()
	...
	items, err := meta.ExtractList(list)
	...
	// 将 items 同步到 r.store 即 DeltaFIFO 中,使用的是 DeltaFIFO 的 Replace() 方法
	if err := r.syncWith(items, resourceVersion); err != nil {
		return fmt.Errorf("%s: Unable to sync list result: %v", r.name, err)
	}
	// 缓存 resourceVersion
	r.setLastSyncResourceVersion(resourceVersion)
	...
	
    go func() {
		resyncCh, cleanup := r.resyncChan()
		defer func() {
			cleanup() // Call the last one written into cleanup
		}()
		// 周期调用 r.store 的 Resync() 方法,实现将 knownObjects 对象缓存中的对象同步到 DeltaFIFO 中
		for {
			select {
			case <-resyncCh:
			case <-stopCh:
				return
			case <-cancelCh:
				return
			}
			if r.ShouldResync == nil || r.ShouldResync() {
				klog.V(4).Infof("%s: forcing resync", r.name)
				if err := r.store.Resync(); err != nil {
					resyncerrc <- err
					return
				}
			}
			cleanup()
			resyncCh, cleanup = r.resyncChan()
		}
	}()

    for {
        ...
        // Watch 会在 timeoutSeconds 后超时,这时 r.watchHandler() 出错,ListAndWatch() 方法出错返回
        // Reflecter 会等待 r.period 事件后重新执行 ListAndWatch() 方法;
        timeoutSeconds := int64(minWatchTimeout.Seconds() * (rand.Float64() + 1.0))
		options = metav1.ListOptions{
			ResourceVersion: resourceVersion,
			TimeoutSeconds: &timeoutSeconds,
		}
        w, err := r.listerWatcher.Watch(options)
		...
		// 阻塞处理 Watch 事件
        if err := r.watchHandler(w, &resourceVersion, resyncerrc, stopCh); err != nil {
			if err != errorStopRequested {
				klog.Warningf("%s: watch of %v ended with: %v", r.name, r.expectedType, err)
			}
			return nil
		}
    }
}

// 用 items 中的对象替换 r.store
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)
}

// 根据 Watch 到的事件类型,调用 r.store 的方法,将对象更新到 r.store 中
func (r *Reflector) watchHandler(w watch.Interface, resourceVersion *string, errc chan error, stopCh <-chan struct{}) error {
...
loop:
	for {
		select {
        ...
		case event, ok := <-w.ResultChan():
            ...
			newResourceVersion := meta.GetResourceVersion()
			switch event.Type {
			case watch.Added:
				err := r.store.Add(event.Object)
				if err != nil {
					utilruntime.HandleError(fmt.Errorf("%s: unable to add watch event object (%#v) to store: %v", r.name, event.Object, err))
				}
			case watch.Modified:
				err := r.store.Update(event.Object)
				if err != nil {
					utilruntime.HandleError(fmt.Errorf("%s: unable to update watch event object (%#v) to store: %v", r.name, event.Object, err))
				}
			case watch.Deleted:
				err := r.store.Delete(event.Object)
				if err != nil {
					utilruntime.HandleError(fmt.Errorf("%s: unable to delete watch event object (%#v) from store: %v", r.name, event.Object, err))
				}
			default:
				utilruntime.HandleError(fmt.Errorf("%s: unable to understand watch event %#v", r.name, event))
			}
			*resourceVersion = newResourceVersion
			r.setLastSyncResourceVersion(newResourceVersion)
			eventCount++
		}
	}
    ...
}

使用 Reflector 的 controller

Controller 封装了 Reflector,Reflector 使用 ListerWatcher 从 apiserver 获取对象列表和事件,存到 DeltaFIFO 中,Controller 持续弹出 Reflector 的 DeltaFIFO,用弹出的 Delta 更新它的 ClientState 缓存,并调用 Informer 设置的 OnAdd/OnUpdate/OnDelete 回调函数。

// 来源于:k8s.io/client-go/tools/cache/controller.go
type Config struct {
    // 缓存 ObjectType 类型对象的队列,同时也被 Reflector 使用。
    // 后续 NewInformer、NewIndexerInformer 创建该配置时,实际创建的是 DeltaFIFO 类型的 Queue。
	Queue

    // Controller 为 ObjectType 创建 Reflector 使用的 ListerWatcher;
	ListerWatcher

    // 对于对象的每个事件,调用该处理函数
	Process ProcessFunc

    // 该 Controller 关注和管理的对象类型
	ObjectType runtime.Object

	// 周期调用 Queue 的 Resync() 方法的周期
	FullResyncPeriod time.Duration

	// 而外判断是否需要 Resync() 的函数(一般是 nil)
	ShouldResync ShouldResyncFunc

	// 如果 Process 处理弹出的对象失败,是否将对象重新加到 Queue 中(一般是 false)
	RetryOnError bool
}

// controller 是 Controller 的实现,但是没有创建它的 New 方法
// 所以 controller 实际是由 NewInformer、NewIndexerInformer 来创建和使用的
type controller struct {
	config         Config
	reflector      *Reflector
	reflectorMutex sync.RWMutex
	clock          clock.Clock
}

Run() 方法

func (c *controller) Run(stopCh <-chan struct{}) {
	defer utilruntime.HandleCrash()
	go func() {
		<-stopCh
        // controller 运行结束时关闭队列
		c.config.Queue.Close()
	}()

    // 根据 Controller 的配置,初始化监控 ObjectType 类型对象的 Reflector
    // 初始化 Refector 时,传入了 Controller 的 Queue(DeltaFIFO),所以 Refector 会同步更新该 Queue;
	r := NewReflector(
		c.config.ListerWatcher,
		c.config.ObjectType,
		c.config.Queue,
		c.config.FullResyncPeriod,
	)
	r.ShouldResync = c.config.ShouldResync
	r.clock = c.clock

	c.reflectorMutex.Lock()
	c.reflector = r
	c.reflectorMutex.Unlock()

	var wg wait.Group
	defer wg.Wait()

    // 在另一个 goroutine 里启动 Reflector
	wg.StartWithChannel(stopCh, r.Run)

	// 阻塞执行 c.processLoop,该 Loop 是个死循环,只是在出错时才返回值,然后等待 1s 后重新执行
	wait.Until(c.processLoop, time.Second, stopCh)
}

processLoop() 方法

从 DeltaFIFO 中弹出 Deltas 事件,然后调用配置的 PopProcessFunc 函数,该函数:

  1. 使用弹出的对象更新 DeltaFIFO 使用的 knownObjests 对象缓存(controller 创建的 clientState);
  2. 调用用户注册的回调函数;

DelteFIFO 的 Pop() 方法是在加锁的情况下执行 PopProcessFunc 的,所以即使多个 goroutine 并发调用 Pop() 方法,它们也是串行执行的,所以不会出现多个 goroutine 同时处理一个资源对象的情况。

func (c *controller) processLoop() {
	for {
		obj, err := c.config.Queue.Pop(PopProcessFunc(c.config.Process))
		if err != nil {
			if err == FIFOClosedError {
				return
			}
			if c.config.RetryOnError {
				// This is the safe way to re-enqueue.
				c.config.Queue.AddIfNotPresent(obj)
			}
		}
	}
}

c.config.RetryOnError 一般为 false(参考后续的 NewInformer() 函数),所以当 PopProcessFunc 执行出错时并不会将对象添加到 DeltaFIFO 中。

使用 controller 的 Informer

NewInformer()、NewIndexInformer() 函数使用 controller 来 List/Watch 特定资源类型的对象,缓存到本地,并调用用户提供的回调函数(保存在 ResourceEventHandler 中)。

// 来源于:k8s.io/client-go/tools/cache/controller.go
func NewInformer(
	lw ListerWatcher,
	objType runtime.Object,
	resyncPeriod time.Duration,
	h ResourceEventHandler,
) (Store, Controller) {
    // Store 是一个存储对象的内存数据库,它使用 KeyFunc 函数获取对象的唯一访问 key。
    // NewStore 实际返回的是一个 ThreadSafe 类型
	clientState := NewStore(DeletionHandlingMetaNamespaceKeyFunc)
	fifo := NewDeltaFIFO(MetaNamespaceKeyFunc, clientState)

	cfg := &Config{
		Queue:            fifo,
		ListerWatcher:    lw,
		ObjectType:       objType,
		FullResyncPeriod: resyncPeriod,
		// 不将执行失败 Process 失败的对象放回 Queue
		RetryOnError:     false,

		// DeltaFIFO 的 Pop() 方法是在内部加锁的情况下执行 Process 函数,所以对 clientState 的更新和调用 OnUpdate/OnAdd/OnDelted 处理函数是串行的。
		Process: func(obj interface{}) error {
			// from oldest to newest
			for _, d := range obj.(Deltas) {
				switch d.Type {
				case Sync, Added, Updated:
					// 根据 clientState 中是否有该对象决定调用 OnUpdate() 还是 OnAdd() 处理函数
					// 所以,Controller 刚启动时,由于 clientState 为空,会为所有 list 到的对象调用 OnAdd() 处理函数;
					if old, exists, err := clientState.Get(d.Object); err == nil && exists {
						if err := clientState.Update(d.Object); err != nil {
							return err
						}
						h.OnUpdate(old, d.Object)
					} else {
						if err := clientState.Add(d.Object); err != nil {
							return err
						}
						h.OnAdd(d.Object)
					}
				case Deleted:
					// 删除的时候,是先将对象从 clientState 中删除,再调用用户处理函数
					if err := clientState.Delete(d.Object); err != nil {
						return err
					}
					// d.Object 可能是原生资源类型对象,也可能是 DeletedFinalStateUnknown 类型对象,所以 OnDeleted()函数需要能区分和处理
					h.OnDelete(d.Object)
				}
			}
			return nil
		},
	}
	return clientState, New(cfg)
}

HasSynced() 方法

调用 DeltaFIFO 的 HasSynced() 方法。

当 processLoop() 弹出 DeltaFIFO 中第一批 Reflector List 到的对象并处理结束后,该方法返回 true,而且后续一直返回 true;

func (c *controller) HasSynced() bool {
	return c.config.Queue.HasSynced()
}

sharedInformer/sharedIndexInformer 的 HasSynced() 方法实际是调用 controller 的 HasSynced() 方法,而且该方法的签名与 InformerSynced 函数类型一致:

// 来源:k8s.io/client-go/tools/cache/shared_informer.go
func (s *sharedIndexInformer) HasSynced() bool {
	s.startedLock.Lock()
	defer s.startedLock.Unlock()

	if s.controller == nil {
		return false
	}
	return s.controller.HasSynced()
}

type InformerSynced func() bool

自定义 Controller 使用 HasSynced() 方法的场景

在开发 K8S Controller 的时候,一个惯例是调用 cache.WaitForCacheSync,等待所有 Informer Cache 都同步后才启动消费 workqueue 的 worker:

// 来源于:https://github.com/kubernetes/sample-controller/blob/master/controller.go

// 自定义的 Controller
type Controller struct {
	...
	deploymentsLister appslisters.DeploymentLister
	deploymentsSynced cache.InformerSynced  // InformerSynced 函数类型
	...
}

func NewController(
	kubeclientset kubernetes.Interface,
	sampleclientset clientset.Interface,
	deploymentInformer appsinformers.DeploymentInformer,
	fooInformer informers.FooInformer) *Controller {
		...
		controller := &Controller{
			kubeclientset:     kubeclientset,
			sampleclientset:   sampleclientset,
			deploymentsLister: deploymentInformer.Lister(),
			deploymentsSynced: deploymentInformer.Informer().HasSynced, // Infomer 的 HasSynced() 方法
		}
		...
}

// 等待所有类型的 Informer 的 HasSynced() 方法返回为 true 时再启动 workers
func (c *Controller) Run(threadiness int, stopCh <-chan struct{}) error {
	...
	// Wait for the caches to be synced before starting workers
	klog.Info("Waiting for informer caches to sync")
	if ok := cache.WaitForCacheSync(stopCh, c.deploymentsSynced, c.foosSynced); !ok {
		return fmt.Errorf("failed to wait for caches to sync")
	}

	klog.Info("Starting workers")
	// Launch two workers to process Foo resources
	for i := 0; i < threadiness; i++ {
		go wait.Until(c.runWorker, time.Second, stopCh)
	}
	...
}

为何要等各 informer 的 HasSynced() 返回为 true 时才开始启动 worker 呢?

因为 HasSynced() 返回 true 时表明 Reflecter List 的第一批对象都从 DeltaFIFO 弹出,并由 controller 更新到 clientState 缓存中,这样 worker 才能通过通过对象名称(Key)从 Lister Get 到对象。否则对象可能还在 DeltaFIFO 中且没有同步到 clientState 缓存中,这样 worker 通过对象名称从 Lister 中 Get 不到对象。

银行客户投诉预测模型优化 在实际操作中,银行可以使用这个优化后的模型来识别那些可能引起客户不满的因素,并采取预防措施。例如,如果模型表明退款金额是一个很强的预测因子,银行可能会审查其退款政策,以确保它对客户是公平和透明的。在银行业务中,客户投诉是一个重要的指标,它直接关系到客户满意度和银行服务质量。某一个指标,比如:退款金额,本文假设退款金额对于预测投诉的权重应该高于赔偿金额。在每次迭代中,本文使用当前的权重组合来训练模型,并计算在测试集上的准确率。的可能值来搜索最佳的权重组合,并输出最佳准确率及对应的权重。来寻找最佳的权重组合。 阅读详情

相关推荐

STM32 Modbus-RTU从零到实战:手把手教你用HAL库实现工业通信(附完整代码)

本文详细介绍了如何使用STM32 HAL库从零开始实现Modbus-RTU从机通信。内容涵盖RS485硬件设计、CubeMX串口与定时器配置、基于空闲中断的高效数据接收、协议栈状态机设计、CRC16校验实现以及常用功能码处理。文章提供了完整的代码框架和调试方法,帮助开发者快速构建稳定的工业通信节点。

weixin_28278377的博客 185

跟着chatGPT学习:kubernetes中的Reflector、list-watcher、informer等概念

以下是我跟chatGPT学习kubernetes中Reflector、list-watcher、informer等的概念的过程 不敢保证chatGPT回答的百分之百准确。但是,确实帮助我了我理解!

码二哥的技术池 1365

GPU架构-SIMT

本文深度剖析了GPU的SIMT架构,从硬件实现层面解析其与SIMD的本质差异。SIMT通过将32个线程捆绑为warp执行,在标量编程模型下实现数据并行。文章对比了SIMD和SIMT的执行模型、分支处理机制及存储层次,指出SIMT是对SIMD的虚拟化抽象。重点分析了Volta架构引入的独立线程调度技术,以及访存合并对性能的关键影响。通过定量数据展示了各代GPU架构的寄存器文件规模和线程调度机制,为开发者优化GPU程序提供了硬件层面的理论基础。

技术引领业务创新 971

Kubernetes Reflector 使用指南

Kubernetes Reflector 是一个开源工具,旨在解决 Kubernetes 集群中资源同步的问题。它通过监听特定的 Kubernetes 资源变动,并自动地将这些变更反映到其他命名空间或集群中,从而实现资源的一致性管理。这对于那些需要在多环境或多集群间保持配置一致性的场景尤为重要。Emberstack 开发并维护此项目,以简化微服务架构下的资源管理流程。 ## 项目快速启动 ##...

gitblog_00148的博客 735

Android13 SurfaceFlinger启动流程分析

在Android13版本中,SurfaceFlinger是由Android.bp去启动init.rc文件,然后再解析文件去加载SurfaceFlinger。上面方法主要处理如下:1、调用surfaceflinger的createSurfaceFlinger方法,实例化surfaceflinger。2、调用flinger(surfaceflinger)的init()方法,在客户端连接之前进行初始化。

liuning1985622的博客 1918

论文理解【LLM-OR】——【OptiMUS】Scalable Optimization Modeling with (MI)LP Solvers and Large Language Models

OptiMUS 把“自然语言→优化建模与求解”从单次生成改造成“结构化问题 + 多智能体协作 + 连接图检索”的模块化流程,并在更长更难的数据集上验证了这种结构化方法相对端到端 prompting 的优

佚失的诗篇 821

从手动同步到自动化:kubernetes-reflector如何提升Kubernetes资源管理效率

在Kubernetes集群管理中,手动同步Secrets和ConfigMaps等资源不仅耗时费力,还容易因人为操作失误导致配置不一致。**kubernetes-reflector**作为一款强大的Kubernetes控制器,通过自动化资源镜像功能,彻底解决了跨命名空间资源同步的难题,让开发者和运维人员从繁琐的手动操作中解放出来。 ## 🚀 为什么需要kubernetes-reflector?

gitblog_01006的博客 722

【Python爬虫】股票信息定向爬取

【Python爬虫】股票信息定向爬取:通过requests-bs4-re技术路线爬虫获取上交所和深交所所有股票的名称和交易信息

qq_39321802的博客 1499

[k8s源码]6.reflector

而 Informer 是一个更高级别的抽象,它内部使用了 Reflector,但提供了更全面的功能。然后下面是测试部分,首先为fw赋值为watcher,一开始为nil,而在上面r=NewReflector初始化的时候,就已经自动调用了watchFunc,创建了一个watcher。sendingRV随机生成一个资源的版本,随着从ids拿出来的值,作为pod的属性用来创建pod,在测试中,这里的fw是闭包里面创建的watcher,并不和外面的fw冲突,如果不理解,这里的fw也可以改为别的名字。

weixin_45396500的博客 912

力软敏捷开发框架源码7.0.6旗舰版 JNPF开发平台3.4.5 企业版 旗舰版

JNPF作为作为一款深受用户喜爱的低代码产品,凭借雄厚的科研技术力量、丰富的软件开发项目经验、严谨的项目开发流程和高质量的售后服务,已成功帮助千家企业完成数字化项目,建立了覆盖不同行业客户和不同发展阶段的软件产品服务体系。

huoooos0的博客 1838

2026年国赛C题微网与外部电网电力调控策略 (三):问题三、问题四滚动预测、实时电价与结果核验

专栏内定期更新ABCDE五题详细思路、代码(含运行结果图)和讲解视频-(11号之前半价):问题三看起来只是多给了一个光伏预报附件,实质上它改变了整道题的信息结构:问题四则是在问题二、问题三之上替换价格来源。问题4-2相当于“确定性计划+实时电价”,问题4-3相当于“滚动预测+实时电价”。物理模型完全不变,改变的是价格序列和费用口径。本文先给出核心结果:从结果看,问题3比问题2多约164.21万元,问题4-3比问题4-2多约168.79万元。主要增量不是“储能没有工作”,而是预测误差、计划偏差和紧急购电造成的

YOLO 343

k8s client-go源码分析 informer源码分析(3)-Reflector源码分析

k8s informers实现了持续获取集群的所有资源对象、监听集群的资源对象变化功能,并在本地维护了全量资源对象的内存缓存,以减少对apiserver、对etcd的请求压力。Informers在启动的时候会首先在客户端调用List接口来获取全量的对象集合,然后通过Watch接口来获取增量的对象,然后更新本地缓存。

良凯尔的博客 1009

MobileViT替换YOLOv11主干网络:轻量化新思路-(用MobileViT等轻量视觉Transformer降低计算成本)

在计算机视觉领域,目标检测一直是核心任务之一,而YOLO(You Only Look Once)系列算法因其出色的速度-精度平衡成为工业界和学术界的热门选择。随着YOLOv11的推出,模型性能达到了新的高度,但其计算复杂度和参数量也随之增加,这对资源受限的移动设备和嵌入式系统提出了挑战。与此同时,视觉Transformer(ViT)的出现为计算机视觉带来了全新范式,但其庞大的计算需求限制了在移动端的应用。MobileViT作为一种创新的轻量级视觉Transformer架构,巧妙结合了CNN的归纳偏置优势和V

本博客聚焦 YOLOv11 全流程落地,涵盖架构优化、数据集处理、训练技巧与多场景部署,兼及国产数据库、Java 开发与 AI 模型应用,内容兼顾理论与工程实践,为开发者提供系统干货,助力高效提升技术能力。 1646

Kubernetes 1.23 到 1.24:一次让 Reddit 宕机超 5 小时的升级踩坑

它究竟是如何崩溃的?这真是最出乎意料的事情之一。在研究过程中,我们发现路由反射器的配置方式是将控制平面节点设置为反射器,并让所有其他节点都使用这些反射器。这相当直接,而且在自动扩缩容集群中,控制平面节点是唯一始终可用的节点,这样做也合乎逻辑。然而,这种配置方式存在一个隐蔽的缺陷。请看下面的代码,看看你能不能发现它。提示一下:我们当时升级到的版本是 k8s 1.24。

AymB1cByo的博客 381

假人有限元模型

假人模型,车辆碰撞假人有限元模型,适合做乘员安全的研究者利用

锂电池充电器电路设计 里面有电路

锂电池充电器电路设计 里面有电路 3.7V升压到5V电路设计。

k8s 源码client-go 系列 sharedProcessor

run() 和 pop() 是 processorListener 的两个最核心的函数,processorListener 就是实现了事件的缓冲和处理,在没有事件的时候可以阻塞处理器,当事件较多是可以把事件缓冲起来,实现了事件分发器与处理器的异步处理。收到变更的事件后, 遍历通知给所有的 listener 监听器, 这里的通知是把事件写到 listener 的 addCh 通道.有些人可能理解不了pop的两种case,我这里和群里的兄弟们讨论了下,得出结果。简化的实现原理架构图.本文由博客一文多发平台。

chaoyueshijian的博客 1100

数学建模模型总结

这是本人在两年内准备参加数学建模竞赛搜集到的模型资料,已经经过了本人的筛选和整理,希望能够对准备参加数学建模竞赛的学弟学妹们提供一些帮助!

一次让 Reddit 宕机超 5 小时的升级踩坑

Reddit遭遇314分钟大宕机,根源竟是Kubernetes升级过程中的路由反射器配置问题。团队在将Kubernetes从1.23升级到1.24版本时,由于一个历史遗留的Calico路由反射器配置(使用已弃用的master标签)失效,导致整个网络瘫痪。此次故障暴露了基础设施管理中的深层问题:集群配置不一致、文档缺失以及备份恢复流程的不足。尽管团队最终通过高风险的回滚操作恢复了服务,但这次事故促使Reddit决心彻底重构基础设施,推动标准化和自动化建设,以提升系统可靠性。这次PiDay宕机事件成为公司基础设

xdpcxq的专栏 913

老版QQ原生表情gif动态

这是老版的QQ表情,不过全是动态的的,Windows照片查看器是不会动的,可以右击用浏览器打开查看,再说一遍是老版的,只是分享给宁愿用老版也要动态的人。

听 GPT 讲 client-go 源代码 (15)

在client-go的项目中,resourceclaimconsumerreference.go文件的作用是定义了资源声明消费者的引用对象。在client-go/kubernetes/typed/apiserverinternal/v1alpha1/fake/fake_apiserverinternal_client.go文件中定义了一个虚假(fake)的 API 客户端,用于模拟与 apiextensions.k8s.io/v1alpha1/apiserverinternal 资源的交互。

techdashen的博客 727

kubernetes网络(三)之bird的路由反射器的使用

上一篇文章中我们用 bird 程序实现了三台服务器之间的BGP full mesh。本文我们将full mesh 方式改为路由反射器方式 ,让宿主相互学习到对方的容器网段,从而达到容器网段能相互通信的目的。

鲜少伟的博客 1534

Kubernetes网络插件Calico深度解析:组件架构、网络模型与BGP路由反射器实战

作为Kubernetes生态中最强大的之一,Calico凭借其高性能、灵活的策略控制及对标准协议的支持,成为企业级容器网络的首选。本文将深入剖析Calico的组件架构、核心网络模型原理,并通过的配置实战,带你全面掌握Calico的底层机制与高级应用技巧。

2401_85773741的博客 1096

# Kubernetes(K8s)笔记Day15:K8s网络工作原理【Pod 网络模型,CNI 网络插件,Calico 工作模式详解,BGP协议,路由反射器】

BGP(Border Gateway Protocol,边界网关协议)是一个 Linux 内核原生支持的、专门用在大规模数据中心里维护不同自治系统(AS)之间路由信息的、无中心的路由协议。BGP 的核心作用,是 让不同网络之间互相筛选并通告最佳路径。自治系统(AS):指一个组织管辖下的所有 IP 网络和路由器的全体。例如,一个大型企业、一家云服务商,或者整个 Kubernetes 集群,都可以被视为一个自治系统。特性性能★★★★★★★★★★★★★★★封装开销无有无有网络策略✅ 支持。

weixin_46136019的博客 612

ip地址 k8s 显示pod_访问k8s内部pod service网络

kafa部署在k8s中,并且使用statefulset 方式部署,应用pod连接kafka 使用kafka-0.kafka-hs.sy-platform-demo.svc.cluster.local.:9093, 如果k8s 外部开发测试,无法连接,所以需要外部网络与pod service网络打通#kafka注册到zk中[zk: localhost:2181(CONNECTED) 5] get...

weixin_29156679的博客 797

Calico BGP Route Reflectors 路由反射器使用方式

而使用路由反射器(RR)时,同一自治系统(AS)内的路由器只需与 RR 建立邻居关系即可。RR 将路由信息从一台 iBGP 路由器 "反射" 给其他的 iBGP 邻居。通过这种方式,就不需要构建 iBGP 全互联(full-mesh)拓扑了。这是一种 服务器-客户端 解决方案:其中 RR 充当服务器,其余所有 iBGP 路由器作为客户端。这种方案简化了配置,减少了 iBGP 建立邻居关系的数量,同时也节省了 CPU 和网络资源。

GXa7aY4zZ的博客 241

calico RR路由反射器

配置calicoctl 在官网下载calicoctl二进制文件,并给予可执行权限 curl -O -L https://github.com/projectcalico/calicoctl/releases/download/v3.13.2/calicoctl 在master节点上配置

XXX的博客 703

Kubernetes Informer 机制全解析教程

在 Kubernetes(k8s)中,Informer 是一种极为关键的机制,用于缓存和高效检索资源对象信息,它能够有效减少对 API Server 的查询压力,并实现事件驱动的编程模型,使得客户端应用可以及时响应集群中资源的变化。通过以上对 Informer 机制核心源码的解析,我们可以深入理解其在 Kubernetes 集群中如何高效地管理资源对象信息,以及如何实现与客户端应用的有效交互,为开发基于 Kubernetes 的应用提供坚实的基础。一、Informer 概述。(三)事件处理及回调相关。

进击的Blazor 1319

kube-rs:用Rust构建Kubernetes应用的强大工具

在现代软件开发中,Kubernetes已成为容器编排的事实标准。然而,使用传统的Go语言客户端(如client-go)来开发Kubernetes应用可能会遇到一些挑战,特别是在性能和开发效率方面。现在,有了`kube-rs`,这一切都变得更加简单和高效。`kube-rs`是一个用Rust编写的Kubernetes客户端,它不仅提供了高性能和安全性,还带来了Rust语言的强大特性。 ## 项目介绍...

gitblog_00051的博客 1281
上一篇: Kubernetes 事件队列
下一篇: Informer
KingDragon_Baron
博客等级 码龄12年 55粉丝 73原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值