client-go 实战之四:dynamicClient
您可能会好奇上述 FromUnstructured 方法究竟是如何实现转换的,咱们去看下此方法的内部实现,如下图所示,其实也没啥悬念了,通过反射可以得到 podList 的字段信息:
至此,Unstructured 的分析就结束了吗?没有,强烈推荐您进入上图红框 2 中的 fromUnstructured 方法去看细节,这里面是非常精彩的,以 podList 为例,这是个数据结构,而 fromUnstructured 只处理原始类型,对于数据结构会调用 structFromUnstructured 方法处理,在 structFromUnstructured 方法中
处理数据结构的每个字段,又会调用 fromUnstructured,这是相互迭代的过程,最终,不论 podList 中有多少数据结构的嵌套都会被处理掉,篇幅所限就不展开相信分析了,下图是一部分关键代码:
小结:Unstructured 转为资源对象的套路并不神秘,无非是用反射取得资源对象的字段类型,然后按照字段名去 Unstructured 的 map 中取得原始数据,再用反射设置到资源对象的字段中即可;
做完了准备工作,接下来该回到本篇文章的主题了:dynamicClient 客户端
[](()关于 dynamicClient
deployment、pod 这些资源,其数据结构是明确的固定的,可以精确对应到 Clientset 中的数据结构和方法,但是对于 CRD(用户自定义资源),Clientset 客户端就无能为力了,此时需要有一种数据结构来承载资源对象的数据,也要有对应的方法来处理这些数据;
此刻,前面提到的 Unstructured 可以登场了,没错,把 Clientset 不支持的资源对象交给 Unstructured 来承载,接下来看看 dynamicClient 和 Unstructured 的关系:
先看数据结构定义,和 clientset 没啥区别,只有个 restClient 字段:
type dynamicClient struct {
client *rest.RESTClient
}
这个数据结构只有一个关联方法 Resource,入参为 GVR,返回的是另一个数据结构 dynamicResourceClient:
func (c *dynamicClient) Resource(resource schema.GroupVersionResource) NamespaceableResourceInterface {
return &dynamicResourceClient{client: c, resource: resource}
}
通过上述代码可知,dynamicClient 的关键是数据结构 dynamicResourceClient 及其关联方法,来看看这个 dynamicResourceClient,如下图,果然,dynamicClient 所有和资源相关的操作都是 dynamicResourceClient 在做(代理模式?),选了 create 方法细看,序列化和反序列化都交给 unstructured 的 UnstructuredJSONScheme,与 kubernetes 的交互交给 Restclient:
小结:
与 Clientset 不同,dynamicClient 为各种类型的资源都提供统一的操作 API,资源需要包装为 Unstructured 数据结构;
内部使用了 Restclient 与 kubernetes 交互;
对 dynamicClient 的介绍分析就这些吧,可以开始实战了;
[](()需求确认
本次编码实战的需求很简单:查询指定 namespace 下的所有 pod,然后在控制台打印出来,要求用 dynamicClient 实现;
您可能会问:pod 是 kubernetes 的内置资源,更适合 Clientset 来操作,而 dynamicClient 更适合处理 CRD 不是么?—您说得没错,这里用 pod 是因为折腾 CRD 太麻烦了,定义好了还要在 kubernetes 上发布,于是干脆用 pod 来代替 CRD,反正 dynamicClient 都能处理,咱们通过实战掌握 dynamicClient 的用法就行了,以后遇到各种资源都能处理之;
[](()源码下载
本篇实战中的源码可在 GitHub 下载到,地址和链接信息如下表所示(https://github.com/zq2599/blog_demos):
| 名称 | 链接 | 备注 |
| :-- | :-- | :-- |
| 项目主页 | https://github.com/zq2599/blog_demos | 该项目在 GitHub 上的主页 |
| git 仓库地址(https) | https://github.com/zq2599/blog_demos.git | 该项目源码的仓库地址,https 协议 |
| git 仓库地址(ssh) | git@github.com:zq2599/blog_demos.git | 该项目源码的仓库地址,ssh 协议 |
这个 git 项目中有多个文件夹,client-go 相关的应用在 client-go-tutorials 文件夹下,如下图红框所示:
![在这里插入图片描述](https://img-blog.csdnimg.cn/20210210175040851.png?x-oss-process=image/waterma 《一线大厂 Java 面试题解析+后端开发学习笔记+最新架构讲解视频+实战项目源码讲义》开源 rk,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2JvbGluZ19jYXZhbHJ5,size_16,color_FFFFFF,t_70)
client-go-tutorials 文件夹下有多个子文件夹,本篇对应的源码在 dynamicclientdemo 目录下,如下图红框所示:
[](()编码
新建文件夹 dynamicclientdemo,在里面执行以下命令,新建 module:
go mod init dynamicclientdemo
添加 k8s.io/api 和 k8s.io/client-go 这两个依赖,注意版本要匹配 kubernetes 环境:
go get k8s.io/api@v0.20.0
go get k8s.io/client-go@v0.20.0
新建 main.go,内容如下,稍后会说一下要注意的重点:
package main
import (
"context"
"flag"
"fmt"
apiv1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/client-go/dynamic"
"k8s.io/client-go/tools/clientcmd"
"k8s.io/client-go/util/homedir"
"path/filepath"
)
func main() {
var kubeconfig *string
// home 是家目录,如果能取得家目录的值,就可以用来做默认值
if home:=homedir.Ho Java 开源项目【ali1024.coding.net/public/P7/Java/git】 meDir(); home != "" {
// 如果输入了 kubeconfig 参数,该参数的值就是 kubeconfig 文件的绝对路径,
// 如果没有输入 kubeconfig 参数,就用默认路径~/.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")
}
flag.Parse()
// 从本机加载 kubeconfig 配置文件,因此第一个参数为空字符串
config, err := clientcmd.BuildConfigFromFlags("", *kubeconfig)
// kubeconfig 加载失败就直接退出了
if err != nil {
panic(err.Error())
}
dynamicClient, err := dynamic.NewForConfig(config)
if err != nil {
panic(err.Error())
}
// dynamicClient 的唯一关联方法所需的入参
gvr := schema.GroupVersionResource{Version: "v1", Resource: "pods"}
// 使用 dynamicClient 的查询列表方法,查询指定 namespace 下的所有 pod,
// 注意此方法返回的数据结构类型是 UnstructuredList
unstructObj, err := dynamicClient.
Resource(gvr).
Namespace("kube-system").
List(context.TODO(), metav1.ListOptions{Limit: 100})
if err != nil {
panic(err.Error())
}
// 实例化一个 PodList 数据结构,用于接收从 unstructObj 转换后的结果
最后
总而言之,面试官问来问去,问的那些 Redis 知识点也就这么多吧,复习的不够到位,知识点掌握不够熟练,所以面试才会卡壳。将这些 Redis 面试知识解析以及我整理的一些学习笔记分享出来给大家参考学习
还有更多学习笔记面试资料也分享如下:
评论