参考
利用 Scheme 反序列化的理解
0 | 前置知识 —— restful 访问 k8s 资源
-
集群作用域的资源:
GET /apis/GROUP/VERSION/RESOURCETYPE
- 返回指定资源类型的资源的集合GET /apis/GROUP/VERSION/RESOURCETYPE/NAME
- 返回指定资源类型下名称为 NAME 的资源
-
名字空间作用域的资源:
GET /apis/GROUP/VERSION/RESOURCETYPE
- 返回所有名字空间中指定资源类型的全部实例的集合GET /apis/GROUP/VERSION/namespaces/NAMESPACE/RESOURCETYPE
- 返回名字空间 NAMESPACE 内给定资源类型的全部实例的集合GET /apis/GROUP/VERSION/namespaces/NAMESPACE/RESOURCETYPE/NAME
- 返回名字空间 NAMESPACE 中给定资源类型的名称为 NAME 的实例
-
某些资源类型有一个或多个子资源(Sub-resource),表现为对应资源下面的子路径:
- 集群作用域的子资源:
GET /apis/GROUP/VERSION/RESOURCETYPE/NAME/SUBRESOURCE
- 名字空间作用域的子资源:`GET /apis/GROUP/VERSION/namespaces/NAMESPACE/RESOURCETYPE/NAME/SUBRESOURCE
- 集群作用域的子资源:
-
例子
-
列举给定名字空间中的所有 Pods:
-
GET /api/v1/namespaces/test/pods --- 200 OK Content-Type: application/json { "kind": "PodList", "apiVersion": "v1", "metadata": {"resourceVersion":"10245"}, "items": [...] }
-
从访问的 url
GET /api/v1/namespaces/test/pods
即可得知资源的 GVK(core(核心组忽略显示,就是这么规定的),v1,pod) -
可以看出发起请求,相应给出了相关 Pod 信息,而其信息的存储是通过相应的数据结构存储的
- 不难看出,不同资源就会有不同的数据存储结构
- 那么如何从 url 获取的 GVK 获取到响应的数据信息呢?就是如何将 GVK 与 响应的资源数据结构做对应呢?
- 答:Scheme ,下面将进行详细讲解
-
1 | 为什么需要 Scheme
- 因为在web开发中随着版本的更新迭代,通常要在系统中维护多个版本的api,多个版本的api在数据结构上往往也各不相同
- 为了解决上述问题 —— 出现了 Scheme ——
实现 GVK 与 api数据结构的对应
2 | web 请求的处理流程
- 收到请求后,通常首先是webServer先进行Http协议的处理
- 解析成基础的webServer内部的一个Http请求对象
- 该 Http 请求对象持有对应请求的请求头和底层对应的字节序列(从socket流中读取)
- 接着根据对应的编码格式来进行反序列化
- 完成从字节序列到当前接口的业务模型的映射, 然后在交给业务逻辑处理
- 从而最终进行持久化存储, 本文的重点也就在反序列化部分
3 | 模型映射的实现
3.1 | 描述资源版本信息
/api/{version}/{resource}/{action}
- 上面是一个基础的web url通常我们都会为每个版本注册一个对应的URL
- 其中会包含很关键的两个信息即version与resource
- 通过这两个信息,通常我们就可以知道这可能是某个资源的那个版本
- 如果我们把后面的action也包裹进来,我们通常就可以知道对应的资源的那个具体操作
3.2 | Group 组信息
在微服务流行的今天我们通常会为按照业务功能来进行微服务的切分,本质上一个微服务可能就是实现某个具体业务场景的功能集合,比如用户系统通常会包含用户的所有相关操作,在kubernetes中也有类似的概念就是所谓的Group
POST /apis/batch/v1beta1/namespaces/{namespace}/cronjobs
POST /apis/apps/v1/namespaces/{namespace}/daemonsets
我们来看一个实例这是一个创建daemonsets和cronjobs的url, 如果按照Group、resource、version来进行拆分可以拆成如下:batch、v1beta1、cronjobs和apps、v1、daemonsets,也就是大家尝试的GroupVersionKind,其中kind对应的就是resource
3.3 | 模型映射的实现
- 我们通过url里面获取到资源的GroupVersionKind信息,如何将其映射为一个具体的类型呢?
- 这貌似就很简单了结合反射和map来进行就可以了,我们通过url获取到对应想的GVK信息,然后在通过我们的映射表,就知道对应的模型是哪个,接下来就只需要进行转换就行了
gvkToType map[schema.GroupVersionKind]reflect.Type
4 | 反序列化实现
4.1 | 解码机制
- 那如何将对应的Http里面的数据流反序列化成内部的一个对象呢?
- 别忘记了是Http协议, 肯定就是header头里面的信息了
- 通过header头里面的序列化就可以知道对应的编码格式,只需要调用对应格式的解码就可以完成了
Content-Type: "application/json"
4.2 | 默认对象
- 如果要将json格式的字节数组进行解码通常要进行如下操作:
- 需要传入一个目标对象的指针,然后由json将对应的字节数据解析到目标对象中,我们也需要这样一个对象,用于存储反序列化的结果
func Unmarshal(data []byte, v interface{}) error {}
- 该目标存储对象如何获取呢?
- 那只要我再提供一个当前版本对应的对象构造函数是不是就可以呢?答案是的
- 构造函数,通过 GVK 调用相应的构造函数,即可构造出对应版本的目标对象
func() Object{ return 目标对象 },
5 | 总览
- 在 webserver 上注册 url 时,即完成了 url 与 资源的版本信息GroupVersionKind(GVK) 映射关系的构建
- 之后收到请求进行处理时
- 通过 url 获取到 GVK
- 根据 GVK 调用相应构造函数,创建目标资源对象
- 根据请求的 Header 得知编码格式,获取对应的解码器
- 将Body里面的字节序列进行解码到目标对象
- 以上流程就可以实现多版本资源的映射和反序列化操作了
Scheme 的其他作用
- schema 是 kubernetes 资源管理的核心数据结构。由以前文章我们了解到 kubernetes 会将其管理的资源划分为 group/version/kind 的概念,scheme 可以利用进行 GVK 进行一下操作:
- 可以将资源在内部版本和其他版本中相互转化
- 可以序列化和反序列化的过程中识别资源类型,创建资源对象,设置默认值等等。
- 这些 group/version/kind 和资源 model 的对应关系,资源 model 的默认值函数,不同版本之间相互转化的函数等等全部由 schema 维护。可以说 schema 是组织 kubernetes 资源的核心
具体讲解见下一节