Indexer 源码分析

介绍

我们知道DeltaFIFO 中的元素通过 Pop 函数弹出后,在指定的回调函数中将元素添加到了 Indexer 中。

Indexer 是什么?字面意思是索引器,它就是 Informer 中的 LocalStore 部分,我们可以和数据库进行类比,数据库是建立在存储之上的,索引也是构建在存储之上,只是和数据做了一个映射,使得按照某些条件查询速度会非常快,所以说 Indexer 本身也是一个存储,只是它在存储的基础上扩展了索引功能。从 Indexer 接口的定义可以证明这一点:

// staging/src/k8s.io/client-go/tools/cache/index.go

// Indexer extends Store with multiple indices and restricts each
// accumulator to simply hold the current object (and be empty after
// Delete).
//
// There are three kinds of strings here:
// 1. a storage key, as defined in the Store interface,
// 2. a name of an index, and
// 3. an "indexed value", which is produced by an IndexFunc and
// can be a field value or any other string computed from the object. // Indexer 使用多个索引扩展了 Store,并限制了每个累加器只能容纳当前对象
// 这里有3种字符串需要说明:
// 1. 一个存储键,在 Store 接口中定义(其实就是对象键)
// 2. 一个索引的名称(相当于索引分类名称)
// 3. 索引键,由 IndexFunc 生成,可以是一个字段值或从对象中计算出来的任何字符串
type Indexer interface {
// Store 继承了 Store 存储接口,所以说Indexer 也是存储
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
// indexName 是索引类名称,obj 是对象,计算 obj 在 indexName 索引类中的索引键,然后通过索引键把所有的对象取出来
// 获取 obj 对象在索引类中的索引键相匹配的对象
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
// indexKey 是 indexName 索引分类中的一个索引键
// 函数返回 indexKey 指定的所有对象键 I
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)
// GetIndexer return the indexers
GetIndexers() Indexers // AddIndexers adds more indexers to this store. If you call this after you already have data
// in the store, the results are undefined.
// 添加更多的存储在索引中
AddIndexers(newIndexers Indexers) error
}

Indexer

在去查看 Indexer 的接口具体实现之前,我们需要了解Indexer中几个非常重要的概念:Indices、Index、Indexers 及 IndexFunc。

// staging/src/k8s.io/client-go/tools/cache/index.go

// IndexFunc knows how to compute the set of indexed values for an object.
// 用于计算一个对象的索引键的集合
type IndexFunc func(obj interface{}) ([]string, error) // 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
// 索引器名称与 IndexFunc 的映射,相当于存储索引的各种分类
type Indexers map[string]IndexFunc // Indices maps a name to an Index
// 索引器名称与 Index 索引的映射
type Indices map[string]Index

这4个数据结构的命名非常容易让大家混淆,直接查看源码也不是那么容易的。

这里我们来仔细解释下。

首先什么叫索引,索引就是为了快速查找的,比如我们需要查找某个节点上的所有 Pod,那就让 Pod 按照节点名称排序列举出来,对应的就是 Index 这个类型,具体的就是 map[node]sets.pod,但是如何去查找可以有多种方式,就是上面的 Indexers 这个类型的作用。

我们可以用一个比较具体的示例来解释他们的关系和含义,如下所示:

package main

import (
"fmt"
v1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/meta"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/client-go/tools/cache"
) const (
NamespaceIndexName = "namespace"
NodeNameIndexName = "nodeName"
) func NamespaceIndexFunc(obj interface{}) ([]string, error) {
m, err := meta.Accessor(obj)
if err != nil {
return []string{""}, fmt.Errorf("object has no meta:%v", err)
}
return []string{m.GetNamespace()}, nil
} func NodeNameIndexFunc(obj interface{}) ([]string, error) {
pod, ok := obj.(*v1.Pod)
if !ok {
return []string{}, nil
}
return []string{pod.Spec.NodeName}, nil
} func main() {
index := cache.NewIndexer(cache.MetaNamespaceKeyFunc, cache.Indexers{
NamespaceIndexName: NamespaceIndexFunc,
NodeNameIndexName: NodeNameIndexFunc,
}) pod1 := &v1.Pod{
ObjectMeta: metav1.ObjectMeta{
Name: "index-pod-1",
Namespace: "default",
},
Spec: v1.PodSpec{
NodeName: "node1",
},
} pod2 := &v1.Pod{
ObjectMeta: metav1.ObjectMeta{
Name: "index-pod-2",
Namespace: "default",
},
Spec: v1.PodSpec{
NodeName: "node2",
},
} pod3 := &v1.Pod{
ObjectMeta: metav1.ObjectMeta{
Name: "index-pod-3",
Namespace: "kube-system",
},
Spec: v1.PodSpec{
NodeName: "node2",
},
} _ = index.Add(pod1)
_ = index.Add(pod2)
_ = index.Add(pod3) // ByIndex 两个参数:IndexName(索引器的名称) 和 indexKey(需要检索的key)
pods, err := index.ByIndex(NamespaceIndexName, "default")
if err != nil {
panic(err)
} for _, pod := range pods {
fmt.Println(pod.(*v1.Pod).Name)
} fmt.Println("=========================") pods, err = index.ByIndex(NodeNameIndexName, "node2")
if err != nil {
panic(err)
} for _, pod := range pods {
fmt.Println(pod.(*v1.Pod).Name)
}
} // 输出结果为:
index-pod-1
index-pod-2
=========================
index-pod-2
index-pod-3

在上面的示例中首先通过 NewIndexer 函数实例化 Indexer 对象,第一个参数就是用于计算资源对象键的函数,这里我们使用的是 MetaNamespaceKeyFunc 这个默认的对象键函数;第二个参数是 Indexers,也就是存储索引器,上面我们知道 Indexers 的定义为 map[string]IndexFunc,为什么要定义成一个 map 呢?

我们可以类比数据库中,我们要查询某项数据,索引的方式是不是多种多样啊?为了扩展,Kubernetes 中就使用一个 map 来存储各种各样的存储索引器,至于存储索引器如何生成,就使用一个 IndexFunc 暴露出去,给使用者自己实现即可。

这里我们定义的了两个索引键生成函数: NamespaceIndexFuncNodeNameIndexFunc,一个根据资源对象的命名空间来进行索引,一个根据资源对象所在的节点进行索引。然后定义了3个 Pod,前两个在 default 命名空间下面,另外一个在 kube-system 命名空间下面,然后通过 index.Add 函数添加这3个 Pod 资源对象。然后通过 index.ByIndex 函数查询在名为 namespace 的索引器下面匹配索引键为 default 的 Pod 列表。也就是查询 default 这个命名空间下面的所有 Pod,这里就是前两个定义的 Pod。

对上面的示例如果我们理解了,那么就很容易理解上面定义的4个数据结构了:

  • IndexFunc:索引器函数,用于计算一个资源对象的索引值列表,上面示例是指定命名空间为索引值结果,当然我们也可以根据需求定义其他的,比如根据 Label 标签、Annotation 等属性来生成索引值列表。
  • Index:存储数据,对于上面的示例,我们要查找某个命名空间下面的 Pod,那就要让 Pod 按照其命名空间进行索引,对应的 Index 类型就是 map[namespace]sets.pod
  • Indexers:存储索引器,key 为索引器名称,value 为索引器的实现函数,上面的示例就是 map["namespace"]MetaNamespaceIndexFunc。
  • Indices:存储缓存器,key 为索引器名称,value 为缓存的数据,对于上面的示例就是 map["namespace"]map[namespace]sets.pod。

可能最容易混淆的是 Indexers 和 Indices 这两个概念,因为平时很多时候我们没有怎么区分二者的关系,这里我们可以这样理解:Indexers 是存储索引的,Indices 里面是存储的真正的数据(对象键),这样可能更好理解。

按照上面的理解我们可以得到上面示例的索引数据如下所示:

// Indexers 就是包含的所有索引器(分类)以及对应实现
Indexers: {
"namespace": NamespaceIndexFunc,
"nodeName": NodeNameIndexFunc,
} // Indices 就是包含的所有索引分类中所有的索引数据
Indices: {
"namespace": { //namespace 这个索引分类下的所有索引数据
"default": ["pod-1", "pod-2"], // Index 就是一个索引键下所有的对象键列表
"kube-system": ["pod-3"] // Index
},
"nodeName": { //nodeName 这个索引分类下的所有索引数据(对象键列表)
"node1": ["pod-1"], // Index
"node2": ["pod-2", "pod-3"] // Index
}
}

ThreadSafeMap

上面我们理解了 Indexer 中的几个重要的数据类型,下面我们来看下 Indexer 接口的具体实现 cache,位于文件staging/src/k8s.io/client-go/tools/cache/store.go中:

// staging/src/k8s.io/client-go/tools/cache/store.go

// `*cache` implements Indexer in terms of a ThreadSafeStore and an
// associated KeyFunc.
// cache 用一个 ThreadSafeStore 和一个关联的 KeyFunc 来实现 Indexer
type cache struct {
// cacheStorage bears the burden of thread safety for the cache
// cacheStorage 是一个线程安全的存储
cacheStorage ThreadSafeStore
// keyFunc is used to make the key for objects stored in and retrieved from items, and
// should be deterministic.
// keyFunc 用于计算对象键
keyFunc KeyFunc
}

我们可以看到这个 cache 包含一个 ThreadSafeStore 的属性,这是一个并发安全的存储,因为是存储,所以自然就有存储相关的增、删、改、查等操作,Indexer 就是在 ThreadSafeMap 基础上进行封装的,实现了索引相关的功能。

接下来我们先来看看 ThreadSafeStore 的定义,位于 staging/src/k8s.io/client-go/tools/cache/thread_safe_store.go 文件中:

// staging/src/k8s.io/client-go/tools/cache/thread_safe_store.go 

type ThreadSafeStore interface {
Add(key string, obj interface{})
Update(key string, obj interface{})
Delete(key string)
Get(key string) (item interface{}, exists bool)
List() []interface{}
ListKeys() []string
Replace(map[string]interface{}, string)
Index(indexName string, obj interface{}) ([]interface{}, error)
IndexKeys(indexName, indexKey string) ([]string, error)
ListIndexFuncValues(name string) []string
ByIndex(indexName, indexKey string) ([]interface{}, error)
GetIndexers() Indexers // AddIndexers adds more indexers to this store. If you call this after you already have data
// in the store, the results are undefined.
AddIndexers(newIndexers Indexers) error
// Resync is a no-op and is deprecated
Resync() error
}

从接口的定义可以看出 ThreadSafeStore 和 Index 基本上差不多,但还是有一些区别的,这个接口是需要通过对象键来进行索引的。接下来我们来看看这个接口的具体实现 threadSafeMap 的定义:

// staging/src/k8s.io/client-go/tools/cache/thread_safe_store.go 

// threadSafeMap implements ThreadSafeStore
// threadSafeMap 实现了 ThreadSafeStore
type threadSafeMap struct {
lock sync.RWMutex
// 存储资源对象数据,key(对象键) 通过 keyFunc 得到
// 这就是真正的存储数据(对象键->对象)
items map[string]interface{} // indexers maps a name to an IndexFunc
// indexers 索引分类与索引键函数的映射
indexers Indexers
// indices maps a name to an Index
// indices 通过索引可以快速找到对象键
indices Indices
}

不要把索引键和对象键搞混了,索引键是用于对象快速查找的;对象键是对象在存储中的唯一命名,对象是通过名字+对象的方式存储的。

接下来我们来仔细看下接口的具体实现,首先还是比较简单的 Add、Delete、Update 几个函数的实现:

// staging/src/k8s.io/client-go/tools/cache/thread_safe_store.go 

// Add 添加对象,和Update一样
func (c *threadSafeMap) Add(key string, obj interface{}) {
c.Update(key, obj)
} func (c *threadSafeMap) Update(key string, obj interface{}) {
c.lock.Lock()
defer c.lock.Unlock()
// 获取老得对象
oldObject := c.items[key]
// 写入新的对象
c.items[key] = obj
// 添加了新的对象,所以要更新索引
c.updateIndices(oldObject, obj, key)
} // Delete 删除对象
func (c *threadSafeMap) Delete(key string) {
c.lock.Lock()
defer c.lock.Unlock()
if obj, exists := c.items[key]; exists {
c.updateIndices(obj, nil, key)
delete(c.items, key)
}
}

可以看到基本的实现比较简单,就是添加、更新、删除对象数据后,然后更新或删除对应的索引,所以我们需要查看下更新或删除索引的具体实现:

// staging/src/k8s.io/client-go/tools/cache/thread_safe_store.go 

// updateIndices modifies the objects location in the managed indexes:
// - for create you must provide only the newObj
// - for update you must provide both the oldObj and the newObj
// - for delete you must provide only the oldObj
// updateIndices must be called from a function that already has a lock on the cache
// updateIndices修改对象在管理索引中的位置。
// - 对于创建,你必须只提供newObj
// - 对于更新,你必须同时提供oldObj和newObj
// - 对于删除,你必须只提供oldObj。
// updateIndices必须从一个已经拥有缓存锁的函数中调用。
func (c *threadSafeMap) updateIndices(oldObj interface{}, newObj interface{}, key string) {
var oldIndexValues, indexValues []string
var err error
for name, indexFunc := range c.indexers {
// 判断oldObj是否为空
if oldObj != nil {
oldIndexValues, err = indexFunc(oldObj)
} else {
// 置空?
oldIndexValues = oldIndexValues[:0]
}
if err != nil {
panic(fmt.Errorf("unable to calculate an index entry for key %q on index %q: %v", key, name, err))
} if newObj != nil {
indexValues, err = indexFunc(newObj)
} else {
indexValues = indexValues[:0]
}
if err != nil {
panic(fmt.Errorf("unable to calculate an index entry for key %q on index %q: %v", key, name, err))
} index := c.indices[name]
if index == nil {
index = Index{}
c.indices[name] = index
} if len(indexValues) == 1 && len(oldIndexValues) == 1 && indexValues[0] == oldIndexValues[0] {
// We optimize for the most common case where indexFunc returns a single value which has not been changed
continue
} // 将旧对象进行删除
for _, value := range oldIndexValues {
c.deleteKeyFromIndex(key, value, index)
}
// 将新对象添加到集合
for _, value := range indexValues {
c.addKeyToIndex(key, value, index)
}
}
} func (c *threadSafeMap) addKeyToIndex(key, indexValue string, index Index) {
set := index[indexValue]
if set == nil {
set = sets.String{}
index[indexValue] = set
}
set.Insert(key)
} func (c *threadSafeMap) deleteKeyFromIndex(key, indexValue string, index Index) {
set := index[indexValue]
if set == nil {
return
}
set.Delete(key)
// If we don't delete the set when zero, indices with high cardinality
// short lived resources can cause memory to increase over time from
// unused empty sets. See `kubernetes/kubernetes/issues/84959`.
if len(set) == 0 {
delete(index, indexValue)
}
}

添加索引和删除索引的实现都挺简单的,其实主要还是要对 indices、indexs 这些数据结构非常了解,这样就非常容易了,我们可以将 indexFunc 当成当前对象的命名空间来看待,这样对于上面的索引更新和删除的理解就肯定没问题了。

// staging/src/k8s.io/client-go/tools/cache/thread_safe_store.go 

// Get 获取对象
func (c *threadSafeMap) Get(key string) (item interface{}, exists bool) {
c.lock.RLock()
defer c.lock.RUnlock()
// 直接从 map 中获取值
item, exists = c.items[key]
return item, exists
} func (c *threadSafeMap) List() []interface{} {
c.lock.RLock()
defer c.lock.RUnlock()
list := make([]interface{}, 0, len(c.items))
for _, item := range c.items {
list = append(list, item)
}
return list
} // ListKeys returns a list of all the keys of the objects currently
// in the threadSafeMap.
// 返回 threadSafeMap 中所有的对象键列表
func (c *threadSafeMap) ListKeys() []string {
c.lock.RLock()
defer c.lock.RUnlock()
list := make([]string, 0, len(c.items))
for key := range c.items {
list = append(list, key)
}
return list
} // Replace 替换所有对象,相当于重新构建索引
func (c *threadSafeMap) Replace(items map[string]interface{}, resourceVersion string) {
c.lock.Lock()
defer c.lock.Unlock()
// 直接覆盖之前的对象
c.items = items // rebuild any index
c.indices = Indices{}
// 重新构建索引
for key, item := range c.items {
c.updateIndices(nil, item, key)
}
}

接下来就是和索引相关的几个接口实现,第一个就是Index函数:

// staging/src/k8s.io/client-go/tools/cache/thread_safe_store.go 

// Index returns a list of items that match the given object on the index function.
// Index is thread-safe so long as you treat all items as immutable.
// 通过指定的索引器和对象获取符合这个对象特征的所有对象
func (c *threadSafeMap) Index(indexName string, obj interface{}) ([]interface{}, error) {
c.lock.RLock()
defer c.lock.RUnlock()
// 获得索引器 indexName 的索引键计算函数
indexFunc := c.indexers[indexName]
if indexFunc == nil {
return nil, fmt.Errorf("Index with name %s does not exist", indexName)
} // 获取指定 obj 对象的索引键
indexedValues, err := indexFunc(obj)
if err != nil {
return nil, err
}
// 获取索引器,indexName的所有索引
index := c.indices[indexName] // 用来存储对象键的集合
var storeKeySet sets.String
if len(indexedValues) == 1 {
// In majority of cases, there is exactly one value matching.
// Optimize the most common path - deduping is not needed here.
// 大多数情况下只有一个值匹配(默认获取的索引键就是对象的 namespace)
// 直接拿到这个索引键的对象键集合
storeKeySet = index[indexedValues[0]]
} else {
// Need to de-dupe the return list.
// Since multiple keys are allowed, this can happen.
// 由于有多个索引键,则可能有重复的对象键出现,索引需要去重
storeKeySet = sets.String{}
// 循环索引键
for _, indexedValue := range indexedValues {
// 循环索引键下面的对象键,因为要去重
for key := range index[indexedValue] {
storeKeySet.Insert(key)
}
}
} // 拿到所有的对象键集合过后,循环拿到所有的对象集合
list := make([]interface{}, 0, storeKeySet.Len())
for storeKey := range storeKeySet {
list = append(list, c.items[storeKey])
}
return list, nil
}

这个 Index 函数就是获取一个指定对象的索引键,然后把这个索引键下面的所有的对象全部获取到,比如我们要获取一个 Pod 所在命名空间下面的所有 Pod,如果更抽象一点,就是符合对象某些特征的所有对象,而这个特征就是我们指定的索引键函数计算出来的。然后接下来就是一个比较重要的 ByIndex 函数的实现:

// staging/src/k8s.io/client-go/tools/cache/thread_safe_store.go 

// ByIndex returns a list of the items whose indexed values in the given index include the given indexed value
// 和上面的 Index 函数类似,这里只是直接指定了索引键
func (c *threadSafeMap) ByIndex(indexName, indexedValue string) ([]interface{}, error) {
c.lock.RLock()
defer c.lock.RUnlock() // 获得索引器 indexName 的索引键计算函数
indexFunc := c.indexers[indexName]
if indexFunc == nil {
return nil, fmt.Errorf("Index with name %s does not exist", indexName)
} // 获得索引器 indexName 的所有索引
index := c.indices[indexName] // 获取指定索引键的所有对象键
set := index[indexedValue]
// 根据对象键遍历获取对象
list := make([]interface{}, 0, set.Len())
for key := range set {
list = append(list, c.items[key])
} return list, nil
}

可以很清楚地看到 ByIndex 函数和 Index 函数比较类似,但是更简单了,直接获取一个指定的索引键的全部资源对象。然后是其他几个索引相关的函数:

// staging/src/k8s.io/client-go/tools/cache/thread_safe_store.go 

// IndexKeys returns a list of the Store keys of the objects whose indexed values in the given index include the given indexed value.
// IndexKeys is thread-safe so long as you treat all items as immutable.
// IndexKeys 和上面的 ByIndex 几乎是一样的,只是这里是直接返回对象键列表
func (c *threadSafeMap) IndexKeys(indexName, indexedValue string) ([]string, error) {
c.lock.RLock()
defer c.lock.RUnlock()
// 获取索引器 indexName 的索引键计算函数
indexFunc := c.indexers[indexName]
if indexFunc == nil {
return nil, fmt.Errorf("Index with name %s does not exist", indexName)
}
// 获取索引器 indexName 的所有索引
index := c.indices[indexName]
// 直接获取指定索引键的对象键集合
set := index[indexedValue]
return set.List(), nil
} // ListIndexFuncValues 获取索引器下面的所有索引键
func (c *threadSafeMap) ListIndexFuncValues(indexName string) []string {
c.lock.RLock()
defer c.lock.RUnlock() // 获取索引器 indexName 的所有索引
index := c.indices[indexName]
names := make([]string, 0, len(index))
// 遍历索引得到索引键
for key := range index {
names = append(names, key)
}
return names
} // GetIndexers 直接返回 indexers
func (c *threadSafeMap) GetIndexers() Indexers {
return c.indexers
} // AddIndexers 添加一个新的 Indexers
func (c *threadSafeMap) AddIndexers(newIndexers Indexers) error {
c.lock.Lock()
defer c.lock.Unlock() if len(c.items) > 0 {
return fmt.Errorf("cannot add indexers to running index")
} // 获取旧的索引器和新的索引器keys
oldKeys := sets.StringKeySet(c.indexers)
newKeys := sets.StringKeySet(newIndexers) // 如果包含新的索引器,则提示冲突
if oldKeys.HasAny(newKeys.List()...) {
return fmt.Errorf("indexer conflict: %v", oldKeys.Intersection(newKeys))
} // 将新的索引器添加到 Indexers 中
for k, v := range newIndexers {
c.indexers[k] = v
}
return nil
}

这里我们就将 ThreadSafeMap 的实现进行了分析说明。整体来说比较方便,一个就是将对象数据存入到一个 map 中,然后就是维护索引,方便根据索引来查找到对应的对象。

cache

接下来再回过头去看 cache 的实现就非常简单了,因为 cache 就是对 ThreadSafeStore 的一个再次封装,很多操作都是直接调用的 ThreadSafeStore 的操作实现的,如下所示:

// staging/src/k8s.io/client-go/tools/cache/store.go

// Add inserts an item into the cache.
// Add 插入一个元素到 cache 中
func (c *cache) Add(obj interface{}) error {
key, err := c.keyFunc(obj)
if err != nil {
return KeyError{obj, err}
}
// 将对象添加到底层的 ThreadSafeStore 中
c.cacheStorage.Add(key, obj)
return nil
} // Update sets an item in the cache to its updated state.
// 更新cache 中的对象
func (c *cache) Update(obj interface{}) error {
key, err := c.keyFunc(obj)
if err != nil {
return KeyError{obj, err}
}
c.cacheStorage.Update(key, obj)
return nil
} // Delete removes an item from the cache.
// 删除cache中的对象
func (c *cache) Delete(obj interface{}) error {
key, err := c.keyFunc(obj)
if err != nil {
return KeyError{obj, err}
}
c.cacheStorage.Delete(key)
return nil
} // List returns a list of all the items.
// List is completely threadsafe as long as you treat all items as immutable.
// 获取cache中所有的对象
func (c *cache) List() []interface{} {
return c.cacheStorage.List()
} // ListKeys returns a list of all the keys of the objects currently
// in the cache.
// 获取cache中所有的对象键
func (c *cache) ListKeys() []string {
return c.cacheStorage.ListKeys()
} // GetIndexers returns the indexers of cache
// 得到cache中的Indexers
func (c *cache) GetIndexers() Indexers {
return c.cacheStorage.GetIndexers()
} // Index returns a list of items that match on the index function
// Index is thread-safe so long as you treat all items as immutable
// 得到对象obj与indexName索引器关联的所有对象
func (c *cache) Index(indexName string, obj interface{}) ([]interface{}, error) {
return c.cacheStorage.Index(indexName, obj)
} func (c *cache) IndexKeys(indexName, indexKey string) ([]string, error) {
return c.cacheStorage.IndexKeys(indexName, indexKey)
} // ListIndexFuncValues returns the list of generated values of an Index func
func (c *cache) ListIndexFuncValues(indexName string) []string {
return c.cacheStorage.ListIndexFuncValues(indexName)
} func (c *cache) ByIndex(indexName, indexKey string) ([]interface{}, error) {
return c.cacheStorage.ByIndex(indexName, indexKey)
} func (c *cache) AddIndexers(newIndexers Indexers) error {
return c.cacheStorage.AddIndexers(newIndexers)
} // Get returns the requested item, or sets exists=false.
// Get is completely threadsafe as long as you treat all items as immutable.
func (c *cache) Get(obj interface{}) (item interface{}, exists bool, err error) {
key, err := c.keyFunc(obj)
if err != nil {
return nil, false, KeyError{obj, err}
}
return c.GetByKey(key)
} // GetByKey returns the request item, or exists=false.
// GetByKey is completely threadsafe as long as you treat all items as immutable.
func (c *cache) GetByKey(key string) (item interface{}, exists bool, err error) {
item, exists = c.cacheStorage.Get(key)
return item, exists, nil
} // Replace will delete the contents of 'c', using instead the given list.
// 'c' takes ownership of the list, you should not reference the list again
// after calling this function.
// 替换cache中所有的对象
func (c *cache) Replace(list []interface{}, resourceVersion string) error {
items := make(map[string]interface{}, len(list))
for _, item := range list {
key, err := c.keyFunc(item)
if err != nil {
return KeyError{item, err}
}
items[key] = item
}
c.cacheStorage.Replace(items, resourceVersion)
return nil
} // Resync is meaningless for one of these
func (c *cache) Resync() error {
return nil
}

可以看到 cache 没有自己独特的实现方式,都是调用的包含的 ThreadSafeStore 操作接口。

总结

Reflector 通过 ListAndWatch 把数据传入 DeltaFIFO 后,经过 DeltaFIFO 的 Pop 函数将资源对象存入到了本地的一个存储 Indexer 中,而这个底层真正的存储其实就是上面的 ThreadSafeStore。

要理解 Indexer 组件,最主要就是要把索引、索引器(索引分类)、索引键、对象键这几个概念弄清楚,有时候确实容易混乱,我们将上面的示例理解了应该就很好理解了,我们可以简单的理解为 Indexer 就是简单的把相同命名空间的对象放在一个集合中,然后基于命名空间来查找对象。

【转载】Indexer 源码分析的更多相关文章

  1. k8s client-go源码分析 informer源码分析(6)-Indexer源码分析

    client-go之Indexer源码分析 1.Indexer概述 Indexer中有informer维护的指定资源对象的相对于etcd数据的一份本地内存缓存,可通过该缓存获取资源对象,以减少对api ...

  2. [转载]URL 源码分析

    URI 引用包括最多三个部分:模式.模式特定部分和片段标识符.一般为: 模式:模式特定部分:片段 如果省略模式,这个URI引用则是相对的.如果省略片段标识符,这个URI引用就是一个纯URI. URI是 ...

  3. [转载]URI 源码分析

    需要提前了解下什么是URI,及URI和URL的区别: URI. URL 和 URN 的区别 URI 引用包括最多三个部分:模式.模式特定部分和片段标识符.一般为: 模式:模式特定部分:片段 如果省略模 ...

  4. [转载] Netty源码分析

    转载自http://blog.csdn.net/kobejayandy/article/details/11836813 Netty提供异步的.事件驱动的网络应用程序框架和工具,用以快速开发高性能.高 ...

  5. ArrayList实现原理及源码分析之JDK8

    转载 ArrayList源码分析 一.ArrayList介绍 Java 集合框架主要包括两种类型的容器: 一种是集合(Collection),存储一个元素集合. 一种是图(Map),存储键/值对映射. ...

  6. Kubernetes client-go Indexer / ThreadSafeStore 源码分析

    Kubernetes client-go Indexer / ThreadSafeStore 源码分析   请阅读原文:原文地址   Contents 概述 Indexer 接口 ThreadSafe ...

  7. 转载-HashMap1.7源码分析

    原文地址-https://www.cnblogs.com/chengxiao/p/6059914.html HashMap实现原理及源码分析   哈希表(hash table)也叫散列表,是一种非常重 ...

  8. EasyUI学习总结(三)——easyloader源码分析(转载)

    声明:这一篇文章是转载过来的,转载地址忘记了,原作者如果看到了,希望能够告知一声,我好加上去! easyloader模块是用来加载jquery easyui的js和css文件的,而且它可以分析模块的依 ...

  9. jQuery源码分析系列(转载来源Aaron.)

    声明:非本文原创文章,转载来源原文链接Aaron. 版本截止到2013.8.24 jQuery官方发布最新的的2.0.3为准 附上每一章的源码注释分析 :https://github.com/JsAa ...

  10. ArrayList源码分析超详细(转载)

    ArrayList源码分析超详细   ArrayList源码分析超详解 想要分析下源码是件好事,但是如何去进行分析呢?以我的例子来说,我进行源码分析的过程如下几步: 找到类:利用 IDEA 找到所需要 ...

随机推荐

  1. Containerd 配置使用 Nvidia container runtime

    前言 Kubernetes 集群中 Docker 如何使用 GPU,请看这一篇 docker配置Nvidia环境,使用GPU 本文着重讲 Containerd 如何作为容器运行时来使用 GPU CRI ...

  2. Golang Linux、Windows、Mac 下交叉编译

    前言 Golang 支持交叉编译, 即同一份代码,在一个平台上生成,然后可以在另外一个平台去执行. 之前写过一篇 Golang windows下 交叉编译 感觉写的不够全面,这篇作为补充. 交叉编译 ...

  3. map_server 中障碍物计算规则

    博客地址:https://www.cnblogs.com/zylyehuo/ 参考链接 Autolabor-ROS机器人入门课程<ROS理论与实践> 1.地图中的每一个像素取值在 [0,2 ...

  4. CentOS 版本选择DVD、Everything、LiveCD、Minimal、NetInstall

    CentOS 7.X,主要是下载的时候有很多版本供选择,如何选择? DVD版:这个是常用版本,就是普通安装版了,推荐大家安装.里面包含大量的常用软件,大部分情况下安装时无需再在线下载,体积为4G.Ev ...

  5. 张高兴的大模型开发实战:(三)使用 LangGraph 为对话添加历史记录

    目录 基础概念 环境搭建与配置 将对话历史存储至内存 将对话历史存储至 PostgreSQL 在构建聊天机器人时,对话历史记录是提升用户体验的核心功能之一,用户希望机器人能够记住之前的对话内容,从而避 ...

  6. 【数据库】Java实体类的属性类型与数据库表字段类型对应表

    JDBC类型与Java类型 JDBC类型 Java Object类型 CHAR java.lang.String VARCHAR java.lang.String LONGVARCHAR java.l ...

  7. 【完结】【一本通提高】KMP做题记录

    题目编号 标题 估分 正确 提交 Y 2076 Problem  A [一本通提高篇KMP]剪花布条 --- 156 293 Y 2077 Problem  B [一本通提高篇KMP]Radio Tr ...

  8. 2025dsfzB层考试总结

    2025年东师附中B层春季测试1考试总结 Designed By Lwj54joy AT 2025-03-01 本文将遵守GNU GPL2.0开源协议. 概述 总分:\(310/400\) 改题分数: ...

  9. kubelet 创建 Pod 前发生了什么?

    Kubelet Watch 到新增的 Pod,需要做的主要有以下几件事: 管理 Pod 状态,除了更新本地缓存,还要同步给 API server 计算节点的资源是否足够创建 Pod 创建 Cgroup ...

  10. 《Universal Number Posit Arithmetic Generator on FPGA》(一)

    A parameterized Verilog HDL is constructed for each unit which takes posit word size (N) and posit e ...