client-go的Indexer三部曲之一:基本功能

380 阅读14分钟

欢迎访问我的GitHub

这里分类和汇总了欣宸的全部原创(含配套源码):github.com/zq2599/blog…

关于《client-go的Indexer三部曲》系列

  • 该系列是《client-go实战系列》的子系列文章,共三篇内容,分别从功能、性能、源码三个角度对client-go内部的Indexer组件进行说明,目标是与大家一同学习Indexer,并掌握如何在开发中通过Indexer实现灵活、高性能的查询检索操作
  • 再来回顾一下client-go的基本架构图,如下图所示,这次重点关注的是中间那根横向的虚线,可见在自定义的业务开发过程中,当我们需要得到某个资源对象的时候,是可以通过Indexer直接从本地缓存中获取的(Thread safe store) 在这里插入图片描述
  • 从用户角度来看,Indexer的作用就是可以帮助用户从本地获取资源对象,由于client-go的List & Watch机制,资源在kubernetes的所有变化都会及时同步到本地缓存中,这也就保证了本地缓存的数据是实时更新的

为什么要用Indexer获取资源?

  • 看到这里您可能会有疑问:从用户视角来看,client-go已经提供了API,可以远程访问api-server获取完整对象,那不就够用了吗?这里用Indexer获取本地对象有什么意义呢?

  • 理论上讲,从本地缓存获取数据属于进程内操作,如下图所示 在这里插入图片描述

  • 如果使用client-go提供的远程接口,就涉及到网络请求,理论上耗时更长 在这里插入图片描述

  • 从api-server视角来看还有另一层含义:走本地缓存后,来api-server查询的次数就少了,这也就降低了api-server的负载

  • 接下来就通过编码实战的方式,和大家一起了解Indexer的基本用法,另外为了印证本地缓存的性能优势,还会写一段远程访问api-server查询对象的代码,最后用性能工具同时对比两者,看看性能差距是否存在

  • 经过上述操作,相信咱们可以快速了解Indexer的实际用法,还能通过数据对其有更具体的认识,有了这些基础,在后面深入学习Indexer源码的时候,似乎可以轻松很多,每当您看到一段源码,对其设计原因和实际作用都有更多的认识,嗯,这也是欣宸一直推崇的学习方法:实战,不停的实战,拒绝凭空读代码

准备工作

  • 除了保证kubernetes环境正常,还要部署一些资源,以便稍后演示Indexer的功能
  • 先在kubernetes部署三种deployment,分别是:nginx、tomcat、mysql,完整的部署脚本可以在这里下载:github.com/zq2599/blog… ,共有下图这些文件 在这里插入图片描述
  • 由于涉及到本地存储,需要您根据自己电脑的情况修改deploy.sh的内容,如下图红色箭头所示,请修改成您自己电脑的有效路径 在这里插入图片描述
  • 执行deploy.sh即可完成部署,如果要清理掉所有部署的痕迹,执行deploy.sh即可
  • 部署成功后,在indexer-tutorials这个namespace下新增了五个pod,如下所示
kubectl get pods -n indexer-tutorials -o wide
NAME                                          READY   STATUS    RESTARTS   AGE    IP             NODE   NOMINATED NODE   READINESS GATES
mysql-556b999fd8-22hqh                        1/1     Running   0          8m4s   100.91.64.49   hedy   <none>           <none>
nginx-deployment-696cc4bc86-2rqcg             1/1     Running   0          8m1s   100.91.64.50   hedy   <none>           <none>
nginx-deployment-696cc4bc86-bkplx             1/1     Running   0          8m2s   100.91.64.46   hedy   <none>           <none>
nginx-deployment-696cc4bc86-m7wwh             1/1     Running   0          8m1s   100.91.64.47   hedy   <none>           <none>
tomcat-deployment-nautilus-7fcb47fcc4-vvdq6   1/1     Running   0          8m3s   100.91.64.48   hedy   <none>           <none>

准备一批业务需求

  • 本篇的目标是学会Indexer最基本的使用方式,因此先准备几个业务需求,然后用Indexer来实现这些需求,这样也算是有了具体的目标,可以更容易理解和掌握技术
  • 回顾刚才的部署脚本,如下图,nginx、tomcat、mysql都有两个自定义label:language和business-service-type 在这里插入图片描述
  • 下面的表格说明了nginx、tomcat、mysql这些pod的两个label的具体值
pod语言类型(language)服务类型(business-service-type)
nginxcweb
tomcatcweb
mysqljavastorage
  • 根据上述表格可见,nginx、tomcat、mysql这些pod,一共有两种分类方式:
  1. 按照语言类型分类,nginx和mysql都是c语言类,tomcat是java类
  2. 按照服务类型分类,nginx和tomcat都是web类,mysql是存储类
  • 请记住上述分类方式,接下来可以列出业务需求了
  1. 根据指定语言查询对应的pod的key,例如输入java,返回tomcat的pod的key
  2. 根据指定的pod的key查询pod对象,例如输入indexer-tutorials/nginx-deployment-87945df85-vzjgv,返回nginx的pod对象
  3. 查询指定语言的所有对象,例如输入java,返回tomcat的pod
  4. 根据某个对象的key,获取同语言类型的所有对象,例如输入indexer-tutorials/nginx-deployment-87945df85-vzjgv,返回的就是nginx和mysql的所有pod,因为它们的语言类型都是c
  5. 返回所有语言类型,这里应该是c和java
  6. 返回所有分类方式,这里应该是按服务类型和按语言类型两种
  • 接下来开始编码啦,用Indexer来实现上述六个需求

接口一览

  • 编码前先初步了解Indexer,直接看源码吧,毕竟稍后就要用到,结合刚刚准备好的kubernetes部署以及业务需求,这里对Indexer的接口做了详细的说明,请注意方法的注释
type Indexer interface {
	// 存储相关的,不在本章讨论
	Store
	
	// indexName表示分类方式,obj表示用来查询的对象,
	// 例如indexName等于BY_LANGUAGE,obj等于nginx的pod对象,
	// 那么Index方法就会根据BY_LANGUAGE去获取pod对象的语言类型,即c语言,再返回所有c语言类型的对象
	// 简而言之就是:查找和obj同一个语言类型的所有对象
	Index(indexName string, obj interface{}) ([]interface{}, error)

	// indexName表示分类方式,indexedValue表示分类的值,
	// 例如indexName等于BY_LANGUAGE,indexedValue等于c,
	// 那么IndexKeys方法就会返回所有语言类型等于c的对象的key
	IndexKeys(indexName, indexedValue string) ([]string, error)
	
	// indexName表示分类方式,
	// 例如indexName等于BY_LANGUAGE,
	// ListIndexFuncValues返回的就是java和c
	ListIndexFuncValues(indexName string) []string
	
	// indexName表示分类方式,indexedValue表示分类的值,
	// 例如indexName等于BY_LANGUAGE,indexedValue等于c,
	// 那么ByIndex方法就会返回所有语言类型等于c的对象
	ByIndex(indexName, indexedValue string) ([]interface{}, error)
	
	// Indexers是个map,key是分类方式,
	// 本文中key有两个,分别是BY_LANGUAGE和BY_SERVICE,
	// value则是个方法,
	// key等于BY_LANGUAGE的时候,该方法的入参是个对象pod,返回值是这个pod的语言,
	// key等于BY_SERVICE的时候,该方法的入参是个对象pod,返回值是这个pod的服务类型,
	GetIndexers() Indexers
	// 添加Indexers
	AddIndexers(newIndexers Indexers) error
}

源码下载

  • 接下来要进入的是编码环节,如果您不想编写代码,也可以从GitHub上直接下载,地址和链接信息如下表所示(github.com/zq2599/blog…
名称链接备注
项目主页github.com/zq2599/blog…该项目在GitHub上的主页
git仓库地址(https)github.com/zq2599/blog…该项目源码的仓库地址,https协议
git仓库地址(ssh)git@github.com:zq2599/blog_demos.git该项目源码的仓库地址,ssh协议
  • 这个git项目中有多个文件夹,本篇的源码在tutorials/client-go-indexer-tutorials文件夹下,如下图红框所示: 在这里插入图片描述

项目简介

  • 接下来要开发的项目,使用常见的gin作为web框架,提供web接口来实现上述六个需求
  • 为了省事儿,这边会将编译好的二进制文件直接部署在kubernetes机器上,这样运行起来就能直接用上配置文件/root/.kube/config
  • 为了使用Indexer,需要做一些初始化操作,这里提前梳理出来,稍后只要对着这个流程图实现编码即可 在这里插入图片描述

编码,搭建框架

  • 新建名为client-go-indexer-tutorials的文件夹,构建一个新的module
go mod init client-go-indexer-tutorials
  • 下载gin
go get -u github.com/gin-gonic/gin
  • main.go的内容很简单:执行kubernetes初始化相关的方法,再设定好六个web接口的handler
package main

import (
	"client-go-indexer-tutorials/basic"

	"github.com/gin-gonic/gin"
)

func main() {
	r := gin.Default()
	// kubernetes相关的初始化操作
	basic.DoInit()

	// 用于提供基本功能的路由组
	basicGroup := r.Group("/basic")

	// a. 查询指定语言的所有对象的key(演示2. IndexKeys方法)
	basicGroup.GET("get_obj_keys_by_language_name", basic.GetObjKeysByLanguageName)

	// b. 返回对象的key,返回对应的对象(演示Store.GetByKey方法)
	basicGroup.GET("get_obj_by_obj_key", basic.GetObjByObjKey)

	// c. 查询指定语言的所有对象(演示4. ByIndex方法)
	basicGroup.GET("get_obj_by_language_name", basic.GetObjByLanguageName)

	// d. 根据某个对象的key,获取同语言类型的所有对象(演示1. Index方法)
	basicGroup.GET("get_all_obj_by_one_name", basic.GetAllObjByOneName)

	// e. 返回所有语言类型(演示3. ListIndexFuncValues方法)
	basicGroup.GET("get_all_languange", basic.GetAllLanguange)

	// f. 返回所有分类方式,这里应该是按服务类型和按语言类型两种(演示5. GetIndexers方法)
	basicGroup.GET("get_all_class_type", basic.GetAllClassType)

	r.Run(":18080")
}

编码,业务功能

  • 完整的业务代码如下,已经添加了详细注释,另有几处要注意的地方,稍后会说明
package basic

import (
	"errors"
	"flag"
	"fmt"
	"log"
	"path/filepath"
	"sync"

	"github.com/gin-gonic/gin"
	v1 "k8s.io/api/core/v1"
	"k8s.io/apimachinery/pkg/api/meta"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/fields"
	"k8s.io/apimachinery/pkg/util/runtime"
	"k8s.io/client-go/kubernetes"
	"k8s.io/client-go/tools/cache"
	"k8s.io/client-go/tools/clientcmd"
	"k8s.io/client-go/util/homedir"
)

const (
	NAMESPACE      = "indexer-tutorials"
	PARAM_LANGUAGE = "language"
	PARAM_OBJ_KEY  = "obj_key"

	LANGUAGE_C = "c"

	INDEXER_LANGUAGE              = "indexer_language"
	INDEXER_BUSINESS_SERVICE_TYPE = "indexer_business_service_type"

	LABEL_LANGUAGE              = "language"
	LABEL_BUSINESS_SERVICE_TYPE = "business-service-type"
)

var ClientSet *kubernetes.Clientset
var once sync.Once
var INDEXER cache.Indexer

// DoInit Indexer相关的初始化操作,这里确保只执行一次
func DoInit() {
	once.Do(initIndexer)
}

// initIndexer 这里是真正的初始化逻辑
func initIndexer() {
	log.Println("开始初始化Indexer")

	var kubeconfig *string

	// 试图取到当前账号的家目录
	if home := homedir.HomeDir(); home != "" {
		// 如果能取到,就把家目录下的.kube/config作为默认配置文件
		kubeconfig = flag.String("kubeconfig", filepath.Join(home, ".kube", "config"), "(optional) absolute path to the kubeconfig file")
	} else {
		// 如果取不到,就没有默认配置文件,必须通过kubeconfig参数来指定
		kubeconfig = flag.String("kubeconfig", "", "absolute path to the kubeconfig file")
	}

	// 加载配置文件
	config, err := clientcmd.BuildConfigFromFlags("", *kubeconfig)
	if err != nil {
		panic(err.Error())
	}

	// 用clientset类来执行后续的查询操作
	ClientSet, err = kubernetes.NewForConfig(config)
	if err != nil {
		panic(err.Error())
	}

	log.Println("kubernetes配置文件加载成功")

	// 确定从apiserver订阅的类型
	podListWatcher := cache.NewListWatchFromClient(ClientSet.CoreV1().RESTClient(), "pods", NAMESPACE, fields.Everything())

	// Indexers对象的类型是map,key是自定义字符串,value是个function,用于根据业务逻辑返回一个对象的字符串
	indexers := cache.Indexers{
		INDEXER_LANGUAGE: func(obj interface{}) ([]string, error) {
			var object metav1.Object
			object, err = meta.Accessor(obj)
			if err != nil {
				return []string{}, nil
			}

			labelValue := object.GetLabels()[LABEL_LANGUAGE]
			if labelValue == "" {
				return []string{}, nil
			}
			return []string{labelValue}, nil
		},
		INDEXER_BUSINESS_SERVICE_TYPE: func(obj interface{}) ([]string, error) {
			var object metav1.Object
			object, err = meta.Accessor(obj)
			if err != nil {
				return []string{}, nil
			}

			labelValue := object.GetLabels()[LABEL_BUSINESS_SERVICE_TYPE]
			if labelValue == "" {
				return []string{}, nil
			}
			return []string{labelValue}, nil
		},
	}

	var informer cache.Controller

	INDEXER, informer = cache.NewIndexerInformer(podListWatcher, &v1.Pod{}, 0, cache.ResourceEventHandlerFuncs{}, indexers)

	log.Println("Indexer初始化成功")

	stopCh := make(chan struct{})

	// informer的Run方法执行后,就开始接受apiserver推送的资源变更事件,并更新本地存储
	go informer.Run(stopCh)

	// 等待本地存储和apiserver完成同步
	if !cache.WaitForCacheSync(stopCh, informer.HasSynced) {
		err = errors.New("timed out waiting for caches to sync")
		runtime.HandleError(err)
		return
	}

	log.Println("pod加载完成")

}

// language 辅助方法,从请求参数中获取语言类型,默认返回c
func language(c *gin.Context) string {
	return c.DefaultQuery(PARAM_LANGUAGE, LANGUAGE_C)
}

// objKey 辅助方法,从请求参数中获取对象key
func objKey(c *gin.Context) string {
	return c.DefaultQuery(PARAM_OBJ_KEY, "")
}

// getObjKeysByLanguageName a. 查询指定语言的所有对象的key(演示2. IndexKeys方法)
func GetObjKeysByLanguageName(c *gin.Context) {
	language := language(c)

	v, err := INDEXER.IndexKeys(INDEXER_LANGUAGE, language)

	if err != nil {
		c.String(500, fmt.Sprintf("a. get pod failed, %v", err))
	} else if nil == v || len(v) < 1 {
		c.String(500, fmt.Sprintf("a. get empty pod, %v", err))
	} else {
		m := make(map[string][]string)
		m["language"] = v
		c.JSON(200, m)
	}
}

// GetObjByObjKey b. 根据对象的key返回(演示Store.Get方法)
func GetObjByObjKey(c *gin.Context) {
	rawObj, exists, err := INDEXER.GetByKey(objKey(c))

	if err != nil {
		c.String(500, fmt.Sprintf("b. get pod failed, %v", err))
	} else if !exists {
		c.String(500, fmt.Sprintf("b. get empty pod, %v", err))
	} else {
		if v, ok := rawObj.(*v1.Pod); ok {
			c.JSON(200, v)
		} else {
			c.String(500, "b. convert interface to pod failed")
		}
	}
}

// getObjByLanguageName c. 查询指定语言的所有对象(演示4. ByIndex方法)
func GetObjByLanguageName(c *gin.Context) {
	v, err := INDEXER.ByIndex(INDEXER_LANGUAGE, language(c))

	if err != nil {
		c.String(500, fmt.Sprintf("c. get pod failed, %v", err))
	} else if v == nil {
		c.String(500, fmt.Sprintf("c. get empty pod, %v", err))
	} else {
		m := make(map[string][]interface{})
		m["language"] = v
		c.JSON(200, m)
	}
}

// getAllObjByOneName d. 根据某个对象的key,获取同语言类型的所有对象(演示1. Index方法)
func GetAllObjByOneName(c *gin.Context) {
	// 注意,Index方法的第二个入参是对象,所以这里要先根据对象key查询到对象,然后再调用Index方法
	rawObj, exists, err := INDEXER.GetByKey(objKey(c))

	if err != nil {
		c.String(500, fmt.Sprintf("d1. get pod failed, %v", err))
	} else if !exists {
		c.String(500, fmt.Sprintf("d1. get empty pod, %v", err))
	} else {
		// 先得到pod对象,再根据pod对象查询同类型的所有对象
		if podObj, ok := rawObj.(*v1.Pod); ok {
			rawArray, err := INDEXER.Index(INDEXER_LANGUAGE, podObj)

			if err != nil {
				c.String(500, fmt.Sprintf("d2. get pod failed, %v", err))
			} else if len(rawArray) < 1 {
				c.String(500, fmt.Sprintf("d2. get empty pod, %v", err))
			} else {
				m := make(map[string][]interface{})
				m["language"] = rawArray
				c.JSON(200, m)
			}
		} else {
			c.String(500, "d1. convert interface to pod failed")
		}
	}
}

// getAllClassType e. 返回所有语言类型(演示3. ListIndexFuncValues方法)
func GetAllLanguange(c *gin.Context) {
	languages := INDEXER.ListIndexFuncValues(INDEXER_LANGUAGE)

	m := make(map[string][]string)
	m["language"] = languages

	c.JSON(200, m)
}

// getAllClassType f. 返回所有分类方式,这里应该是按服务类型和按语言类型两种(演示5. GetIndexers方法)
func GetAllClassType(c *gin.Context) {
	indexers := INDEXER.GetIndexers()
	// indexers是个map,其value是cache.IndexFunc类型,无法被序列化,所以这里只返回key
	names := make([]string, 0)
	for key, _ := range indexers {
		names = append(names, key)
	}
	c.JSON(200, names)
}
  • 上述代码有以下几处需要注意
  1. 注意区分Indexers和Indexer,两者不是同一个东西,Indexers是个map,Indexer提供了各种查询API,Indexer是本篇我们要重点关注的内容
  2. 对应Indexers,key是分类方式的名称,这个可以自己起名字,value是方法,用于自定义如何设定该对象在此分类下的的结果,举个例子,如果按照语言分类,那么key等于"indexer_language",value是个方法,该方法用于返回一个pod的label值,该label就是"language"
  3. Indexers中的func,让沉迷于Java的我想起了一段Java代码,就是自定义排序,也是自己写好具体的排序逻辑,至于如何使用这个逻辑就无需关心了,只要调用Arrays.sort就好,如下所示
public class CustomSort {
    public static void main(String[] args) {
        String[] arr = {"banana", "apple", "orange", "pear"};
        
        // 使用自定义排序方法进行排序
        Arrays.sort(arr, new CustomComparator());
        
        // 输出排序后的数组
        for (String str : arr) {
            System.out.print(str + " ");
        }
    }
}

class CustomComparator implements Comparator<String> {
    @Override
    public int compare(String s1, String s2) {
        // 按照字符串长度进行排序
        return Integer.compare(s1.length(), s2.length());
    }
}

编码,运行脚本

  • 如果您使用的是vscode,可以把launch.json配置如下,以便使用运行和调试功能
{
    "version": "0.2.0",
    "configurations": [
        {
            "name": "Launch Package",
            "type": "go",
            "request": "launch",
            "mode": "auto",
            "program": "${workspaceFolder}"
        }
    ]
}

编码,测试脚本

  • 写到这里,其实可以运行程序,然后用postman或者直接用浏览器来访问接口验证效果了,不过为了将操作也能归档,以便今后也能随时再次操作,我这里使用了vscode的REST Client插件,把所有web请求放在脚本中,借助插件来完成请求、获取响应,您若对REST Client没有兴趣可以跳过这段
  • 新建hello.http文件,内容如下
### 变量,这是一个pod的对象key,和pod是一对一的关系
@obj_key=indexer-tutorials/nginx-deployment-87945df85-vzjgv

### 测试用例a. 查询指定语言的所有对象的key(演示2. IndexKeys方法)
GET http://192.168.50.76:18080/basic/get_obj_keys_by_language_name?language=c

### 测试用例b. 返回对象的key,返回对应的对象(演示Store.GetByKey方法)
GET http://192.168.50.76:18080/basic/get_obj_by_obj_key?obj_key={{obj_key}}

### 测试用例c. 查询指定语言的所有对象(演示4. ByIndex方法)
GET http://192.168.50.76:18080/basic/get_obj_by_language_name?language=c

### 测试用例d. 根据某个对象的key,获取同语言类型的所有对象(演示1. Index方法)
GET http://192.168.50.76:18080/basic/get_all_obj_by_one_name?obj_key={{obj_key}}

### 测试用例e. 返回所有语言类型(演示3. ListIndexFuncValues方法)
GET http://192.168.50.76:18080/basic/get_all_languange

### 测试用例f. 返回所有分类方式,这里应该是按服务类型和按语言类型两种(演示5. GetIndexers方法)
GET http://192.168.50.76:18080/basic/get_all_class_type

验证,查询指定语言的所有对象的key(演示2. IndexKeys方法)

  • 运行程序,可见如果和kubernetes同机部署,首次加载全量数据也不会消耗太长时间 在这里插入图片描述
  • 测试,第一个请求如下
http://192.168.50.76:18080/basic/get_obj_keys_by_language_name?language=c
  • 如果是REST Client,点击下图黄色箭头位置即可 在这里插入图片描述
  • 收到响应如下,可见编程语言是c的pod一共有四个,一个mysql,三个nginx的,它们的key都在JSON数组中返回,符合预期,
HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
Date: Wed, 14 Jun 2023 22:56:37 GMT
Content-Length: 219
Connection: close

{
  "language": [
    "indexer-tutorials/mysql-556b999fd8-22hqh",
    "indexer-tutorials/nginx-deployment-696cc4bc86-2rqcg",
    "indexer-tutorials/nginx-deployment-696cc4bc86-bkplx",
    "indexer-tutorials/nginx-deployment-696cc4bc86-m7wwh"
  ]
}
  • 现在拿到了对象的key,可见是namespace和pod name拼接而成,接下来试试用key得到对象,这是Store的基本功能

验证,查询指定语言的所有对象的key(演示Store.GetByKey方法)

  • 如果REST Client,可以用变量来保存对象key,这样便于维护,如下图 在这里插入图片描述
  • 根据对象key查询资源对象的path
http://192.168.50.76:18080/basic/get_obj_by_obj_key?obj_key=indexer-tutorials/nginx-deployment-696cc4bc86-2rqcg
  • 响应如下(篇幅所限只显示部分),可见是一个完整的pod信息
HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
Date: Wed, 14 Jun 2023 23:19:50 GMT
Connection: close
Transfer-Encoding: chunked

{
  "metadata": {
    "name": "nginx-deployment-696cc4bc86-2rqcg",
    "generateName": "nginx-deployment-696cc4bc86-",
    "namespace": "indexer-tutorials",
    "uid": "f079b9e3-2b0f-4090-8d0f-5bab4fa2eeda",
    "resourceVersion": "1341749",
    "creationTimestamp": "2023-06-12T23:04:58Z",
    "labels": {
      "app": "nginx-app",
      "business-service-type": "web",
      "language": "c",
      "pod-template-hash": "696cc4bc86",
      "type": "front-end"
    },
    ...

验证, 查询指定语言的所有对象(演示4. ByIndex方法)

  • 刚才咱们试过了get_obj_keys_by_language_name,可以根据语言查找对象的key,现在来试试根据语言查对象
http://192.168.50.76:18080/basic/get_obj_by_language_name?language=c
  • 响应(篇幅所限只显示部分)
HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
Date: Wed, 14 Jun 2023 23:25:00 GMT
Connection: close
Transfer-Encoding: chunked

{
  "language": [
    {
      "metadata": {
        "name": "mysql-556b999fd8-22hqh",
        "generateName": "mysql-556b999fd8-",
        "namespace": "indexer-tutorials",
        "uid": "ac7ca6a2-f463-450d-848a-f3a2ea6a02df",
        "resourceVersion": "1341711",
        "creationTimestamp": "2023-06-12T23:04:55Z",
        "labels": {
          "app": "mysql",
          "business-service-type": "storage",
          "language": "c",
          "pod-template-hash": "556b999fd8"
        },

验证,根据某个对象的key,获取同语言类型的所有对象(演示1. Index方法)

  • 用nginx对象的key,能查到同类型语言的所有对象
http://192.168.50.76:18080/basic/get_all_obj_by_one_name?obj_key=indexer-tutorials/nginx-deployment-696cc4bc86-2rqcg
  • 响应
HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
Date: Wed, 14 Jun 2023 23:30:11 GMT
Connection: close
Transfer-Encoding: chunked

{
  "language": [
    {
      "metadata": {
        "name": "nginx-deployment-696cc4bc86-bkplx",
        "generateName": "nginx-deployment-696cc4bc86-",
        "namespace": "indexer-tutorials",
        "uid": "eba92053-bc35-4ef5-835a-1c2f054891d5",
        "resourceVersion": "1341721",
        "creationTimestamp": "2023-06-12T23:04:57Z",
        "labels": {
          "app": "nginx-app",
          "business-service-type": "web",
          "language": "c",
          "pod-template-hash": "696cc4bc86",
          "type": "front-end"
        },
	...

验证,返回所有语言类型(演示3. ListIndexFuncValues方法)

  • 可以指定分类方式后,看到该方式分类后共有哪些值
http://192.168.50.76:18080/basic/get_all_languange
  • 可见共有两种
HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
Date: Wed, 14 Jun 2023 23:32:37 GMT
Content-Length: 25
Connection: close

{
  "language": [
    "java",
    "c"
  ]
}

验证,返回所有分类方式,这里应该是按服务类型和按语言类型两种(演示5. GetIndexers方法)

  • 由于具体的分类方式是函数,无法做序列化,因此返回的是分类方式对应的name
http://192.168.50.76:18080/basic/get_all_class_type
  • 响应
HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
Date: Wed, 14 Jun 2023 23:59:56 GMT
Content-Length: 52
Connection: close

[
  "indexer_language",
  "indexer_business_service_type"
]
  • 至此,Indexer的基本功能已经体验完成,对于如何使用Indexer相信您已经掌握了,接下来咱们会进入性能篇,来看看这个理论上的高性能在实际环境中有着怎样的表现

欢迎关注掘金:程序员欣宸

学习路上,你不孤单,欣宸原创一路相伴...