FIFO
在reflector中定义了一个FIFO的存储。
items
: 一个 map,用于存储实际的对象,键是对象的唯一标识符。queue
: 一个字符串切片,保存了对象键的有序列表,维护了插入顺序。keyFunc
: 一个函数,用于从对象生成唯一键。lock
: 用于保护并发访问的读写锁。cond
: 条件变量,用于在队列状态改变时发出通知。
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 用于后续的高效数据检索和查询。
// 创建 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")来创建额外的索引。
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。
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
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 map[string]IndexFunc,然后这里也定义了type IndexFunc func(Book) []string,那么就可以将indexer定义为“author”:authorIndexFunc,查看index.go的源码,可以看到类似的type定义。
func NewLibrary() *Library {
return &Library{
books: make(map[string]Book),
indexers: Indexers{
"author": authorIndexFunc,
"genre": genreIndexFunc,
},
indices: make(Indices),
}
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)
}