[k8s源码]7.indexer

FIFO

在reflector中定义了一个FIFO的存储。

  • items: 一个 map,用于存储实际的对象,键是对象的唯一标识符。
  • queue: 一个字符串切片,保存了对象键的有序列表,维护了插入顺序。
  • keyFunc: 一个函数,用于从对象生成唯一键。
  • lock: 用于保护并发访问的读写锁。
  • cond: 条件变量,用于在队列状态改变时发出通知。
Go 复制代码
func NewFIFO(keyFunc KeyFunc) *FIFO {
	f := &FIFO{
		items:   map[string]interface{}{},
		queue:   []string{},
		keyFunc: keyFunc,
	}
	f.cond.L = &f.lock
	return f
}

FIFO 和 Indexer 都是 Kubernetes 客户端库中的重要组件,但它们服务于不同的目的。FIFO 主要用于保持对象的处理顺序,而 Indexer 提供了更强大的查询和索引能力。在实际应用中,它们常常一起使用:FIFO 用于初始数据接收和排序,而 Indexer 用于后续的高效数据检索和查询。

Go 复制代码
// 创建 Indexer
indexer := cache.NewIndexer(cache.MetaNamespaceKeyFunc, cache.Indexers{
    "namespace": cache.MetaNamespaceIndexFunc,
})

// 创建 DeltaFIFO
fifo := cache.NewDeltaFIFO(cache.MetaNamespaceKeyFunc, indexer)

// 创建 Reflector
reflector := cache.NewReflector(
    listerWatcher,
    &api.Pod{},
    fifo,
    0,
)

// 启动 Reflector
go reflector.Run(stopCh)

// 处理 FIFO 中的项目
for {
    item, shutdown := fifo.Pop(cache.PopProcessFunc(func(obj interface{}) error {
        // 处理逻辑
        indexer.Update(obj)
        return nil
    }))
    if shutdown {
        break
    }
}

Reflector 持续监听 API 服务器的变更。

变更被推送到 DeltaFIFO。

DeltaFIFO 保持这些变更的顺序。

处理函数从 DeltaFIFO 取出变更,更新 Indexer。

Indexer 维护最新的对象状态,并提供查询能力。

indexer demo

indexer提供存储和索引的能力。Indexer 使用 MetaNamespaceKeyFunc 为每个对象生成一个唯一的键。同时,Indexer 使用索引函数(如 "namespace" 和 "nodeName")来创建额外的索引。

Go 复制代码
package main

import (
	"fmt"

	v1 "k8s.io/api/core/v1"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/client-go/tools/cache"
)

func main() {
	// 创建 Indexer
	indexer := cache.NewIndexer(
		cache.MetaNamespaceKeyFunc,
		cache.Indexers{
			"namespace": func(obj interface{}) ([]string, error) {
				pod, ok := obj.(*v1.Pod)
				if !ok {
					return []string{}, nil
				}
				return []string{pod.Namespace}, nil
			},
		},
	)

	// 添加 nodeName 索引
	err := indexer.AddIndexers(cache.Indexers{
		"nodeName": func(obj interface{}) ([]string, error) {
			pod, ok := obj.(*v1.Pod)
			if !ok {
				return []string{}, nil
			}
			return []string{pod.Spec.NodeName}, nil
		},
	})
	if err != nil {
		panic(err)
	}

	// 添加一些 Pod
	pods := []*v1.Pod{
		{
			ObjectMeta: metav1.ObjectMeta{Name: "pod1", Namespace: "default"},
			Spec:       v1.PodSpec{NodeName: "node1"},
		},
		{
			ObjectMeta: metav1.ObjectMeta{Name: "pod2", Namespace: "kube-system"},
			Spec:       v1.PodSpec{NodeName: "node1"},
		},
		{
			ObjectMeta: metav1.ObjectMeta{Name: "pod3", Namespace: "default"},
			Spec:       v1.PodSpec{NodeName: "node2"},
		},
	}

	for _, pod := range pods {
		err := indexer.Add(pod)
		if err != nil {
			fmt.Printf("Error adding pod: %v\n", err)
		}
	}

	// 使用 namespace 索引
	defaultPods, err := indexer.ByIndex("namespace", "default")
	if err != nil {
		panic(err)
	}
	fmt.Printf("Pods in default namespace: %d\n", len(defaultPods))

	// 使用 nodeName 索引
	node1Pods, err := indexer.ByIndex("nodeName", "node1")
	if err != nil {
		panic(err)
	}
	fmt.Printf("Pods on node1: %d\n", len(node1Pods))

	// 获取所有索引器
	allIndexers := indexer.GetIndexers()
	for indexName := range allIndexers {
		fmt.Printf("Index: %s\n", indexName)
	}
}

这里创建的就是indexer(索引器),加入的"namespace"和"NodeName"就是索引index。

Go 复制代码
Store {
    // 主存储
    MainStorage: {
        "default/pod-1": Pod-1对象,
        "kube-system/pod-2": Pod-2对象,
        "default/pod-3": Pod-3对象
    },

    // Indices(索引集)
    Indices: {
        "namespace": {
            // Index(命名空间索引)
            "default": ["default/pod-1", "default/pod-3"],
            "kube-system": ["kube-system/pod-2"]
        },
        "nodeName": {
            // Index(节点名索引)
            "node-A": ["default/pod-1", "kube-system/pod-2"],
            "node-B": ["default/pod-3"]
        }
    }
}
library indexer demo
Go 复制代码
package main

import (
	"fmt"
)

type Book struct {
	ID     string
	Title  string
	Author string
	Genre  string
}
type IndexFunc func(Book) []string
type Index map[string][]string
type Indexers map[string]IndexFunc
type Indices map[string]Index

type Library struct {
	books    map[string]Book
	indexers Indexers
	indices  Indices
}

func authorIndexFunc(book Book) []string {
	return []string{book.Author}
}
func genreIndexFunc(book Book) []string {
	return []string{book.Genre}
}
func NewLibrary() *Library {
	return &Library{
		books: make(map[string]Book),
		indexers: Indexers{
			"author": authorIndexFunc,
			"genre":  genreIndexFunc,
		},
		indices: make(Indices),
	}
}
func (l *Library) AddBook(book Book) {
	l.books[book.ID] = book
	for indexName, indexFunc := range l.indexers {
		indexvalues := indexFunc(book)
		for _, value := range indexvalues {
			if l.indices[indexName] == nil {
				l.indices[indexName] = make(Index)
			}
			l.indices[indexName][value] = append(l.indices[indexName][value], book.ID)
		}
	}
}
func (l *Library) FindBooksByAuthor(author string) []Book {
	bookIDs := l.indices["author"][author]
	var books []Book
	for _, id := range bookIDs {
		books = append(books, l.books[id])
	}
	return books
}
// AddIndexer 方法用于添加新的索引器
func (l *Library) AddIndexer(name string, indexFunc IndexFunc) {
	l.indexers[name] = indexFunc
	l.indices[name] = make(Index)

	// 为现有的书籍创建新的索引
	for _, book := range l.books {
		l.indexBook(name, book)
	}
}
func (l *Library) indexBook(indexName string, book Book) {
	indexFunc := l.indexers[indexName]
	indexValues := indexFunc(book)
	for _, value := range indexValues {
		l.indices[indexName][value] = append(l.indices[indexName][value], book.ID)
	}
}
func main() {
	// 创建一个新的 Library
	library := NewLibrary()

	// 添加一些书籍
	library.AddBook(Book{ID: "1", Title: "Harry Potter and the Philosopher's Stone", Author: "J.K. Rowling", Genre: "Fantasy"})
	library.AddBook(Book{ID: "2", Title: "Harry Potter and the Chamber of Secrets", Author: "J.K. Rowling", Genre: "Fantasy"})
	library.AddBook(Book{ID: "3", Title: "1984", Author: "George Orwell", Genre: "Dystopian"})

	// 测试 FindBooksByAuthor 方法
	rowlingBooks := library.FindBooksByAuthor("J.K. Rowling")
	fmt.Println("Books by J.K. Rowling:")
	for _, book := range rowlingBooks {
		fmt.Printf("- %s\n", book.Title)
	}

	orwellBooks := library.FindBooksByAuthor("George Orwell")
	fmt.Println("\nBooks by George Orwell:")
	for _, book := range orwellBooks {
		fmt.Printf("- %s\n", book.Title)
	}
}

通过这个例子,我们可以更好的理解indexer的代码。比如,下面代码是创建一个名叫library的结构体的代码,可以看到这里indexer也是type Indexers mapstringIndexFunc,然后这里也定义了type IndexFunc func(Book) \[\]string,那么就可以将indexer定义为"author":authorIndexFunc,查看index.go的源码,可以看到类似的type定义。

func NewLibrary() *Library {

return &Library{

books: make(mapstringBook),

indexers: Indexers{

"author": authorIndexFunc,

"genre": genreIndexFunc,

},

indices: make(Indices),

}

Go 复制代码
type Indexer interface {
	Store
	// Index returns the stored objects whose set of indexed values
	// intersects the set of indexed values of the given object, for
	// the named index
	Index(indexName string, obj interface{}) ([]interface{}, error)
	// IndexKeys returns the storage keys of the stored objects whose
	// set of indexed values for the named index includes the given
	// indexed value
	IndexKeys(indexName, indexedValue string) ([]string, error)
	// ListIndexFuncValues returns all the indexed values of the given index
	ListIndexFuncValues(indexName string) []string
	// ByIndex returns the stored objects whose set of indexed values
	// for the named index includes the given indexed value
	ByIndex(indexName, indexedValue string) ([]interface{}, error)
	// GetIndexers return the indexers
	GetIndexers() Indexers

	// AddIndexers adds more indexers to this store. This supports adding indexes after the store already has items.
	AddIndexers(newIndexers Indexers) error
}

// IndexFunc knows how to compute the set of indexed values for an object.
type IndexFunc func(obj interface{}) ([]string, error)

// IndexFuncToKeyFuncAdapter adapts an indexFunc to a keyFunc.  This is only useful if your index function returns
// unique values for every object.  This conversion can create errors when more than one key is found.  You
// should prefer to make proper key and index functions.
func IndexFuncToKeyFuncAdapter(indexFunc IndexFunc) KeyFunc {
	return func(obj interface{}) (string, error) {
		indexKeys, err := indexFunc(obj)
		if err != nil {
			return "", err
		}
		if len(indexKeys) > 1 {
			return "", fmt.Errorf("too many keys: %v", indexKeys)
		}
		if len(indexKeys) == 0 {
			return "", fmt.Errorf("unexpected empty indexKeys")
		}
		return indexKeys[0], nil
	}
}

const (
	// NamespaceIndex is the lookup name for the most common index function, which is to index by the namespace field.
	NamespaceIndex string = "namespace"
)

// MetaNamespaceIndexFunc is a default index function that indexes based on an object's namespace
func MetaNamespaceIndexFunc(obj interface{}) ([]string, error) {
	meta, err := meta.Accessor(obj)
	if err != nil {
		return []string{""}, fmt.Errorf("object has no meta: %v", err)
	}
	return []string{meta.GetNamespace()}, nil
}

// Index maps the indexed value to a set of keys in the store that match on that value
type Index map[string]sets.String

// Indexers maps a name to an IndexFunc
type Indexers map[string]IndexFunc

// Indices maps a name to an Index
type Indices map[string]Index

同理,indexer demo中,"nodeName":func()和上面的"author":authorIndexFunc是一个道理。

func main() {

// 创建 Indexer

indexer := cache.NewIndexer(

cache.MetaNamespaceKeyFunc,

cache.Indexers{

"namespace": func(obj interface{}) (\[\]string, error) {

pod, ok := obj.(*v1.Pod)

if !ok {

return \[\]string{}, nil

}

return \[\]string{pod.Namespace}, nil

},

},

)

// 添加 nodeName 索引

err := indexer.AddIndexers(cache.Indexers{

"nodeName": func(obj interface{}) (\[\]string, error) {

pod, ok := obj.(*v1.Pod)

if !ok {

return \[\]string{}, nil

}

return \[\]string{pod.Spec.NodeName}, nil

},

})

if err != nil {

panic(err)

}

相关推荐
Scott9999HH6 小时前
【IIoT流量实战】蒸汽管道阀门全关却仍有流量?用 Python 实现涡街信号 FFT 频谱分析与温压全补偿积算网关,深度拆解靠谱的涡街流量计厂家硬核技术标准
开发语言·python
腻害兔6 小时前
【若依项目-产品经理视角】深度拆解 RuoYi-Vue-Pro 商城模块:从商品管理到交易引擎,50 张表撑起一整套电商系统
java·大数据·vue.js·产品经理·ai编程
码智社7 小时前
AES加密原理详解及Java实现加解密实战
java·开发语言
AI云海7 小时前
python 列表、元组、集合和字典
开发语言·python
萧瑟余晖8 小时前
JDK 26 新特性详解
java·开发语言
马优晨8 小时前
Freemarker 完整讲解(后端 Java 模板引擎)
java·开发语言·freemarker·freemarker 完整讲解·freemarker模板引擎
人邮异步社区10 小时前
怎么把C语言学到精通?
c语言·开发语言
心平气和量大福大11 小时前
C#-WPF-控件-TextBox 数据绑定
开发语言·c#·wpf
ttwuai11 小时前
Cursor 生成 CRUD 后,Go 后台接口别只测 200:JWT、RBAC 和 tenant_id 怎么验
开发语言·后端·golang
维天说11 小时前
CLI-Switch 2026年3月版历史设计:Hook、TTY 隔离与 JSON 状态
java·服务器·json