目录
直奔主题
FIFO和delta_FIFO抽象
FIFO实现
结构及初始化(调度器用到这个队列)
重要方法介绍
DELTA_FIFO实现
其他几个类型说明
结构及初始化
重要方法介绍
DeltaFIFO创建对象键函数
基础方法
总结
参考
// Queue is exactly like a Store, but has a Pop() method too.
type Queue interface {
Store // 实现存储接口
// 先入先出,从队列中弹出对象。
// pod阻塞 直到队列中有元素处理,返回处理对象及处理结果
// return an ErrRequeue 在释放队列锁之前 元素重新入队列
Pop(PopProcessFunc) (interface{}, error)
AddIfNotPresent(interface{}) error // 对象不在队列中就添加
HasSynced() bool // 通过Replace()放入第一批对象到队列中并且已经被Pop()全部取走
Close() // 关闭队列
}
Queue是在Store基础上扩展了Pop接口可以让对象有序的弹出,Indexer是在Store基础上建立了索引,可以快速检索对象。
FIFO接收来Reflector的添加和更新事件,并将它们按照顺序放入队列。元素在队列中被处理之前,如果有多个adds/updates事件发生,仅仅只会被处理一次。后续才会处理最新版本。没有放入chan中进行同步。
使用场景:1. 处理对象仅一次 2. 处理完当前后才能处理最新版本对象 3. 删除对象之后不会处理
4. 不能周期性重新处理对象
type FIFO struct {
lock sync.RWMutex
cond sync.Cond // 条件变量,唤醒等待的协程
items map[string]interface{} // map存储 对象键 和 对象,方便查找
queue []string // 队列存储 对象键
// true 通过Replace() 第一批元素被插入队列 或者 Delete/Add/Update 首次被调用
populated bool
// Replace()被首次调用 插入的元素数目
initialPopulationCount int
keyFunc KeyFunc // 函数 计算元素 key值
// 队列是否被关闭 关闭互斥锁
closed bool
closedLock sync.Mutex
}
// 初始化
// NewFIFO returns a Store which can be used to queue up items to
// process.
func NewFIFO(keyFunc KeyFunc) *FIFO {
f := &FIFO{
items: map[string]interface{}{},
queue: []string{},
keyFunc: keyFunc,
}
f.cond.L = &f.lock
return f
}
// Add inserts an item, and puts it in the queue. The item is only enqueued
// if it doesn't already exist in the set.
func (f *FIFO) Add(obj interface{}) error {
id, err := f.keyFunc(obj) // 对象键
if err != nil {
return KeyError{obj, err}
}
// 加锁
f.lock.Lock()
defer f.lock.Unlock()
f.populated = true
// 对象键 不存在 入队列
if _, exists := f.items[id]; !exists {
f.queue = append(f.queue, id)
}
// 存在 更新 obj
f.items[id] = obj
f.cond.Broadcast() // 唤醒wait
return nil
}
// Delete removes an item. It doesn't add it to the queue, because
// this implementation assumes the consumer only cares about the objects,
// not the order in which they were created/added.
func (f *FIFO) Delete(obj interface{}) error {
id, err := f.keyFunc(obj)
if err != nil {
return KeyError{obj, err}
}
f.lock.Lock()
defer f.lock.Unlock()
f.populated = true
// 删除时 仅删除map存储的元素
delete(f.items, id)
return err
}
// pod队列的元素 进行处理
func (f *FIFO) Pop(process PopProcessFunc) (interface{}, error) {
f.lock.Lock()
defer f.lock.Unlock()
for {
// 队列为空 等待
for len(f.queue) == 0 {
// 队列为空,pod调用被阻塞直到新元素被添加唤醒
// close函数调用 关闭队列,并唤醒wait
// 循环继续 或者 返回
if f.IsClosed() {
return nil, FIFOClosedError
}
f.cond.Wait()
}
id := f.queue[0]
f.queue = f.queue[1:]
// 根据这个判断首次添加元素的是否同步完
if f.initialPopulationCount > 0 {
f.initialPopulationCount--
}
// 取元素,元素可能被删除 不会联动队列 删除切片时间复杂度比较高
item, ok := f.items[id]
if !ok {
// Item may have been deleted subsequently.
continue
}
delete(f.items, id)
// 处理元素
err := process(item)
if e, ok := err.(ErrRequeue); ok {
// 处理失败之后 重新入队列 和 map items
f.addIfNotPresent(id, item)
err = e.Err
}
// 返回元素及错误
return item, err
}
}
// Replace 删除FIFO内容,并用 list 进行替换
func (f *FIFO) Replace(list []interface{}, resourceVersion string) error {
items := make(map[string]interface{}, len(list))
for _, item := range list {
key, err := f.keyFunc(item)
if err != nil {
return KeyError{item, err}
}
items[key] = item
}
f.lock.Lock()
defer f.lock.Unlock()
if !f.populated {
f.populated = true
f.initialPopulationCount = len(items)
}
f.items = items
f.queue = f.queue[:0]
for id := range items {
f.queue = append(f.queue, id)
}
if len(f.queue) > 0 {
f.cond.Broadcast()
}
return nil
}
// Resync 重新同步对象键 同步的是 item中的对象键 put them into the processing queue
func (f *FIFO) Resync() error {
f.lock.Lock()
defer f.lock.Unlock()
inQueue := sets.NewString()
for _, id := range f.queue {
inQueue.Insert(id)
}
for id := range f.items {
if !inQueue.Has(id) {
f.queue = append(f.queue, id)
}
}
if len(f.queue) > 0 {
f.cond.Broadcast()
}
return nil
}
Delta其实就是kubernetes系统中对象的变化(增、删、改、同步),FIFO是一个先入先出的队列,那么DeltaFIFO就是一个按序的(先入先出)kubernetes对象变化的队列。
// 接口抽象: 列举所有的key值 通过key值获取对象
type KeyListerGetter interface {
KeyLister
KeyGetter
}
type KeyLister interface {
ListKeys() []string
}
type KeyGetter interface {
GetByKey(key string) (interface{}, bool, error)
}
// DeltaType 改变类型 (addition, deletion, etc)
type DeltaType string
const (
Added DeltaType = "Added"
Updated DeltaType = "Updated"
Deleted DeltaType = "Deleted"
// 1. watch 超时/错误 新的list/watch循环开始
// 2. 打开周期性同步
// 3. trigger's DeltaFIFO's Replace() method
Sync DeltaType = "Sync"
)
// 对象的状态变化
type Delta struct {
Type DeltaType // 改变类型
Object interface{} // 对象
}
// watch对象事件变化数组
// The oldest delta is at index 0, the newest delta is the last one.
type Deltas []Delta
// 最旧的对象事件变化
func (d Deltas) Oldest() *Delta {
if len(d) > 0 {
return &d[0]
}
return nil
}
// 最新的对象事件变化
func (d Deltas) Newest() *Delta {
if n := len(d); n > 0 {
return &d[n-1]
}
return nil
}
func copyDeltas(d Deltas) Deltas {
d2 := make(Deltas, len(d))
copy(d2, d)
return d2
}
// DeletedFinalStateUnknown 类型状态 对象被删除但是 watch 删除事件缺失
type DeletedFinalStateUnknown struct {
Key string
Obj interface{}
}
// NewDeltaFIFO 返回一个用于处理元素更改的存储
// keyFunc 计算对象的对象键 函数
// 'keyLister' is expected to return a list of keys that the consumer of
// this queue "knows about". It is used to decide which items are missing
// when Replace() is called; 'Deleted' deltas are produced for these items.
// It may be nil if you don't need to detect all deletions.
// Also see the comment on DeltaFIFO.
func NewDeltaFIFO(keyFunc KeyFunc, knownObjects KeyListerGetter) *DeltaFIFO {
f := &DeltaFIFO{
items: map[string]Deltas{},
queue: []string{},
keyFunc: keyFunc,
knownObjects: knownObjects,
}
f.cond.L = &f.lock
return f
}
// DeltaFIFO 类似于FIFO 队列,但是允许处理删除case
// DeltaFIFO 生产者消费者队列,Reflector 是生产者 watch 事件, 调用pop方法即是消费者
// 处理场景: 1. 处理每一个 对象改变事件 至多一次
2. 当您处理一个对象时,希望看到自上次处理它以来发生在对象上所有改变
3. 处理删除事件的对象 4. 周期性的处理对象
// DeltaFIFO's Pop(), Get(), and GetByKey() methods return
// interface{} to satisfy the Store/Queue interfaces, but it
// will always return an object of type Deltas.
注意:多个线程并行调用pop(),最终可能是多个线程处理相同对象的不同事件。
deltafifo使用的keylister注意:主要目的是列举对象所有键,以便在调用replace()或delete()时确定哪些键已被删除。删除的对象将包含在DeleteFinalStateUnknown标记中, 这些对象可能是陈旧的。
type DeltaFIFO struct {
lock sync.RWMutex
cond sync.Cond // 互斥访问 'items' and 'queue', goroutinue通信唤醒wait
items map[string]Deltas // 对象键 对象事件 对象
queue []string // 对象键队列
// 通过 Replace() 插入第一批元素 或者 Delete/Add/Update首次被调用
populated bool
// 首次插入元素数目
initialPopulationCount int
keyFunc KeyFunc
// 获取对象键。目的是 当调用Replace() or Delete() 的时候直到 哪一个元素已经被删除
knownObjects KeyListerGetter
// 队列关闭
closed bool
closedLock sync.Mutex
}
// KeyOf exposes f's keyFunc, but also detects the key of a Deltas object or
// DeletedFinalStateUnknown objects.
func (f *DeltaFIFO) KeyOf(obj interface{}) (string, error) {
// 对象类型 是 Deltas数组
// obj 是 事件数组
if d, ok := obj.(Deltas); ok {
if len(d) == 0 {
return "", KeyError{obj, ErrZeroLengthDeltasObject}
}
// 获取最新对象事件
obj = d.Newest().Object
}
// 如果类型是 DeletedFinalStateUnknown 说明 对象已经被删了
if d, ok := obj.(DeletedFinalStateUnknown); ok {
return d.Key, nil
}
return f.keyFunc(obj)
}
// 添加对象接口
func (f *DeltaFIFO) Add(obj interface{}) error {
f.lock.Lock()
defer f.lock.Unlock()
f.populated = true // 队列第一次写入操作都要设置标记
return f.queueActionLocked(Added, obj)
}
// 更新对象接口
func (f *DeltaFIFO) Update(obj interface{}) error {
f.lock.Lock()
defer f.lock.Unlock()
f.populated = true // 队列第一次写入操作都要设置标记
return f.queueActionLocked(Updated, obj)
}
// 删除对象接口
func (f *DeltaFIFO) Delete(obj interface{}) error {
id, err := f.KeyOf(obj) // 对象键
if err != nil {
return KeyError{obj, err}
}
f.lock.Lock()
defer f.lock.Unlock()
f.populated = true // 队列第一次写入操作都要设置标记
// knownObjects就是Indexer,里面存有已知全部的对象,即本地local store
if f.knownObjects == nil {
// 没有Indexer的时,只能通过自己存储的对象查一下
if _, exists := f.items[id]; !exists {
return nil
}
} else {
// 索引队列 对象键是否存在 及 items中存在
_, exists, err := f.knownObjects.GetByKey(id)
_, itemsExist := f.items[id]
if err == nil && !exists && !itemsExist {
return nil
}
}
return f.queueActionLocked(Deleted, obj)
}
// 列举对象键接口
func (f *DeltaFIFO) ListKeys() []string {
f.lock.RLock()
defer f.lock.RUnlock()
list := make([]string, 0, len(f.items))
for key := range f.items {
list = append(list, key)
}
return list
}
// 列举对象接口 最新变化对象
func (f *DeltaFIFO) List() []interface{} {
f.lock.RLock()
defer f.lock.RUnlock()
return f.listLocked()
}
// 列举对象的具体实现
func (f *DeltaFIFO) listLocked() []interface{} {
list := make([]interface{}, 0, len(f.items))
for _, item := range f.items {
item = copyDeltas(item)
list = append(list, item.Newest().Object)
}
return list
}
// 获取对象接口
func (f *DeltaFIFO) Get(obj interface{}) (item interface{}, exists bool, err error) {
key, err := f.KeyOf(obj)
if err != nil {
return nil, false, KeyError{obj, err}
}
return f.GetByKey(key)
}
// 通过对象键获取所有对象变化
func (f *DeltaFIFO) GetByKey(key string) (item interface{}, exists bool, err error) {
f.lock.RLock()
defer f.lock.RUnlock()
d, exists := f.items[key]
if exists {
d = copyDeltas(d)
}
return d, exists, nil
}
// 判断是否关闭
func (f *DeltaFIFO) IsClosed() bool {
f.closedLock.Lock()
defer f.closedLock.Unlock()
if f.closed {
return true
}
return false
}
// 对象键 获取 对象的最新事件是不是 Deleted
func (f *DeltaFIFO) willObjectBeDeletedLocked(id string) bool {
deltas := f.items[id]
return len(deltas) > 0 && deltas[len(deltas)-1].Type == Deleted
}
// 调用方必须加锁
// 为对象追加 对象时间变化
func (f *DeltaFIFO) queueActionLocked(actionType DeltaType, obj interface{}) error {
// 获取 对象键
// 对象事件变化 最新事件变化 判断是否是删除事件
id, err := f.KeyOf(obj)
if err != nil {
return KeyError{obj, err}
}
// 对象上次事件是 删除事件 && 或者是同步事件,否则会导致对象重新创建
if actionType == Sync && f.willObjectBeDeletedLocked(id) {
return nil
}
// 同一个对象的多次操作,所以要追加到Deltas数组中
newDeltas := append(f.items[id], Delta{actionType, obj})
// 事件去重
newDeltas = dedupDeltas(newDeltas)
if len(newDeltas) > 0 {
// 对象是否存在 不存在添加到队列
if _, exists := f.items[id]; !exists {
f.queue = append(f.queue, id)
}
// 存在 更新下Deltas数组,通知所有调用pop的消费者
f.items[id] = newDeltas
f.cond.Broadcast()
} else {
// 删除对象键,如果不在map中,队列中的会被忽略
delete(f.items, id)
}
return nil
}
func dedupDeltas(deltas Deltas) Deltas {
n := len(deltas)
if n < 2 {
return deltas
}
// 取出最后两个
a := &deltas[n-1]
b := &deltas[n-2]
// 重复,删除并添加到Deltas{} 数组尾部返回
if out := isDup(a, b); out != nil {
d := append(Deltas{}, deltas[:n-2]...)
return append(d, *out)
}
return deltas
}
// 判断两个Delta是否是重复的
func isDup(a, b *Delta) *Delta {
// 判断多种类型重复
// 当前仅支持判断是否为删除类的重复操作
if out := isDeletionDup(a, b); out != nil {
return out
}
return nil
}
// 判断是否为删除类的重复
func isDeletionDup(a, b *Delta) *Delta {
// 二者都是删除那肯定有一个是重复的
if b.Type != Deleted || a.Type != Deleted {
return nil
}
if _, ok := b.Object.(DeletedFinalStateUnknown); ok {
return a
}
return b
}
系统删除对象时填充对于缺失的填充的时DeletedFinalStateUnknown这个状态,所以会存在两次删除的情况。添加同一个对象操作可能由apiserver进行保证一致性,所以没有存在合并两次相同添加的操作。
// 和FIFO 分析类似
func (f *DeltaFIFO) Pop(process PopProcessFunc) (interface{}, error) {
f.lock.Lock()
defer f.lock.Unlock()
for {
for len(f.queue) == 0 {
// 队列为空 队列关闭
if f.IsClosed() {
return nil, FIFOClosedError
}
f.cond.Wait()
}
id := f.queue[0]
f.queue = f.queue[1:]
// 同步对象计数减一,当减到0就说明外部已经全部同步完毕了
if f.initialPopulationCount > 0 {
f.initialPopulationCount--
}
item, ok := f.items[id]
// 被删除
if !ok {
// Item may have been deleted subsequently.
continue
}
// 删除 对象事件变化
delete(f.items, id)
// 回调函数 处理对象
err := process(item)
if e, ok := err.(ErrRequeue); ok {
// 处理失败 重新如队列
f.addIfNotPresent(id, item)
err = e.Err
}
// Don't need to copyDeltas here, because we're transferring
// ownership to the caller.
return item, err
}
}
// 外部同步缓存的
func (f *DeltaFIFO) Replace(list []interface{}, resourceVersion string) error {
f.lock.Lock()
defer f.lock.Unlock()
keys := make(sets.String, len(list))
// 遍历所有的输入目标
for _, item := range list {
// 计算目标键
key, err := f.KeyOf(item)
if err != nil {
return KeyError{item, err}
}
// 记录处理过的目标键,采用set存储,是为了后续快速查找
keys.Insert(key)
// 因为输入是目标全量,所以每个目标相当于重新同步了一次
if err := f.queueActionLocked(Sync, item); err != nil {
return fmt.Errorf("couldn't enqueue object: %v", err)
}
}
// 缓存的旧对象 在不在全量新对象中,不在就要删除对象
// 对比队列和对象全量来判断对象是否删除
if f.knownObjects == nil {
// 遍历所有的缓存元素 对象键 对象变化事件
for k, oldItem := range f.items {
// 老对象存在,忽略
if keys.Has(k) {
continue
}
// 新对象中不存在 缓存中的对象
var deletedObj interface{}
if n := oldItem.Newest(); n != nil {
deletedObj = n.Object
}
// 避免重复,采用DeletedFinalStateUnknown这种类型
if err := f.queueActionLocked(Deleted, DeletedFinalStateUnknown{k, deletedObj}); err != nil {
return err
}
}
// 如果populated还没有设置,说明是第一次 并且 还没有任何修改操作执行过
if !f.populated {
f.populated = true
f.initialPopulationCount = len(list) // 记录第一次通过来的对象数量
}
return nil
}
// 从本地存储中获取所有对象键, 本地缓存存在
knownKeys := f.knownObjects.ListKeys()
queuedDeletions := 0
// 判读是否存在新目标对象中
for _, k := range knownKeys {
// 存在忽略
if keys.Has(k) {
continue
}
// 对象键 获取对象
deletedObj, exists, err := f.knownObjects.GetByKey(k)
if err != nil {
deletedObj = nil
glog.Errorf("Unexpected error %v during lookup of key %v, placing DeleteFinalStateUnknown marker without object", err, k)
} else if !exists {
deletedObj = nil
glog.Infof("Key %v does not exist in known objects store, placing DeleteFinalStateUnknown marker without object", k)
}
// 删除对象数量++
queuedDeletions++
// 删除对象事件放入队列中
if err := f.queueActionLocked(Deleted, DeletedFinalStateUnknown{k, deletedObj}); err != nil {
return err
}
}
// 增加删除对象数量
if !f.populated {
f.populated = true
f.initialPopulationCount = len(list) + queuedDeletions
}
return nil
}
replace实现对象的全量更新进行同步。DeltaFIFO主要是对目标对象进行增量变化,全量更新时判断对象是否已经删除,可能在全量更新前并没有收到删除对象的请求。cache的Replace()相当于重建,因为cache是对对象的全量内存存储。
图片来源:https://blog.csdn.net/weixin_42663840/article/details/81626789
如有侵权,作者可联系我删除。最近 主要是通过上述链接文章的讲解和源码,加深自己的理解。目前看的仅仅是基础包,可能有些地方理解的不是很到位,需要后续看组件源码的时候再深挖!
https://blog.csdn.net/weixin_42663840/article/details/81626789
https://github.com/kubernetes/client-go/blob/master/tools/cache/delta_fifo.go