教程集 www.jiaochengji.com
教程集 >  Golang编程  >  golang教程  >  正文 client-go 之 Indexer 的理解

client-go 之 Indexer 的理解

发布时间:2022-02-13   编辑:jiaochengji.com
教程集为您提供client-go 之 Indexer 的理解等资源,欢迎您收藏本站,我们将为您提供最新的client-go 之 Indexer 的理解资源

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

<pre style="margin-top:10px;margin-bottom:10px;padding:0px;max-width:100%;"><span style="margin:0px 0px -7px;padding:0px;max-width:100%;background-image:url("https://mmbiz.qpic.cn/mmbiz_png/z9BgVMEm7YtQQPoYe5dS4oUtdKQMB6Xo8l8uZLXrwJ1JJeF4zaCLpgyDuNTjU1gcv0o0VpCerB9zl7ics532hpw/640?wx_fmt=png");background-position:10px 10px;background-repeat:no-repeat;height:30px;width:524px;"/><code style="margin:0px;padding:15px 16px 16px;max-width:100%;color:rgb(171,178,191);font-family:'Operator Mono', Consolas, Monaco, Menlo, monospace;font-size:12px;"><span style="margin:0px;padding:0px;max-width:100%;color:rgb(92,99,112);font-style:italic;line-height:26px;">// k8s.io/client-go/tools/cache/indexer.go</span>

<span style="margin:0px;padding:0px;max-width:100%;color:rgb(92,99,112);font-style:italic;line-height:26px;">// Indexer 使用多个索引扩展了 Store,并限制了每个累加器只能容纳当前对象</span>
<span style="margin:0px;padding:0px;max-width:100%;color:rgb(92,99,112);font-style:italic;line-height:26px;">// 这里有3种字符串需要说明:</span>
<span style="margin:0px;padding:0px;max-width:100%;color:rgb(92,99,112);font-style:italic;line-height:26px;">// 1. 一个存储键,在 Store 接口中定义(其实就是对象键)</span>
<span style="margin:0px;padding:0px;max-width:100%;color:rgb(92,99,112);font-style:italic;line-height:26px;">// 2. 一个索引的名称(相当于索引分类名称)</span>
<span style="margin:0px;padding:0px;max-width:100%;color:rgb(92,99,112);font-style:italic;line-height:26px;">// 3. 索引键,由 IndexFunc 生成,可以是一个字段值或从对象中计算出来的任何字符串</span>
<span style="margin:0px;padding:0px;max-width:100%;color:rgb(198,120,221);line-height:26px;">type</span> Indexer <span style="margin:0px;padding:0px;max-width:100%;color:rgb(198,120,221);line-height:26px;">interface</span> {
 Store  <span style="margin:0px;padding:0px;max-width:100%;color:rgb(92,99,112);font-style:italic;line-height:26px;">// 继承了 Store 存储接口,所以说 Indexer 也是存储</span>
 <span style="margin:0px;padding:0px;max-width:100%;color:rgb(92,99,112);font-style:italic;line-height:26px;">// indexName 是索引类名称,obj 是对象,计算 obj 在 indexName 索引类中的索引键,然后通过索引键把所有的对象取出来</span>
  <span style="margin:0px;padding:0px;max-width:100%;color:rgb(92,99,112);font-style:italic;line-height:26px;">// 获取 obj 对象在索引类中的索引键相匹配的对象</span>
 Index(indexName <span style="margin:0px;padding:0px;max-width:100%;color:rgb(198,120,221);line-height:26px;">string</span>, obj <span style="margin:0px;padding:0px;max-width:100%;color:rgb(198,120,221);line-height:26px;">interface</span>{}) ([]<span style="margin:0px;padding:0px;max-width:100%;color:rgb(198,120,221);line-height:26px;">interface</span>{}, error)
 <span style="margin:0px;padding:0px;max-width:100%;color:rgb(92,99,112);font-style:italic;line-height:26px;">// indexKey 是 indexName 索引分类中的一个索引键</span>
  <span style="margin:0px;padding:0px;max-width:100%;color:rgb(92,99,112);font-style:italic;line-height:26px;">// 函数返回 indexKey 指定的所有对象键 IndexKeys </span>
 IndexKeys(indexName, indexedValue <span style="margin:0px;padding:0px;max-width:100%;color:rgb(198,120,221);line-height:26px;">string</span>) ([]<span style="margin:0px;padding:0px;max-width:100%;color:rgb(198,120,221);line-height:26px;">string</span>, error)
 ListIndexFuncValues(indexName <span style="margin:0px;padding:0px;max-width:100%;color:rgb(198,120,221);line-height:26px;">string</span>) []<span style="margin:0px;padding:0px;max-width:100%;color:rgb(198,120,221);line-height:26px;">string</span>
 ByIndex(indexName, indexedValue <span style="margin:0px;padding:0px;max-width:100%;color:rgb(198,120,221);line-height:26px;">string</span>) ([]<span style="margin:0px;padding:0px;max-width:100%;color:rgb(198,120,221);line-height:26px;">interface</span>{}, error)
 GetIndexers() Indexers
 <span style="margin:0px;padding:0px;max-width:100%;color:rgb(92,99,112);font-style:italic;line-height:26px;">// 添加新的索引在存储中</span>
 AddIndexers(newIndexers Indexers) error
}
</code></pre><h2 style="margin:2.2em 0px 35px;padding:0px;font-size:22px;max-width:100%;line-height:1.5em;"><span style="margin:0px 3px 0px 0px;padding:2px 13px;max-width:100%;color:rgb(81,81,81);height:37px;">Indexer</span></h2>

在去查看 Indexer 的接口具体实现之前,我们需要了解 Indexer 中几个非常重要的概念:<code style="margin:3px;padding:3px;max-width:100%;font-size:14px;font-family:'Operator Mono', Consolas, Monaco, Menlo, monospace;color:rgb(155,110,35);background-color:rgb(255,245,227);">Indices</code>、<code style="margin:3px;padding:3px;max-width:100%;font-size:14px;font-family:'Operator Mono', Consolas, Monaco, Menlo, monospace;color:rgb(155,110,35);background-color:rgb(255,245,227);">Index</code>、<code style="margin:3px;padding:3px;max-width:100%;font-size:14px;font-family:'Operator Mono', Consolas, Monaco, Menlo, monospace;color:rgb(155,110,35);background-color:rgb(255,245,227);">Indexers</code> 及 <code style="margin:3px;padding:3px;max-width:100%;font-size:14px;font-family:'Operator Mono', Consolas, Monaco, Menlo, monospace;color:rgb(155,110,35);background-color:rgb(255,245,227);">IndexFunc</code>。

<pre style="margin-top:10px;margin-bottom:10px;padding:0px;max-width:100%;"><span style="margin:0px 0px -7px;padding:0px;max-width:100%;background-image:url("https://mmbiz.qpic.cn/mmbiz_png/z9BgVMEm7YtQQPoYe5dS4oUtdKQMB6Xo8l8uZLXrwJ1JJeF4zaCLpgyDuNTjU1gcv0o0VpCerB9zl7ics532hpw/640?wx_fmt=png");background-position:10px 10px;background-repeat:no-repeat;height:30px;width:524px;"/><code style="margin:0px;padding:15px 16px 16px;max-width:100%;color:rgb(171,178,191);font-family:'Operator Mono', Consolas, Monaco, Menlo, monospace;font-size:12px;"><span style="margin:0px;padding:0px;max-width:100%;color:rgb(92,99,112);font-style:italic;line-height:26px;">// k8s.io/client-go/tools/cache/indexer.go</span>

<span style="margin:0px;padding:0px;max-width:100%;color:rgb(92,99,112);font-style:italic;line-height:26px;">// 用于计算一个对象的索引键集合</span>
<span style="margin:0px;padding:0px;max-width:100%;color:rgb(198,120,221);line-height:26px;">type</span> IndexFunc <span style="margin:0px;padding:0px;max-width:100%;line-height:26px;"><span style="margin:0px;padding:0px;max-width:100%;color:rgb(198,120,221);line-height:26px;">func</span><span style="margin:0px;padding:0px;max-width:100%;line-height:26px;">(obj <span style="margin:0px;padding:0px;max-width:100%;color:rgb(198,120,221);line-height:26px;">interface</span>{})</span> <span style="margin:0px;padding:0px;max-width:100%;line-height:26px;">([]<span style="margin:0px;padding:0px;max-width:100%;color:rgb(198,120,221);line-height:26px;">string</span>, error)</span>

// 索引键与对象键集合的映射
<span style="margin:0px;padding:0px;max-width:100%;color:rgb(97,174,238);line-height:26px;">type</span> <span style="margin:0px;padding:0px;max-width:100%;color:rgb(97,174,238);line-height:26px;">Index</span> <span style="margin:0px;padding:0px;max-width:100%;color:rgb(97,174,238);line-height:26px;">map</span>[<span style="margin:0px;padding:0px;max-width:100%;color:rgb(97,174,238);line-height:26px;">string</span>]<span style="margin:0px;padding:0px;max-width:100%;color:rgb(97,174,238);line-height:26px;">sets</span>.<span style="margin:0px;padding:0px;max-width:100%;color:rgb(97,174,238);line-height:26px;">String</span>

// 索引器名称(或者索引分类)与 <span style="margin:0px;padding:0px;max-width:100%;color:rgb(97,174,238);line-height:26px;">IndexFunc</span> 的映射,相当于存储索引的各种分类
<span style="margin:0px;padding:0px;max-width:100%;color:rgb(97,174,238);line-height:26px;">type</span> <span style="margin:0px;padding:0px;max-width:100%;color:rgb(97,174,238);line-height:26px;">Indexers</span> <span style="margin:0px;padding:0px;max-width:100%;color:rgb(97,174,238);line-height:26px;">map</span>[<span style="margin:0px;padding:0px;max-width:100%;color:rgb(97,174,238);line-height:26px;">string</span>]<span style="margin:0px;padding:0px;max-width:100%;color:rgb(97,174,238);line-height:26px;">IndexFunc</span>

// 索引器名称与 <span style="margin:0px;padding:0px;max-width:100%;color:rgb(97,174,238);line-height:26px;">Index</span> 索引的映射
<span style="margin:0px;padding:0px;max-width:100%;color:rgb(97,174,238);line-height:26px;">type</span> <span style="margin:0px;padding:0px;max-width:100%;color:rgb(97,174,238);line-height:26px;">Indices</span> <span style="margin:0px;padding:0px;max-width:100%;color:rgb(97,174,238);line-height:26px;">map</span>[<span style="margin:0px;padding:0px;max-width:100%;color:rgb(97,174,238);line-height:26px;">string</span>]<span style="margin:0px;padding:0px;max-width:100%;color:rgb(97,174,238);line-height:26px;">Index</span>
</span></code></pre>

这4个数据结构的命名非常容易让大家混淆,直接查看源码也不是那么容易的。这里我们来仔细解释下。首先什么叫索引,索引就是为了快速查找的,比如我们需要查找某个节点上的所有 Pod,那就让 Pod 按照节点名称排序列举出来,对应的就是 Index 这个类型,具体的就是 <code style="margin:3px;padding:3px;max-width:100%;font-size:14px;font-family:'Operator Mono', Consolas, Monaco, Menlo, monospace;color:rgb(155,110,35);background-color:rgb(255,245,227);">map[node]sets.pod</code>,但是如何去查找可以有多种方式,就是上面的 Indexers 这个类型的作用。我们可以用一个比较具体的示例来解释他们的关系和含义,如下所示:

<pre style="margin-top:10px;margin-bottom:10px;padding:0px;max-width:100%;"><span style="margin:0px 0px -7px;padding:0px;max-width:100%;background-image:url("https://mmbiz.qpic.cn/mmbiz_png/z9BgVMEm7YtQQPoYe5dS4oUtdKQMB6Xo8l8uZLXrwJ1JJeF4zaCLpgyDuNTjU1gcv0o0VpCerB9zl7ics532hpw/640?wx_fmt=png");background-position:10px 10px;background-repeat:no-repeat;height:30px;width:524px;"/><code style="margin:0px;padding:15px 16px 16px;max-width:100%;color:rgb(171,178,191);font-family:'Operator Mono', Consolas, Monaco, Menlo, monospace;font-size:12px;"><span style="margin:0px;padding:0px;max-width:100%;color:rgb(198,120,221);line-height:26px;">package</span> main

<span style="margin:0px;padding:0px;max-width:100%;color:rgb(198,120,221);line-height:26px;">import</span> (
 <span style="margin:0px;padding:0px;max-width:100%;color:rgb(152,195,121);line-height:26px;">"fmt"</span>

 v1 <span style="margin:0px;padding:0px;max-width:100%;color:rgb(152,195,121);line-height:26px;">"k8s.io/api/core/v1"</span>
 <span style="margin:0px;padding:0px;max-width:100%;color:rgb(152,195,121);line-height:26px;">"k8s.io/apimachinery/pkg/api/meta"</span>
 metav1 <span style="margin:0px;padding:0px;max-width:100%;color:rgb(152,195,121);line-height:26px;">"k8s.io/apimachinery/pkg/apis/meta/v1"</span>
 <span style="margin:0px;padding:0px;max-width:100%;color:rgb(152,195,121);line-height:26px;">"k8s.io/client-go/tools/cache"</span>
)

<span style="margin:0px;padding:0px;max-width:100%;color:rgb(198,120,221);line-height:26px;">const</span> (
 NamespaceIndexName = <span style="margin:0px;padding:0px;max-width:100%;color:rgb(152,195,121);line-height:26px;">"namespace"</span>
 NodeNameIndexName  = <span style="margin:0px;padding:0px;max-width:100%;color:rgb(152,195,121);line-height:26px;">"nodeName"</span>
)

<span style="margin:0px;padding:0px;max-width:100%;line-height:26px;"><span style="margin:0px;padding:0px;max-width:100%;color:rgb(198,120,221);line-height:26px;">func</span> <span style="margin:0px;padding:0px;max-width:100%;color:rgb(97,174,238);line-height:26px;">NamespaceIndexFunc</span><span style="margin:0px;padding:0px;max-width:100%;line-height:26px;">(obj <span style="margin:0px;padding:0px;max-width:100%;color:rgb(198,120,221);line-height:26px;">interface</span>{})</span> <span style="margin:0px;padding:0px;max-width:100%;line-height:26px;">([]<span style="margin:0px;padding:0px;max-width:100%;color:rgb(198,120,221);line-height:26px;">string</span>, error)</span></span> {
 m, err := meta.Accessor(obj)
 <span style="margin:0px;padding:0px;max-width:100%;color:rgb(198,120,221);line-height:26px;">if</span> err != <span style="margin:0px;padding:0px;max-width:100%;color:rgb(86,182,194);line-height:26px;">nil</span> {
  <span style="margin:0px;padding:0px;max-width:100%;color:rgb(198,120,221);line-height:26px;">return</span> []<span style="margin:0px;padding:0px;max-width:100%;color:rgb(198,120,221);line-height:26px;">string</span>{<span style="margin:0px;padding:0px;max-width:100%;color:rgb(152,195,121);line-height:26px;">""</span>}, fmt.Errorf(<span style="margin:0px;padding:0px;max-width:100%;color:rgb(152,195,121);line-height:26px;">"object has no meta: %v"</span>, err)
 }
 <span style="margin:0px;padding:0px;max-width:100%;color:rgb(198,120,221);line-height:26px;">return</span> []<span style="margin:0px;padding:0px;max-width:100%;color:rgb(198,120,221);line-height:26px;">string</span>{m.GetNamespace()}, <span style="margin:0px;padding:0px;max-width:100%;color:rgb(86,182,194);line-height:26px;">nil</span>
}

<span style="margin:0px;padding:0px;max-width:100%;line-height:26px;"><span style="margin:0px;padding:0px;max-width:100%;color:rgb(198,120,221);line-height:26px;">func</span> <span style="margin:0px;padding:0px;max-width:100%;color:rgb(97,174,238);line-height:26px;">NodeNameIndexFunc</span><span style="margin:0px;padding:0px;max-width:100%;line-height:26px;">(obj <span style="margin:0px;padding:0px;max-width:100%;color:rgb(198,120,221);line-height:26px;">interface</span>{})</span> <span style="margin:0px;padding:0px;max-width:100%;line-height:26px;">([]<span style="margin:0px;padding:0px;max-width:100%;color:rgb(198,120,221);line-height:26px;">string</span>, error)</span></span> {
 pod, ok := obj.(*v1.Pod)
 <span style="margin:0px;padding:0px;max-width:100%;color:rgb(198,120,221);line-height:26px;">if</span> !ok {
  <span style="margin:0px;padding:0px;max-width:100%;color:rgb(198,120,221);line-height:26px;">return</span> []<span style="margin:0px;padding:0px;max-width:100%;color:rgb(198,120,221);line-height:26px;">string</span>{}, <span style="margin:0px;padding:0px;max-width:100%;color:rgb(86,182,194);line-height:26px;">nil</span>
 }
 <span style="margin:0px;padding:0px;max-width:100%;color:rgb(198,120,221);line-height:26px;">return</span> []<span style="margin:0px;padding:0px;max-width:100%;color:rgb(198,120,221);line-height:26px;">string</span>{pod.Spec.NodeName}, <span style="margin:0px;padding:0px;max-width:100%;color:rgb(86,182,194);line-height:26px;">nil</span>
}

<span style="margin:0px;padding:0px;max-width:100%;line-height:26px;"><span style="margin:0px;padding:0px;max-width:100%;color:rgb(198,120,221);line-height:26px;">func</span> <span style="margin:0px;padding:0px;max-width:100%;color:rgb(97,174,238);line-height:26px;">main</span><span style="margin:0px;padding:0px;max-width:100%;line-height:26px;">()</span></span> {
 index := cache.NewIndexer(cache.MetaNamespaceKeyFunc, cache.Indexers{
  NamespaceIndexName: NamespaceIndexFunc,
  NodeNameIndexName:  NodeNameIndexFunc,
 })

 pod1 := &v1.Pod{
  ObjectMeta: metav1.ObjectMeta{
   Name:      <span style="margin:0px;padding:0px;max-width:100%;color:rgb(152,195,121);line-height:26px;">"index-pod-1"</span>,
   Namespace: <span style="margin:0px;padding:0px;max-width:100%;color:rgb(152,195,121);line-height:26px;">"default"</span>,
  },
  Spec: v1.PodSpec{NodeName: <span style="margin:0px;padding:0px;max-width:100%;color:rgb(152,195,121);line-height:26px;">"node1"</span>},
 }
 pod2 := &v1.Pod{
  ObjectMeta: metav1.ObjectMeta{
   Name:      <span style="margin:0px;padding:0px;max-width:100%;color:rgb(152,195,121);line-height:26px;">"index-pod-2"</span>,
   Namespace: <span style="margin:0px;padding:0px;max-width:100%;color:rgb(152,195,121);line-height:26px;">"default"</span>,
  },
  Spec: v1.PodSpec{NodeName: <span style="margin:0px;padding:0px;max-width:100%;color:rgb(152,195,121);line-height:26px;">"node2"</span>},
 }
 pod3 := &v1.Pod{
  ObjectMeta: metav1.ObjectMeta{
   Name:      <span style="margin:0px;padding:0px;max-width:100%;color:rgb(152,195,121);line-height:26px;">"index-pod-3"</span>,
   Namespace: <span style="margin:0px;padding:0px;max-width:100%;color:rgb(152,195,121);line-height:26px;">"kube-system"</span>,
  },
  Spec: v1.PodSpec{NodeName: <span style="margin:0px;padding:0px;max-width:100%;color:rgb(152,195,121);line-height:26px;">"node2"</span>},
 }

 _ = index.Add(pod1)
 _ = index.Add(pod2)
 _ = index.Add(pod3)

 <span style="margin:0px;padding:0px;max-width:100%;color:rgb(92,99,112);font-style:italic;line-height:26px;">// ByIndex 两个参数:IndexName(索引器名称)和 indexKey(需要检索的key)</span>
 pods, err := index.ByIndex(NamespaceIndexName, <span style="margin:0px;padding:0px;max-width:100%;color:rgb(152,195,121);line-height:26px;">"default"</span>)
 <span style="margin:0px;padding:0px;max-width:100%;color:rgb(198,120,221);line-height:26px;">if</span> err != <span style="margin:0px;padding:0px;max-width:100%;color:rgb(86,182,194);line-height:26px;">nil</span> {
  <span style="margin:0px;padding:0px;max-width:100%;color:rgb(230,192,123);line-height:26px;">panic</span>(err)
 }
 <span style="margin:0px;padding:0px;max-width:100%;color:rgb(198,120,221);line-height:26px;">for</span> _, pod := <span style="margin:0px;padding:0px;max-width:100%;color:rgb(198,120,221);line-height:26px;">range</span> pods {
  fmt.Println(pod.(*v1.Pod).Name)
 }

 fmt.Println(<span style="margin:0px;padding:0px;max-width:100%;color:rgb(152,195,121);line-height:26px;">"=========================="</span>)

 pods, err = index.ByIndex(NodeNameIndexName, <span style="margin:0px;padding:0px;max-width:100%;color:rgb(152,195,121);line-height:26px;">"node2"</span>)
 <span style="margin:0px;padding:0px;max-width:100%;color:rgb(198,120,221);line-height:26px;">if</span> err != <span style="margin:0px;padding:0px;max-width:100%;color:rgb(86,182,194);line-height:26px;">nil</span> {
  <span style="margin:0px;padding:0px;max-width:100%;color:rgb(230,192,123);line-height:26px;">panic</span>(err)
 }
 <span style="margin:0px;padding:0px;max-width:100%;color:rgb(198,120,221);line-height:26px;">for</span> _, pod := <span style="margin:0px;padding:0px;max-width:100%;color:rgb(198,120,221);line-height:26px;">range</span> pods {
  fmt.Println(pod.(*v1.Pod).Name)
 }

}

<span style="margin:0px;padding:0px;max-width:100%;color:rgb(92,99,112);font-style:italic;line-height:26px;">// 输出结果为:</span>
index-pod<span style="margin:0px;padding:0px;max-width:100%;color:rgb(209,154,102);line-height:26px;">-1</span>
index-pod<span style="margin:0px;padding:0px;max-width:100%;color:rgb(209,154,102);line-height:26px;">-2</span>
==========================
index-pod<span style="margin:0px;padding:0px;max-width:100%;color:rgb(209,154,102);line-height:26px;">-2</span>
index-pod<span style="margin:0px;padding:0px;max-width:100%;color:rgb(209,154,102);line-height:26px;">-3</span>
</code></pre>

在上面的示例中首先通过 NewIndexer 函数实例化 Indexer 对象,第一个参数就是用于计算资源对象键的函数,这里我们使用的是 <code style="margin:3px;padding:3px;max-width:100%;font-size:14px;font-family:'Operator Mono', Consolas, Monaco, Menlo, monospace;color:rgb(155,110,35);background-color:rgb(255,245,227);">MetaNamespaceKeyFunc</code> 这个默认的对象键函数;第二个参数是 Indexers,也就是存储索引器,上面我们知道 <code style="margin:3px;padding:3px;max-width:100%;font-size:14px;font-family:'Operator Mono', Consolas, Monaco, Menlo, monospace;color:rgb(155,110,35);background-color:rgb(255,245,227);">Indexers</code> 的定义为 <code style="margin:3px;padding:3px;max-width:100%;font-size:14px;font-family:'Operator Mono', Consolas, Monaco, Menlo, monospace;color:rgb(155,110,35);background-color:rgb(255,245,227);">map[string]IndexFunc</code>,为什么要定义成一个 map 呢?我们可以类比数据库中,我们要查询某项数据,索引的方式是不是多种多样啊?为了扩展,Kubernetes 中就使用一个 map 来存储各种各样的存储索引器,至于存储索引器如何生成,就使用一个 <code style="margin:3px;padding:3px;max-width:100%;font-size:14px;font-family:'Operator Mono', Consolas, Monaco, Menlo, monospace;color:rgb(155,110,35);background-color:rgb(255,245,227);">IndexFunc</code> 暴露出去,给使用者自己实现即可。

这里我们定义的了两个索引键生成函数:<code style="margin:3px;padding:3px;max-width:100%;font-size:14px;font-family:'Operator Mono', Consolas, Monaco, Menlo, monospace;color:rgb(155,110,35);background-color:rgb(255,245,227);">NamespaceIndexFunc</code> 与 <code style="margin:3px;padding:3px;max-width:100%;font-size:14px;font-family:'Operator Mono', Consolas, Monaco, Menlo, monospace;color:rgb(155,110,35);background-color:rgb(255,245,227);">NodeNameIndexFunc</code>,一个根据资源对象的命名空间来进行索引,一个根据资源对象所在的节点进行索引。然后定义了3个 Pod,前两个在 default 命名空间下面,另外一个在 kube-system 命名空间下面,然后通过 <code style="margin:3px;padding:3px;max-width:100%;font-size:14px;font-family:'Operator Mono', Consolas, Monaco, Menlo, monospace;color:rgb(155,110,35);background-color:rgb(255,245,227);">index.Add</code> 函数添加这3个 Pod 资源对象。然后通过 <code style="margin:3px;padding:3px;max-width:100%;font-size:14px;font-family:'Operator Mono', Consolas, Monaco, Menlo, monospace;color:rgb(155,110,35);background-color:rgb(255,245,227);">index.ByIndex</code> 函数查询在名为 <code style="margin:3px;padding:3px;max-width:100%;font-size:14px;font-family:'Operator Mono', Consolas, Monaco, Menlo, monospace;color:rgb(155,110,35);background-color:rgb(255,245,227);">namespace</code> 的索引器下面匹配索引键为 <code style="margin:3px;padding:3px;max-width:100%;font-size:14px;font-family:'Operator Mono', Consolas, Monaco, Menlo, monospace;color:rgb(155,110,35);background-color:rgb(255,245,227);">default</code> 的 Pod 列表。也就是查询 default 这个命名空间下面的所有 Pod,这里就是前两个定义的 Pod。

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

<ul style="margin-top:8px;margin-bottom:8px;padding:0px 0px 0px 25px;max-width:100%;"><li>IndexFunc:索引器函数,用于计算一个资源对象的索引值列表,上面示例是指定命名空间为索引值结果,当然我们也可以根据需求定义其他的,比如根据 Label 标签、Annotation 等属性来生成索引值列表。</li><li>Index:存储数据,对于上面的示例,我们要查找某个命名空间下面的 Pod,那就要让 Pod 按照其命名空间进行索引,对应的 Index 类型就是<code style="margin:3px;padding:3px;max-width:100%;font-size:14px;font-family:'Operator Mono', Consolas, Monaco, Menlo, monospace;color:rgb(155,110,35);background-color:rgb(255,245,227);">map[namespace]sets.pod</code>。</li><li>Indexers:存储索引器,key 为索引器名称,value 为索引器的实现函数,上面的示例就是 <code style="margin:3px;padding:3px;max-width:100%;font-size:14px;font-family:'Operator Mono', Consolas, Monaco, Menlo, monospace;color:rgb(155,110,35);background-color:rgb(255,245,227);">map["namespace"]MetaNamespaceIndexFunc</code>。</li><li>Indices:存储缓存器,key 为索引器名称,value 为缓存的数据,对于上面的示例就是 <code style="margin:3px;padding:3px;max-width:100%;font-size:14px;font-family:'Operator Mono', Consolas, Monaco, Menlo, monospace;color:rgb(155,110,35);background-color:rgb(255,245,227);">map["namespace"]map[namespace]sets.pod</code>。</li></ul>

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

<pre style="margin-top:10px;margin-bottom:10px;padding:0px;max-width:100%;"><span style="margin:0px 0px -7px;padding:0px;max-width:100%;background-image:url("https://mmbiz.qpic.cn/mmbiz_png/z9BgVMEm7YtQQPoYe5dS4oUtdKQMB6Xo8l8uZLXrwJ1JJeF4zaCLpgyDuNTjU1gcv0o0VpCerB9zl7ics532hpw/640?wx_fmt=png");background-position:10px 10px;background-repeat:no-repeat;height:30px;width:524px;"/><code style="margin:0px;padding:15px 16px 16px;max-width:100%;color:rgb(171,178,191);font-family:'Operator Mono', Consolas, Monaco, Menlo, monospace;font-size:12px;"><span style="margin:0px;padding:0px;max-width:100%;color:rgb(92,99,112);font-style:italic;line-height:26px;">// Indexers 就是包含的所有索引器(分类)以及对应实现</span>
Indexers: {  
  <span style="margin:0px;padding:0px;max-width:100%;color:rgb(152,195,121);line-height:26px;">"namespace"</span>: NamespaceIndexFunc,
  <span style="margin:0px;padding:0px;max-width:100%;color:rgb(152,195,121);line-height:26px;">"nodeName"</span>: NodeNameIndexFunc,
}
<span style="margin:0px;padding:0px;max-width:100%;color:rgb(92,99,112);font-style:italic;line-height:26px;">// Indices 就是包含的所有索引分类中所有的索引数据</span>
Indices: {
 <span style="margin:0px;padding:0px;max-width:100%;color:rgb(152,195,121);line-height:26px;">"namespace"</span>: {  <span style="margin:0px;padding:0px;max-width:100%;color:rgb(92,99,112);font-style:italic;line-height:26px;">//namespace 这个索引分类下的所有索引数据</span>
  <span style="margin:0px;padding:0px;max-width:100%;color:rgb(152,195,121);line-height:26px;">"default"</span>: [<span style="margin:0px;padding:0px;max-width:100%;color:rgb(152,195,121);line-height:26px;">"pod-1"</span>, <span style="margin:0px;padding:0px;max-width:100%;color:rgb(152,195,121);line-height:26px;">"pod-2"</span>],  <span style="margin:0px;padding:0px;max-width:100%;color:rgb(92,99,112);font-style:italic;line-height:26px;">// Index 就是一个索引键下所有的对象键列表</span>
  <span style="margin:0px;padding:0px;max-width:100%;color:rgb(152,195,121);line-height:26px;">"kube-system"</span>: [<span style="margin:0px;padding:0px;max-width:100%;color:rgb(152,195,121);line-height:26px;">"pod-3"</span>]   <span style="margin:0px;padding:0px;max-width:100%;color:rgb(92,99,112);font-style:italic;line-height:26px;">// Index</span>
 },
 <span style="margin:0px;padding:0px;max-width:100%;color:rgb(152,195,121);line-height:26px;">"nodeName"</span>: {  <span style="margin:0px;padding:0px;max-width:100%;color:rgb(92,99,112);font-style:italic;line-height:26px;">//nodeName 这个索引分类下的所有索引数据(对象键列表)</span>
  <span style="margin:0px;padding:0px;max-width:100%;color:rgb(152,195,121);line-height:26px;">"node1"</span>: [<span style="margin:0px;padding:0px;max-width:100%;color:rgb(152,195,121);line-height:26px;">"pod-1"</span>],  <span style="margin:0px;padding:0px;max-width:100%;color:rgb(92,99,112);font-style:italic;line-height:26px;">// Index</span>
  <span style="margin:0px;padding:0px;max-width:100%;color:rgb(152,195,121);line-height:26px;">"node2"</span>: [<span style="margin:0px;padding:0px;max-width:100%;color:rgb(152,195,121);line-height:26px;">"pod-2"</span>, <span style="margin:0px;padding:0px;max-width:100%;color:rgb(152,195,121);line-height:26px;">"pod-3"</span>]  <span style="margin:0px;padding:0px;max-width:100%;color:rgb(92,99,112);font-style:italic;line-height:26px;">// Index</span>
 }
}
</code></pre><h2 style="margin:2.2em 0px 35px;padding:0px;font-size:22px;max-width:100%;line-height:1.5em;"><span style="margin:0px 3px 0px 0px;padding:2px 13px;max-width:100%;color:rgb(81,81,81);height:37px;">ThreadSafeMap</span></h2>

上面我们理解了 Indexer 中的几个重要的数据类型,下面我们来看下 Indexer 接口的具体实现 cache,位于文件 <code style="margin:3px;padding:3px;max-width:100%;font-size:14px;font-family:'Operator Mono', Consolas, Monaco, Menlo, monospace;color:rgb(155,110,35);background-color:rgb(255,245,227);">k8s.io/client-go/tools/cache/store.go</code> 中:

<pre style="margin-top:10px;margin-bottom:10px;padding:0px;max-width:100%;"><span style="margin:0px 0px -7px;padding:0px;max-width:100%;background-image:url("https://mmbiz.qpic.cn/mmbiz_png/z9BgVMEm7YtQQPoYe5dS4oUtdKQMB6Xo8l8uZLXrwJ1JJeF4zaCLpgyDuNTjU1gcv0o0VpCerB9zl7ics532hpw/640?wx_fmt=png");background-position:10px 10px;background-repeat:no-repeat;height:30px;width:524px;"/><code style="margin:0px;padding:15px 16px 16px;max-width:100%;color:rgb(171,178,191);font-family:'Operator Mono', Consolas, Monaco, Menlo, monospace;font-size:12px;"><span style="margin:0px;padding:0px;max-width:100%;color:rgb(92,99,112);font-style:italic;line-height:26px;">// [k8s.io/client-go/tools/cache/store.go](http://k8s.io/client-go/tools/cache/store.go)</span>

<span style="margin:0px;padding:0px;max-width:100%;color:rgb(92,99,112);font-style:italic;line-height:26px;">// cache 用一个 ThreadSafeStore 和一个关联的 KeyFunc 来实现 Indexer</span>
<span style="margin:0px;padding:0px;max-width:100%;color:rgb(198,120,221);line-height:26px;">type</span> cache <span style="margin:0px;padding:0px;max-width:100%;color:rgb(198,120,221);line-height:26px;">struct</span> {
 <span style="margin:0px;padding:0px;max-width:100%;color:rgb(92,99,112);font-style:italic;line-height:26px;">// cacheStorage 是一个线程安全的存储</span>
 cacheStorage ThreadSafeStore
  <span style="margin:0px;padding:0px;max-width:100%;color:rgb(92,99,112);font-style:italic;line-height:26px;">// keyFunc 用于计算对象键</span>
 keyFunc KeyFunc
}
</code></pre>

我们可以看到这个 cache 包含一个 <code style="margin:3px;padding:3px;max-width:100%;font-size:14px;font-family:'Operator Mono', Consolas, Monaco, Menlo, monospace;color:rgb(155,110,35);background-color:rgb(255,245,227);">ThreadSafeStore</code> 的属性,这是一个并发安全的存储,因为是存储,所以自然就有存储相关的增、删、改、查等操作,Indexer 就是在 ThreadSafeMap 基础上进行封装的,实现了索引相关的功能。接下来我们先来看看 ThreadSafeStore 的定义,位于 <code style="margin:3px;padding:3px;max-width:100%;font-size:14px;font-family:'Operator Mono', Consolas, Monaco, Menlo, monospace;color:rgb(155,110,35);background-color:rgb(255,245,227);">k8s.io/client-go/tools/cache/thread_safe_store.go</code> 文件中:

<pre style="margin-top:10px;margin-bottom:10px;padding:0px;max-width:100%;"><span style="margin:0px 0px -7px;padding:0px;max-width:100%;background-image:url("https://mmbiz.qpic.cn/mmbiz_png/z9BgVMEm7YtQQPoYe5dS4oUtdKQMB6Xo8l8uZLXrwJ1JJeF4zaCLpgyDuNTjU1gcv0o0VpCerB9zl7ics532hpw/640?wx_fmt=png");background-position:10px 10px;background-repeat:no-repeat;height:30px;width:524px;"/><code style="margin:0px;padding:15px 16px 16px;max-width:100%;color:rgb(171,178,191);font-family:'Operator Mono', Consolas, Monaco, Menlo, monospace;font-size:12px;"><span style="margin:0px;padding:0px;max-width:100%;color:rgb(198,120,221);line-height:26px;">type</span> ThreadSafeStore <span style="margin:0px;padding:0px;max-width:100%;color:rgb(198,120,221);line-height:26px;">interface</span> {
 Add(key <span style="margin:0px;padding:0px;max-width:100%;color:rgb(198,120,221);line-height:26px;">string</span>, obj <span style="margin:0px;padding:0px;max-width:100%;color:rgb(198,120,221);line-height:26px;">interface</span>{})
 Update(key <span style="margin:0px;padding:0px;max-width:100%;color:rgb(198,120,221);line-height:26px;">string</span>, obj <span style="margin:0px;padding:0px;max-width:100%;color:rgb(198,120,221);line-height:26px;">interface</span>{})
 Delete(key <span style="margin:0px;padding:0px;max-width:100%;color:rgb(198,120,221);line-height:26px;">string</span>)
 Get(key <span style="margin:0px;padding:0px;max-width:100%;color:rgb(198,120,221);line-height:26px;">string</span>) (item <span style="margin:0px;padding:0px;max-width:100%;color:rgb(198,120,221);line-height:26px;">interface</span>{}, exists <span style="margin:0px;padding:0px;max-width:100%;color:rgb(198,120,221);line-height:26px;">bool</span>)
 List() []<span style="margin:0px;padding:0px;max-width:100%;color:rgb(198,120,221);line-height:26px;">interface</span>{}
 ListKeys() []<span style="margin:0px;padding:0px;max-width:100%;color:rgb(198,120,221);line-height:26px;">string</span>
 Replace(<span style="margin:0px;padding:0px;max-width:100%;color:rgb(198,120,221);line-height:26px;">map</span>[<span style="margin:0px;padding:0px;max-width:100%;color:rgb(198,120,221);line-height:26px;">string</span>]<span style="margin:0px;padding:0px;max-width:100%;color:rgb(198,120,221);line-height:26px;">interface</span>{}, <span style="margin:0px;padding:0px;max-width:100%;color:rgb(198,120,221);line-height:26px;">string</span>)
 Index(indexName <span style="margin:0px;padding:0px;max-width:100%;color:rgb(198,120,221);line-height:26px;">string</span>, obj <span style="margin:0px;padding:0px;max-width:100%;color:rgb(198,120,221);line-height:26px;">interface</span>{}) ([]<span style="margin:0px;padding:0px;max-width:100%;color:rgb(198,120,221);line-height:26px;">interface</span>{}, error)
 IndexKeys(indexName, indexKey <span style="margin:0px;padding:0px;max-width:100%;color:rgb(198,120,221);line-height:26px;">string</span>) ([]<span style="margin:0px;padding:0px;max-width:100%;color:rgb(198,120,221);line-height:26px;">string</span>, error)
 ListIndexFuncValues(name <span style="margin:0px;padding:0px;max-width:100%;color:rgb(198,120,221);line-height:26px;">string</span>) []<span style="margin:0px;padding:0px;max-width:100%;color:rgb(198,120,221);line-height:26px;">string</span>
 ByIndex(indexName, indexKey <span style="margin:0px;padding:0px;max-width:100%;color:rgb(198,120,221);line-height:26px;">string</span>) ([]<span style="margin:0px;padding:0px;max-width:100%;color:rgb(198,120,221);line-height:26px;">interface</span>{}, error)
 GetIndexers() Indexers

 AddIndexers(newIndexers Indexers) error
 Resync() error
}
</code></pre>

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

<pre style="margin-top:10px;margin-bottom:10px;padding:0px;max-width:100%;"><span style="margin:0px 0px -7px;padding:0px;max-width:100%;background-image:url("https://mmbiz.qpic.cn/mmbiz_png/z9BgVMEm7YtQQPoYe5dS4oUtdKQMB6Xo8l8uZLXrwJ1JJeF4zaCLpgyDuNTjU1gcv0o0VpCerB9zl7ics532hpw/640?wx_fmt=png");background-position:10px 10px;background-repeat:no-repeat;height:30px;width:524px;"/><code style="margin:0px;padding:15px 16px 16px;max-width:100%;color:rgb(171,178,191);font-family:'Operator Mono', Consolas, Monaco, Menlo, monospace;font-size:12px;"><span style="margin:0px;padding:0px;max-width:100%;color:rgb(92,99,112);font-style:italic;line-height:26px;">// k8s.io/client-go/tools/cache/thread_safe_store.go</span>

<span style="margin:0px;padding:0px;max-width:100%;color:rgb(92,99,112);font-style:italic;line-height:26px;">// threadSafeMap 实现了 ThreadSafeStore</span>
<span style="margin:0px;padding:0px;max-width:100%;color:rgb(198,120,221);line-height:26px;">type</span> threadSafeMap <span style="margin:0px;padding:0px;max-width:100%;color:rgb(198,120,221);line-height:26px;">struct</span> {
 lock  sync.RWMutex
  <span style="margin:0px;padding:0px;max-width:100%;color:rgb(92,99,112);font-style:italic;line-height:26px;">// 存储资源对象数据,key(对象键) 通过 keyFunc 得到</span>
  <span style="margin:0px;padding:0px;max-width:100%;color:rgb(92,99,112);font-style:italic;line-height:26px;">// 这就是真正存储的数据(对象键 -> 对象)</span>
 items <span style="margin:0px;padding:0px;max-width:100%;color:rgb(198,120,221);line-height:26px;">map</span>[<span style="margin:0px;padding:0px;max-width:100%;color:rgb(198,120,221);line-height:26px;">string</span>]<span style="margin:0px;padding:0px;max-width:100%;color:rgb(198,120,221);line-height:26px;">interface</span>{}

 <span style="margin:0px;padding:0px;max-width:100%;color:rgb(92,99,112);font-style:italic;line-height:26px;">// indexers 索引分类与索引键函数的映射</span>
 indexers Indexers
 <span style="margin:0px;padding:0px;max-width:100%;color:rgb(92,99,112);font-style:italic;line-height:26px;">// indices 通过索引可以快速找到对象键</span>
 indices Indices
}
</code></pre>

不要把索引键和对象键搞混了,索引键是用于对象快速查找的;对象键是对象在存储中的唯一命名,对象是通过名字 对象的方式存储的。接下来我们来仔细看下接口的具体实现,首先还是比较简单的 Add、Delete、Update 几个函数的实现:

<pre style="margin-top:10px;margin-bottom:10px;padding:0px;max-width:100%;"><span style="margin:0px 0px -7px;padding:0px;max-width:100%;background-image:url("https://mmbiz.qpic.cn/mmbiz_png/z9BgVMEm7YtQQPoYe5dS4oUtdKQMB6Xo8l8uZLXrwJ1JJeF4zaCLpgyDuNTjU1gcv0o0VpCerB9zl7ics532hpw/640?wx_fmt=png");background-position:10px 10px;background-repeat:no-repeat;height:30px;width:524px;"/><code style="margin:0px;padding:15px 16px 16px;max-width:100%;color:rgb(171,178,191);font-family:'Operator Mono', Consolas, Monaco, Menlo, monospace;font-size:12px;"><span style="margin:0px;padding:0px;max-width:100%;color:rgb(92,99,112);font-style:italic;line-height:26px;">// k8s.io/client-go/tools/cache/thread_safe_store.go</span>

<span style="margin:0px;padding:0px;max-width:100%;color:rgb(92,99,112);font-style:italic;line-height:26px;">// 添加对象</span>
<span style="margin:0px;padding:0px;max-width:100%;line-height:26px;"><span style="margin:0px;padding:0px;max-width:100%;color:rgb(198,120,221);line-height:26px;">func</span> <span style="margin:0px;padding:0px;max-width:100%;line-height:26px;">(c *threadSafeMap)</span> <span style="margin:0px;padding:0px;max-width:100%;color:rgb(97,174,238);line-height:26px;">Add</span><span style="margin:0px;padding:0px;max-width:100%;line-height:26px;">(key <span style="margin:0px;padding:0px;max-width:100%;color:rgb(198,120,221);line-height:26px;">string</span>, obj <span style="margin:0px;padding:0px;max-width:100%;color:rgb(198,120,221);line-height:26px;">interface</span>{})</span></span> {
 c.lock.Lock()
 <span style="margin:0px;padding:0px;max-width:100%;color:rgb(198,120,221);line-height:26px;">defer</span> c.lock.Unlock()
  <span style="margin:0px;padding:0px;max-width:100%;color:rgb(92,99,112);font-style:italic;line-height:26px;">// 获取老的对象</span>
 oldObject := c.items[key]
  <span style="margin:0px;padding:0px;max-width:100%;color:rgb(92,99,112);font-style:italic;line-height:26px;">// 写入新的对象,items 中存的是 objKey -> obj 的映射</span>
 c.items[key] = obj
  <span style="margin:0px;padding:0px;max-width:100%;color:rgb(92,99,112);font-style:italic;line-height:26px;">// 添加了新的对象,所以要更新索引</span>
 c.updateIndices(oldObject, obj, key)
}

<span style="margin:0px;padding:0px;max-width:100%;color:rgb(92,99,112);font-style:italic;line-height:26px;">// 更新对象,可以看到实现和 Add 是一样的</span>
<span style="margin:0px;padding:0px;max-width:100%;line-height:26px;"><span style="margin:0px;padding:0px;max-width:100%;color:rgb(198,120,221);line-height:26px;">func</span> <span style="margin:0px;padding:0px;max-width:100%;line-height:26px;">(c *threadSafeMap)</span> <span style="margin:0px;padding:0px;max-width:100%;color:rgb(97,174,238);line-height:26px;">Update</span><span style="margin:0px;padding:0px;max-width:100%;line-height:26px;">(key <span style="margin:0px;padding:0px;max-width:100%;color:rgb(198,120,221);line-height:26px;">string</span>, obj <span style="margin:0px;padding:0px;max-width:100%;color:rgb(198,120,221);line-height:26px;">interface</span>{})</span></span> {
 c.lock.Lock()
 <span style="margin:0px;padding:0px;max-width:100%;color:rgb(198,120,221);line-height:26px;">defer</span> c.lock.Unlock()
 oldObject := c.items[key]
 c.items[key] = obj
 c.updateIndices(oldObject, obj, key)
}

<span style="margin:0px;padding:0px;max-width:100%;color:rgb(92,99,112);font-style:italic;line-height:26px;">// 删除对象</span>
<span style="margin:0px;padding:0px;max-width:100%;line-height:26px;"><span style="margin:0px;padding:0px;max-width:100%;color:rgb(198,120,221);line-height:26px;">func</span> <span style="margin:0px;padding:0px;max-width:100%;line-height:26px;">(c *threadSafeMap)</span> <span style="margin:0px;padding:0px;max-width:100%;color:rgb(97,174,238);line-height:26px;">Delete</span><span style="margin:0px;padding:0px;max-width:100%;line-height:26px;">(key <span style="margin:0px;padding:0px;max-width:100%;color:rgb(198,120,221);line-height:26px;">string</span>)</span></span> {
 c.lock.Lock()
 <span style="margin:0px;padding:0px;max-width:100%;color:rgb(198,120,221);line-height:26px;">defer</span> c.lock.Unlock()
  <span style="margin:0px;padding:0px;max-width:100%;color:rgb(92,99,112);font-style:italic;line-height:26px;">// 判断对象是否存在,存在才执行删除操作</span>
 <span style="margin:0px;padding:0px;max-width:100%;color:rgb(198,120,221);line-height:26px;">if</span> obj, exists := c.items[key]; exists {
    <span style="margin:0px;padding:0px;max-width:100%;color:rgb(92,99,112);font-style:italic;line-height:26px;">// 删除对象索引</span>
  c.deleteFromIndices(obj, key)
    <span style="margin:0px;padding:0px;max-width:100%;color:rgb(92,99,112);font-style:italic;line-height:26px;">// 删除对象本身</span>
  <span style="margin:0px;padding:0px;max-width:100%;color:rgb(230,192,123);line-height:26px;">delete</span>(c.items, key)
 }
}
</code></pre>

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

<pre style="margin-top:10px;margin-bottom:10px;padding:0px;max-width:100%;"><span style="margin:0px 0px -7px;padding:0px;max-width:100%;background-image:url("https://mmbiz.qpic.cn/mmbiz_png/z9BgVMEm7YtQQPoYe5dS4oUtdKQMB6Xo8l8uZLXrwJ1JJeF4zaCLpgyDuNTjU1gcv0o0VpCerB9zl7ics532hpw/640?wx_fmt=png");background-position:10px 10px;background-repeat:no-repeat;height:30px;width:524px;"/><code style="margin:0px;padding:15px 16px 16px;max-width:100%;color:rgb(171,178,191);font-family:'Operator Mono', Consolas, Monaco, Menlo, monospace;font-size:12px;"><span style="margin:0px;padding:0px;max-width:100%;color:rgb(92,99,112);font-style:italic;line-height:26px;">// k8s.io/client-go/tools/cache/thread_safe_store.go</span>

<span style="margin:0px;padding:0px;max-width:100%;color:rgb(92,99,112);font-style:italic;line-height:26px;">// updateIndices 更新索引</span>
<span style="margin:0px;padding:0px;max-width:100%;line-height:26px;"><span style="margin:0px;padding:0px;max-width:100%;color:rgb(198,120,221);line-height:26px;">func</span> <span style="margin:0px;padding:0px;max-width:100%;line-height:26px;">(c *threadSafeMap)</span> <span style="margin:0px;padding:0px;max-width:100%;color:rgb(97,174,238);line-height:26px;">updateIndices</span><span style="margin:0px;padding:0px;max-width:100%;line-height:26px;">(oldObj <span style="margin:0px;padding:0px;max-width:100%;color:rgb(198,120,221);line-height:26px;">interface</span>{}, newObj <span style="margin:0px;padding:0px;max-width:100%;color:rgb(198,120,221);line-height:26px;">interface</span>{}, key <span style="margin:0px;padding:0px;max-width:100%;color:rgb(198,120,221);line-height:26px;">string</span>)</span></span> {
 <span style="margin:0px;padding:0px;max-width:100%;color:rgb(92,99,112);font-style:italic;line-height:26px;">// 如果有旧的对象,需要先从索引中删除这个对象</span>
 <span style="margin:0px;padding:0px;max-width:100%;color:rgb(198,120,221);line-height:26px;">if</span> oldObj != <span style="margin:0px;padding:0px;max-width:100%;color:rgb(86,182,194);line-height:26px;">nil</span> {
  c.deleteFromIndices(oldObj, key)
 }
  <span style="margin:0px;padding:0px;max-width:100%;color:rgb(92,99,112);font-style:italic;line-height:26px;">// 循环所有的索引器</span>
 <span style="margin:0px;padding:0px;max-width:100%;color:rgb(198,120,221);line-height:26px;">for</span> name, indexFunc := <span style="margin:0px;padding:0px;max-width:100%;color:rgb(198,120,221);line-height:26px;">range</span> c.indexers {
    <span style="margin:0px;padding:0px;max-width:100%;color:rgb(92,99,112);font-style:italic;line-height:26px;">// 获取对象的索引键</span>
  indexValues, err := indexFunc(newObj)
  <span style="margin:0px;padding:0px;max-width:100%;color:rgb(198,120,221);line-height:26px;">if</span> err != <span style="margin:0px;padding:0px;max-width:100%;color:rgb(86,182,194);line-height:26px;">nil</span> {
   <span style="margin:0px;padding:0px;max-width:100%;color:rgb(230,192,123);line-height:26px;">panic</span>(fmt.Errorf(<span style="margin:0px;padding:0px;max-width:100%;color:rgb(152,195,121);line-height:26px;">"unable to calculate an index entry for key %q on index %q: %v"</span>, key, name, err))
  }
    <span style="margin:0px;padding:0px;max-width:100%;color:rgb(92,99,112);font-style:italic;line-height:26px;">// 得到当前索引器的索引</span>
  index := c.indices[name]
  <span style="margin:0px;padding:0px;max-width:100%;color:rgb(198,120,221);line-height:26px;">if</span> index == <span style="margin:0px;padding:0px;max-width:100%;color:rgb(86,182,194);line-height:26px;">nil</span> {
      <span style="margin:0px;padding:0px;max-width:100%;color:rgb(92,99,112);font-style:italic;line-height:26px;">// 没有对应的索引,则初始化一个索引</span>
   index = Index{}
   c.indices[name] = index
  }
    <span style="margin:0px;padding:0px;max-width:100%;color:rgb(92,99,112);font-style:italic;line-height:26px;">// 循环所有的索引键</span>
  <span style="margin:0px;padding:0px;max-width:100%;color:rgb(198,120,221);line-height:26px;">for</span> _, indexValue := <span style="margin:0px;padding:0px;max-width:100%;color:rgb(198,120,221);line-height:26px;">range</span> indexValues {
      <span style="margin:0px;padding:0px;max-width:100%;color:rgb(92,99,112);font-style:italic;line-height:26px;">// 得到索引键对应的对象键列表</span>
   set := index[indexValue]
   <span style="margin:0px;padding:0px;max-width:100%;color:rgb(198,120,221);line-height:26px;">if</span> set == <span style="margin:0px;padding:0px;max-width:100%;color:rgb(86,182,194);line-height:26px;">nil</span> {
        <span style="margin:0px;padding:0px;max-width:100%;color:rgb(92,99,112);font-style:italic;line-height:26px;">// 没有对象键列表则初始化一个空列表</span>
    set = sets.String{}
    index[indexValue] = set
   }
      <span style="margin:0px;padding:0px;max-width:100%;color:rgb(92,99,112);font-style:italic;line-height:26px;">// 将对象键插入到集合中,方便索引</span>
   set.Insert(key)
  }
 }
}

<span style="margin:0px;padding:0px;max-width:100%;color:rgb(92,99,112);font-style:italic;line-height:26px;">// deleteFromIndices 删除对象索引</span>
<span style="margin:0px;padding:0px;max-width:100%;line-height:26px;"><span style="margin:0px;padding:0px;max-width:100%;color:rgb(198,120,221);line-height:26px;">func</span> <span style="margin:0px;padding:0px;max-width:100%;line-height:26px;">(c *threadSafeMap)</span> <span style="margin:0px;padding:0px;max-width:100%;color:rgb(97,174,238);line-height:26px;">deleteFromIndices</span><span style="margin:0px;padding:0px;max-width:100%;line-height:26px;">(obj <span style="margin:0px;padding:0px;max-width:100%;color:rgb(198,120,221);line-height:26px;">interface</span>{}, key <span style="margin:0px;padding:0px;max-width:100%;color:rgb(198,120,221);line-height:26px;">string</span>)</span></span> {
  <span style="margin:0px;padding:0px;max-width:100%;color:rgb(92,99,112);font-style:italic;line-height:26px;">// 循环所有的索引器</span>
 <span style="margin:0px;padding:0px;max-width:100%;color:rgb(198,120,221);line-height:26px;">for</span> name, indexFunc := <span style="margin:0px;padding:0px;max-width:100%;color:rgb(198,120,221);line-height:26px;">range</span> c.indexers {
    <span style="margin:0px;padding:0px;max-width:100%;color:rgb(92,99,112);font-style:italic;line-height:26px;">// 获取删除对象的索引键列表</span>
  indexValues, err := indexFunc(obj)
  <span style="margin:0px;padding:0px;max-width:100%;color:rgb(198,120,221);line-height:26px;">if</span> err != <span style="margin:0px;padding:0px;max-width:100%;color:rgb(86,182,194);line-height:26px;">nil</span> {
   <span style="margin:0px;padding:0px;max-width:100%;color:rgb(230,192,123);line-height:26px;">panic</span>(fmt.Errorf(<span style="margin:0px;padding:0px;max-width:100%;color:rgb(152,195,121);line-height:26px;">"unable to calculate an index entry for key %q on index %q: %v"</span>, key, name, err))
  }
    <span style="margin:0px;padding:0px;max-width:100%;color:rgb(92,99,112);font-style:italic;line-height:26px;">// 获取当前索引器的索引</span>
  index := c.indices[name]
  <span style="margin:0px;padding:0px;max-width:100%;color:rgb(198,120,221);line-height:26px;">if</span> index == <span style="margin:0px;padding:0px;max-width:100%;color:rgb(86,182,194);line-height:26px;">nil</span> {
   <span style="margin:0px;padding:0px;max-width:100%;color:rgb(198,120,221);line-height:26px;">continue</span>
  }
    <span style="margin:0px;padding:0px;max-width:100%;color:rgb(92,99,112);font-style:italic;line-height:26px;">// 循环所有索引键</span>
  <span style="margin:0px;padding:0px;max-width:100%;color:rgb(198,120,221);line-height:26px;">for</span> _, indexValue := <span style="margin:0px;padding:0px;max-width:100%;color:rgb(198,120,221);line-height:26px;">range</span> indexValues {
      <span style="margin:0px;padding:0px;max-width:100%;color:rgb(92,99,112);font-style:italic;line-height:26px;">// 获取索引键对应的对象键列表</span>
   set := index[indexValue]
   <span style="margin:0px;padding:0px;max-width:100%;color:rgb(198,120,221);line-height:26px;">if</span> set != <span style="margin:0px;padding:0px;max-width:100%;color:rgb(86,182,194);line-height:26px;">nil</span> {
        <span style="margin:0px;padding:0px;max-width:100%;color:rgb(92,99,112);font-style:italic;line-height:26px;">// 从对象键列表中删除当前要删除的对象键</span>
    set.Delete(key)

    <span style="margin:0px;padding:0px;max-width:100%;color:rgb(92,99,112);font-style:italic;line-height:26px;">// 如果当集合为空的时候不删除set,那么具有高基数的短生命资源的 indices 会导致未使用的空集合随时间增加内存。</span>
        <span style="margin:0px;padding:0px;max-width:100%;color:rgb(92,99,112);font-style:italic;line-height:26px;">// `kubernetes/kubernetes/issues/84959`.</span>
        <span style="margin:0px;padding:0px;max-width:100%;color:rgb(198,120,221);line-height:26px;">if</span> <span style="margin:0px;padding:0px;max-width:100%;color:rgb(230,192,123);line-height:26px;">len</span>(set) == <span style="margin:0px;padding:0px;max-width:100%;color:rgb(209,154,102);line-height:26px;">0</span> {
     <span style="margin:0px;padding:0px;max-width:100%;color:rgb(230,192,123);line-height:26px;">delete</span>(index, indexValue)
    }
   }
  }
 }
}
</code></pre>

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

然后接下来就是几个查询相关的接口实现:

<pre style="margin-top:10px;margin-bottom:10px;padding:0px;max-width:100%;"><span style="margin:0px 0px -7px;padding:0px;max-width:100%;background-image:url("https://mmbiz.qpic.cn/mmbiz_png/z9BgVMEm7YtQQPoYe5dS4oUtdKQMB6Xo8l8uZLXrwJ1JJeF4zaCLpgyDuNTjU1gcv0o0VpCerB9zl7ics532hpw/640?wx_fmt=png");background-position:10px 10px;background-repeat:no-repeat;height:30px;width:524px;"/><code style="margin:0px;padding:15px 16px 16px;max-width:100%;color:rgb(171,178,191);font-family:'Operator Mono', Consolas, Monaco, Menlo, monospace;font-size:12px;"><span style="margin:0px;padding:0px;max-width:100%;color:rgb(92,99,112);font-style:italic;line-height:26px;">// k8s.io/client-go/tools/cache/thread_safe_store.go</span>

<span style="margin:0px;padding:0px;max-width:100%;color:rgb(92,99,112);font-style:italic;line-height:26px;">// 获取对象</span>
<span style="margin:0px;padding:0px;max-width:100%;line-height:26px;"><span style="margin:0px;padding:0px;max-width:100%;color:rgb(198,120,221);line-height:26px;">func</span> <span style="margin:0px;padding:0px;max-width:100%;line-height:26px;">(c *threadSafeMap)</span> <span style="margin:0px;padding:0px;max-width:100%;color:rgb(97,174,238);line-height:26px;">Get</span><span style="margin:0px;padding:0px;max-width:100%;line-height:26px;">(key <span style="margin:0px;padding:0px;max-width:100%;color:rgb(198,120,221);line-height:26px;">string</span>)</span> <span style="margin:0px;padding:0px;max-width:100%;line-height:26px;">(item <span style="margin:0px;padding:0px;max-width:100%;color:rgb(198,120,221);line-height:26px;">interface</span>{}, exists <span style="margin:0px;padding:0px;max-width:100%;color:rgb(198,120,221);line-height:26px;">bool</span>)</span></span> {
 c.lock.RLock()  <span style="margin:0px;padding:0px;max-width:100%;color:rgb(92,99,112);font-style:italic;line-height:26px;">// 只需要读锁</span>
 <span style="margin:0px;padding:0px;max-width:100%;color:rgb(198,120,221);line-height:26px;">defer</span> c.lock.RUnlock()
  <span style="margin:0px;padding:0px;max-width:100%;color:rgb(92,99,112);font-style:italic;line-height:26px;">// 直接从 map 中读取值</span>
 item, exists = c.items[key]
 <span style="margin:0px;padding:0px;max-width:100%;color:rgb(198,120,221);line-height:26px;">return</span> item, exists
}

<span style="margin:0px;padding:0px;max-width:100%;color:rgb(92,99,112);font-style:italic;line-height:26px;">// 对象列举</span>
<span style="margin:0px;padding:0px;max-width:100%;line-height:26px;"><span style="margin:0px;padding:0px;max-width:100%;color:rgb(198,120,221);line-height:26px;">func</span> <span style="margin:0px;padding:0px;max-width:100%;line-height:26px;">(c *threadSafeMap)</span> <span style="margin:0px;padding:0px;max-width:100%;color:rgb(97,174,238);line-height:26px;">List</span><span style="margin:0px;padding:0px;max-width:100%;line-height:26px;">()</span> []<span style="margin:0px;padding:0px;max-width:100%;color:rgb(97,174,238);line-height:26px;">interface</span></span>{} {
 c.lock.RLock()
 <span style="margin:0px;padding:0px;max-width:100%;color:rgb(198,120,221);line-height:26px;">defer</span> c.lock.RUnlock()
 list := <span style="margin:0px;padding:0px;max-width:100%;color:rgb(230,192,123);line-height:26px;">make</span>([]<span style="margin:0px;padding:0px;max-width:100%;color:rgb(198,120,221);line-height:26px;">interface</span>{}, <span style="margin:0px;padding:0px;max-width:100%;color:rgb(209,154,102);line-height:26px;">0</span>, <span style="margin:0px;padding:0px;max-width:100%;color:rgb(230,192,123);line-height:26px;">len</span>(c.items))
 <span style="margin:0px;padding:0px;max-width:100%;color:rgb(198,120,221);line-height:26px;">for</span> _, item := <span style="margin:0px;padding:0px;max-width:100%;color:rgb(198,120,221);line-height:26px;">range</span> c.items {
  list = <span style="margin:0px;padding:0px;max-width:100%;color:rgb(230,192,123);line-height:26px;">append</span>(list, item)
 }
 <span style="margin:0px;padding:0px;max-width:100%;color:rgb(198,120,221);line-height:26px;">return</span> list
}

<span style="margin:0px;padding:0px;max-width:100%;color:rgb(92,99,112);font-style:italic;line-height:26px;">// 返回 threadSafeMap 中所有的对象键列表</span>
<span style="margin:0px;padding:0px;max-width:100%;line-height:26px;"><span style="margin:0px;padding:0px;max-width:100%;color:rgb(198,120,221);line-height:26px;">func</span> <span style="margin:0px;padding:0px;max-width:100%;line-height:26px;">(c *threadSafeMap)</span> <span style="margin:0px;padding:0px;max-width:100%;color:rgb(97,174,238);line-height:26px;">ListKeys</span><span style="margin:0px;padding:0px;max-width:100%;line-height:26px;">()</span> []<span style="margin:0px;padding:0px;max-width:100%;color:rgb(97,174,238);line-height:26px;">string</span></span> {
 c.lock.RLock()
 <span style="margin:0px;padding:0px;max-width:100%;color:rgb(198,120,221);line-height:26px;">defer</span> c.lock.RUnlock()
 list := <span style="margin:0px;padding:0px;max-width:100%;color:rgb(230,192,123);line-height:26px;">make</span>([]<span style="margin:0px;padding:0px;max-width:100%;color:rgb(198,120,221);line-height:26px;">string</span>, <span style="margin:0px;padding:0px;max-width:100%;color:rgb(209,154,102);line-height:26px;">0</span>, <span style="margin:0px;padding:0px;max-width:100%;color:rgb(230,192,123);line-height:26px;">len</span>(c.items))
 <span style="margin:0px;padding:0px;max-width:100%;color:rgb(198,120,221);line-height:26px;">for</span> key := <span style="margin:0px;padding:0px;max-width:100%;color:rgb(198,120,221);line-height:26px;">range</span> c.items {
  list = <span style="margin:0px;padding:0px;max-width:100%;color:rgb(230,192,123);line-height:26px;">append</span>(list, key)
 }
 <span style="margin:0px;padding:0px;max-width:100%;color:rgb(198,120,221);line-height:26px;">return</span> list
}

<span style="margin:0px;padding:0px;max-width:100%;color:rgb(92,99,112);font-style:italic;line-height:26px;">// 替换所有对象,相当于重新构建索引</span>
<span style="margin:0px;padding:0px;max-width:100%;line-height:26px;"><span style="margin:0px;padding:0px;max-width:100%;color:rgb(198,120,221);line-height:26px;">func</span> <span style="margin:0px;padding:0px;max-width:100%;line-height:26px;">(c *threadSafeMap)</span> <span style="margin:0px;padding:0px;max-width:100%;color:rgb(97,174,238);line-height:26px;">Replace</span><span style="margin:0px;padding:0px;max-width:100%;line-height:26px;">(items <span style="margin:0px;padding:0px;max-width:100%;color:rgb(198,120,221);line-height:26px;">map</span>[<span style="margin:0px;padding:0px;max-width:100%;color:rgb(198,120,221);line-height:26px;">string</span>]<span style="margin:0px;padding:0px;max-width:100%;color:rgb(198,120,221);line-height:26px;">interface</span>{}, resourceVersion <span style="margin:0px;padding:0px;max-width:100%;color:rgb(198,120,221);line-height:26px;">string</span>)</span></span> {
 c.lock.Lock()
 <span style="margin:0px;padding:0px;max-width:100%;color:rgb(198,120,221);line-height:26px;">defer</span> c.lock.Unlock()
  <span style="margin:0px;padding:0px;max-width:100%;color:rgb(92,99,112);font-style:italic;line-height:26px;">// 直接覆盖之前的对象</span>
 c.items = items

 <span style="margin:0px;padding:0px;max-width:100%;color:rgb(92,99,112);font-style:italic;line-height:26px;">// 重新构建索引</span>
 c.indices = Indices{}
 <span style="margin:0px;padding:0px;max-width:100%;color:rgb(198,120,221);line-height:26px;">for</span> key, item := <span style="margin:0px;padding:0px;max-width:100%;color:rgb(198,120,221);line-height:26px;">range</span> c.items {
    <span style="margin:0px;padding:0px;max-width:100%;color:rgb(92,99,112);font-style:italic;line-height:26px;">// 更新元素的索引</span>
  c.updateIndices(<span style="margin:0px;padding:0px;max-width:100%;color:rgb(86,182,194);line-height:26px;">nil</span>, item, key)
 }
}
</code></pre>

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

<pre style="margin-top:10px;margin-bottom:10px;padding:0px;max-width:100%;"><span style="margin:0px 0px -7px;padding:0px;max-width:100%;background-image:url("https://mmbiz.qpic.cn/mmbiz_png/z9BgVMEm7YtQQPoYe5dS4oUtdKQMB6Xo8l8uZLXrwJ1JJeF4zaCLpgyDuNTjU1gcv0o0VpCerB9zl7ics532hpw/640?wx_fmt=png");background-position:10px 10px;background-repeat:no-repeat;height:30px;width:524px;"/><code style="margin:0px;padding:15px 16px 16px;max-width:100%;color:rgb(171,178,191);font-family:'Operator Mono', Consolas, Monaco, Menlo, monospace;font-size:12px;"><span style="margin:0px;padding:0px;max-width:100%;color:rgb(92,99,112);font-style:italic;line-height:26px;">// k8s.io/client-go/tools/cache/thread_safe_store.go</span>

<span style="margin:0px;padding:0px;max-width:100%;color:rgb(92,99,112);font-style:italic;line-height:26px;">// 通过指定的索引器和对象获取符合这个对象特征的所有对象</span>
<span style="margin:0px;padding:0px;max-width:100%;line-height:26px;"><span style="margin:0px;padding:0px;max-width:100%;color:rgb(198,120,221);line-height:26px;">func</span> <span style="margin:0px;padding:0px;max-width:100%;line-height:26px;">(c *threadSafeMap)</span> <span style="margin:0px;padding:0px;max-width:100%;color:rgb(97,174,238);line-height:26px;">Index</span><span style="margin:0px;padding:0px;max-width:100%;line-height:26px;">(indexName <span style="margin:0px;padding:0px;max-width:100%;color:rgb(198,120,221);line-height:26px;">string</span>, obj <span style="margin:0px;padding:0px;max-width:100%;color:rgb(198,120,221);line-height:26px;">interface</span>{})</span>  client-go 之 Indexer 的理解
go mod使用
Golang学习之路(二):Windows下Go Micro微服务开发环境搭建
nsq源码channel的messagePump实现原理
Golang 实现 Redis(6): 实现 pipeline 模式的 redis 客户端
golang实现匿名聊天后台并发处理服务器
Golang基础第五篇——golang的gRPC
想系统学习GO语言(Golang
支持多语言的微服务框架Tars-Go
Magento 2.2.5和2.2.6的问题产品设置特价又删除问题分析与解决

[关闭]
~ ~