list-watch机制
list-watch
有两部分组成,分别是list
和watch
。list
非常好理解,就是调用资源的list API
罗列资源,基于HTTP
短链接实现;
watch则是调用资源的
watch API监听资源变更事件,基于
HTTP 长链接实现
Watch API
和apiserver
保持一个长链接
,接收资源的状态变更事件
并做相应处理。如果仅调用watch API
,若某个时间点连接中断,就有可能导致消息丢失,所以需要通过list API
和
resourceversion
(根据版本)
解决消息丢失
的问题。
watch失败后会自动调用list,list返回全量数据,每次watch失败都会relist。在大规模场景,如果所有client同时发生relist,那server肯定受不了。为了应对这种情况,提供了EtcdResync
;
List-watch的问题:
watch请求时http streaming方式,当服务端挂了后,客户端是感知不到网络断开,以为服务端没有数据,所以客户端不会主动关闭长连接。
处理:
1.Watch请求需要 带一个超时参数(默认是5-10min之间的随机数),时间一到,断开连接。
2.kube-proxy增加连接超时的参数
workqueue机制
client-go中抽象了几种队列,包括通用队列、限速队列、延时队列等
通用队列:防止两个相同类型的item同时处理
增加dirty set,当item正在处理,又来一个同类的item,为了保证不能同时有两个相同的item
正在被处理,将后面的item加入dirty,等前面的item执行完,才将后面的item加入queue。
限速队列:防止短时间内处理失败的item再次处理,陷入循环
增加AddAfter
方法, 可以允许用户告诉DelayingInterface
过多长时间把该item
加入队列中
延时队列:
根据元素错误次数逐渐累加等待时间,然后加入到延时队列
workqueue 提供的一个保障就是,如果是同一个object,比如同一个 pod,被多次加到 workqueue 里,在 dequeue 时,它只会出现一次。防止会有同一个 object 被多个 worker 同时处理。
普通队列workqueue:
防止 hot loop:它保证了一个 item 被 reenqueued 后,不会马上被处理
限速队列RateLimitingQueue,它相比普通的 workqueue 多了以下的功能:
限流:可以限制一个 item 被 reenqueued 的次数。(维护了一个delaying_queue
,并用
heap
将等待时间最小的
item
加入到
workqueue去处理)
防止 hot loop:它保证了一个 item 被 reenqueued 后,不会马上被处理,(processing
存在正在处理的
item)
延时队列RateLimiter:
根据元素错误次数逐渐累加等待时间,然后加入到延时队列
informer核心
0.kube-apiserver通过etcd自身的watch机制获取变化的资源,并将资源分类存入watchCache中,封装成pod/service Storage
0.1.客户端通过http长连接监听kube-apiserver的pod资源的变化,即从pod storage的缓存中获取kube-apiserver从etcd watch到的数据
1.ListWatch监听apiserver的资源变化,Lister一般用于首次获取某资源(如Pod)的全量信息,而Watcher用于持续获取该资源的增量变化信息
2.reflector使用listerWatcher获取资源,并将其保存在队列DeltaFIFO
3.DeltaFIFO是一个生产者-消费者队列,生产者为Reflector,消费者为Pop()函数,消费的数据一方面存储到Indexer中,另一方面可以通过informer的handler进行处理
4.Local Store是本地缓存,Indexer是含有索引能力的本地缓存,本质是能根据id、名字等key获取对象的map
5.workqueue用于保存DeltaFIFO中增量变化的对象,由worker将当前状态的对象处理成期望状态的对象。
*注:SharedInformer.Run启动了两个chan,s.c.Run为controller的入口,s.c.Run函数中会Pop DeltaFIFO中的元素,并根据DeltaFIFO的元素的类型(Sync/Added/Updated/Deleted)进两类处理,一类会使用indexer.Update,indexer,Add,indexer.Delete对保存的在Store中的数据进行处理;另一类会根据DeltaFIFO的元素的类型将其封装为sharedInformer内部类型updateNotification,addNotification,deleteNotification,传递给s.processor.Listeners.addCh,后续给注册的pl.handler处理。
informer工作流程
1.reflector通过ListWatch的list获取资源的全量信息,包括监听对象最新的resourceVersion。将获取的资源信息存入indexer
2.reflector通过ListWatch的watch监听对象resourceVersion之后的所有变化,并将变化存入DeltaFIFO。
3.Informer的controller不断地pop DeltaFIFO的变化,根据变化更新indexer对应的数据,根据变化调用回调函数handler去处理,即将变化的对象放入workqueue
4.controller不断地从workqueue取出对象,通过sync方法将对象从当前状态处理成期望状态。
使用informer创建controller
1.与kube-apiserver建立连接client
2.创建sharedInformerFactory对象informer,并关联要监听的资源
3.定义回调函数并关联到informer
4.启动informer,并等待资源信息同步到本地缓存
5.informer监听变更事件,并将事件存入workqueue,
6.controller从workqueue去除object,并通过调用sync方法将对象从当前状态处理成期望状态。
Informer 在使用时需要先初始化一个 InformerFactory,目前主要推荐使用的是 SharedInformerFactory,Shared 指的是在多个 Informer 中共享一个本地 cache,即共同使用一个SharedIndexer。
建议使用 RateLimitingQueue,它相比普通的 workqueue 多了以下的功能:
参考:https://www.jianshu.com/p/1e2e686fe363