• Kubernetes clientgo 源码分析 ListWatcher


    概述ListWatch 对象的创建GetterListWatchList() & Watch()

    概述

    源码版本信息

    • Project: kubernetes
    • Branch: master
    • Last commit id: d25d741c
    • Date: 2021-09-26

    ListWatcherReflector 的一个主要能力提供者,今天我们具体看下 ListWatcher 是如何实现 List()Watch() 过程的。这里我们只跟到 RESTClient 到调用层,不深入 RESTClient 本身的实现;后面有机会再单独结合 apiserver、etcd 等整体串在一起讲 k8s 里的 list-watch 机制底层原理。

    ListWatch 对象的创建

    ListWatcher 对应的新建实例函数如下:

    • client-go/tools/cache/listwatch.go:70
    1// 这里 Getter 类型的 c 对应一个 RESTClient
    2func NewListWatchFromClient(c Getter, resource string, namespace string, fieldSelector fields.Selector) *ListWatch {
    3   optionsModifier := func(options *metav1.ListOptions) {
    4      options.FieldSelector = fieldSelector.String() // 序列化成字符串
    5   }
    6   // 调用下面这个 NewFilteredListWatchFromClient() 函数
    7   return NewFilteredListWatchFromClient(c, resource, namespace, optionsModifier)
    8}

    主要逻辑在下面,list 和 watch 能力都是通过 RESTClient 提供:

     1func NewFilteredListWatchFromClient(c Getter, resource string, namespace string, optionsModifier func(options *metav1.ListOptions)) *ListWatch {
    2   // list 某个 namespace 下的某个 resource
    3   listFunc := func(options metav1.ListOptions) (runtime.Object, error) {
    4      optionsModifier(&options)
    5      return c.Get(). // RESTClient.Get() -> *request.Request
    6         Namespace(namespace).
    7         Resource(resource).
    8         VersionedParams(&options, metav1.ParameterCodec).
    9         Do(context.TODO()).
    10         Get()
    11   }
    12   // watch 某个 namespace 下的某个 resource
    13   watchFunc := func(options metav1.ListOptions) (watch.Interface, error) {
    14      options.Watch = true
    15      optionsModifier(&options)
    16      return c.Get().
    17         Namespace(namespace).
    18         Resource(resource).
    19         VersionedParams(&options, metav1.ParameterCodec).
    20         Watch(context.TODO())
    21   }
    22   return &ListWatch{ListFunc: listFunc, WatchFunc: watchFunc}
    23}

    Getter

    上面有一个 Getter 接口,看下定义:

    • client-go/tools/cache/listwatch.go:65
    1type Getter interface {
    2   Get() *restclient.Request
    3}

    这里需要一个能够获得 *restclient.Request 的方式

    我们实际使用的时候,会用 rest.Interface 接口类型的实例,这是一个相对底层的工具,封装的是 Kubernetes REST apis 相应动作:

    • client-go/rest/client.go:41
     1type Interface interface {
    2   GetRateLimiter() flowcontrol.RateLimiter
    3   Verb(verb string) *Request
    4   Post() *Request
    5   Put() *Request
    6   Patch(pt types.PatchType) *Request
    7   Get() *Request
    8   Delete() *Request
    9   APIVersion() schema.GroupVersion
    10}

    对应实现是:

    • client-go/rest/client.go:81
     1type RESTClient struct {
    2   base *url.URL
    3   versionedAPIPath string
    4   content ClientContentConfig
    5   createBackoffMgr func() BackoffManager
    6   rateLimiter flowcontrol.RateLimiter
    7   warningHandler WarningHandler
    8   Client *http.Client
    9}
    10

    Getter 接口的 Get() 方法返回的是一个 *restclient.Request 类型,Request 的用法我们直接看 ListWatch 的 New 函数里已经看到是怎么玩的了。

    至于这里的 RESTClient 和我们代码里常用的 Clientset 的关系,这里先简单举个例子介绍一下:我们在用 clientset 去 Get 一个指定名字的 DaemonSet 的时候,调用过程类似这样:

    1r.AppsV1().DaemonSets("default").Get(ctx, "test-ds", getOpt)

    这里的 Get 其实就是利用了 RESTClient 提供的能力,方法实现对应如下:

     1func (c *daemonSets) Get(ctx context.Context, name string, options v1.GetOptions) (result *v1beta1.DaemonSet, err error) {
    2    result = &v1beta1.DaemonSet{}
    3  err = c.client.Get(). // 其实就是 RESTClient.Get(),返回的是 *rest.Request 对象
    4        Namespace(c.ns).
    5        Resource("daemonsets").
    6        Name(name).
    7        VersionedParams(&options, scheme.ParameterCodec).
    8        Do(ctx).
    9        Into(result)
    10    return
    11}

    ListWatch

    上面 NewFilteredListWatchFromClient() 函数里实现了 ListFuncWatchFunc 属性的初始化,我们接着看下 ListWatch 结构体定义:

    • client-go/tools/cache/listwatch.go:57
    1type ListWatch struct {
    2   ListFunc  ListFunc
    3   WatchFunc WatchFunc
    4   // DisableChunking requests no chunking for this list watcher.
    5   DisableChunking bool
    6}

    实现的接口叫做 ListWatcher

    1type ListerWatcher interface {
    2    Lister
    3    Watcher
    4}

    这里的 Lister 是

    1type Lister interface {
    2   // List 的返回值应该是一个 list 类型对象,也就是里面有 Items 字段,里面的 ResourceVersion 可以用来 watch
    3   List(options metav1.ListOptions) (runtime.Object, error)
    4}

    这里的 Watcher 是

    1type Watcher interface {
    2   // 从指定的资源版本开始 watch
    3   Watch(options metav1.ListOptions) (watch.Interface, error)
    4}

    List() & Watch()

    最后 ListWatch 对象的 List() 和 Watch() 的实现就没有太多新内容了:

    • client-go/tools/cache/listwatch.go:103
    1func (lw *ListWatch) List(options metav1.ListOptions) (runtime.Object, error) {   // ListWatch 在 Reflector 中使用,在 Reflector 中已经有了分页逻辑,所以这里不能再添加分页相关代码   return lw.ListFunc(options)}func (lw *ListWatch) Watch(options metav1.ListOptions) (watch.Interface, error) {   return lw.WatchFunc(options)}

    (转载请保留本文原始链接 https://www.danielhu.cn)

  • 相关阅读:
    【转】设计模式总结
    【转】并行开发
    C#开发微信公众平台-就这么简单
    (转载)MVC,MVP 和 MVVM 的图示
    插入排序
    选择排序
    VS发布 错误 未能将文件 复制到
    SqlServer 更改数据库名称
    linq查询时查询语句中附带多个查询时“已有打开的与此 Command 相关联的 DataReader,必须首先将它关闭”
    不能将 CHECK_POLICY 和 CHECK_EXPIRATION 选项设为 OFF (关)
  • 原文地址:https://www.cnblogs.com/daniel-hutao/p/15424709.html
Copyright © 2020-2023  润新知