kubernetes之client-go基础包fifo和delta_fifo

目录

直奔主题

FIFO和delta_FIFO抽象

FIFO实现

结构及初始化(调度器用到这个队列)

重要方法介绍

DELTA_FIFO实现

其他几个类型说明

结构及初始化

重要方法介绍

DeltaFIFO创建对象键函数

基础方法

总结

参考


直奔主题

FIFO和delta_FIFO抽象

// 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实现

结构及初始化(调度器用到这个队列)

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_FIFO实现

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
}

重要方法介绍

DeltaFIFO创建对象键函数

// 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是对对象的全量内存存储。

总结

kubernetes之client-go基础包fifo和delta_fifo_第1张图片

图片来源: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

 

你可能感兴趣的:(kubernetes,fifo,delta_fifo,client-go,kubernetes)