Kubernetes client-go 原始碼分析 - ListWatcher
概述ListWatch 物件的建立GetterListWatchList() & Watch()
概述
原始碼版本資訊
- Project: kubernetes
- Branch: master
- Last commit id: d25d741c
- Date: 2021-09-26
ListWatcher 是 Reflector 的一個主要能力提供者,今天我們具體看下 ListWatcher 是如何實現 List()
和 Watch()
過程的。這裡我們只跟到 RESTClient 到呼叫層,不深入 RESTClient 本身的實現;後面有機會再單獨結合 apiserver、etcd 等整體串在一起講 k8s 裡的 list-watch 機制底層原理。
ListWatch 物件的建立
ListWatcher 對應的新建例項函式如下:
- client-go/tools/cache/listwatch.go:70
1//這裡Getter型別的c對應一個RESTClient
2funcNewListWatchFromClient(cGetter,resourcestring,namespacestring,fieldSelectorfields.Selector)*ListWatch{
3optionsModifier:=func(options*metav1.ListOptions){
4options.FieldSelector=fieldSelector.String()//序列化成字串
5}
6//呼叫下面這個NewFilteredListWatchFromClient()函式
7returnNewFilteredListWatchFromClient(c,resource,namespace,optionsModifier)
8}
主要邏輯在下面,list 和 watch 能力都是通過 RESTClient 提供:
1funcNewFilteredListWatchFromClient(cGetter,resourcestring,namespacestring,optionsModifierfunc(options*metav1.ListOptions))*ListWatch{
2 //list某個namespace下的某個resource
3listFunc:=func(optionsmetav1.ListOptions)(runtime.Object,error){
4optionsModifier(&options)
5returnc.Get().//RESTClient.Get()->*request.Request
6Namespace(namespace).
7Resource(resource).
8VersionedParams(&options,metav1.ParameterCodec).
9Do(context.TODO()).
10Get()
11}
12//watch某個namespace下的某個resource
13watchFunc:=func(optionsmetav1.ListOptions)(watch.Interface,error){
14options.Watch=true
15optionsModifier(&options)
16returnc.Get().
17Namespace(namespace).
18Resource(resource).
19VersionedParams(&options,metav1.ParameterCodec).
20Watch(context.TODO())
21}
22return&ListWatch{ListFunc:listFunc,WatchFunc:watchFunc}
23}
Getter
上面有一個 Getter 介面,看下定義:
- client-go/tools/cache/listwatch.go:65
1typeGetterinterface{
2Get()*restclient.Request
3}
這裡需要一個能夠獲得 *restclient.Request
的方式
我們實際使用的時候,會用 rest.Interface
介面型別的例項,這是一個相對底層的工具,封裝的是 Kubernetes REST apis 相應動作:
- client-go/rest/client.go:41
1typeInterfaceinterface{
2GetRateLimiter()flowcontrol.RateLimiter
3Verb(verbstring)*Request
4Post()*Request
5Put()*Request
6Patch(pttypes.PatchType)*Request
7Get()*Request
8Delete()*Request
9APIVersion()schema.GroupVersion
10}
對應實現是:
- client-go/rest/client.go:81
1typeRESTClientstruct{
2base*url.URL
3versionedAPIPathstring
4contentClientContentConfig
5createBackoffMgrfunc()BackoffManager
6rateLimiterflowcontrol.RateLimiter
7warningHandlerWarningHandler
8Client*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(ctxcontext.Context,namestring,optionsv1.GetOptions)(result*v1beta1.DaemonSet,errerror){
2result=&v1beta1.DaemonSet{}
3err=c.client.Get().//其實就是RESTClient.Get(),返回的是*rest.Request物件
4Namespace(c.ns).
5Resource("daemonsets").
6Name(name).
7VersionedParams(&options,scheme.ParameterCodec).
8Do(ctx).
9Into(result)
10return
11}
ListWatch
上面 NewFilteredListWatchFromClient()
函式裡實現了 ListFunc 和 WatchFunc 屬性的初始化,我們接著看下 ListWatch 結構體定義:
- client-go/tools/cache/listwatch.go:57
1typeListWatchstruct{
2ListFuncListFunc
3WatchFuncWatchFunc
4//DisableChunkingrequestsnochunkingforthislistwatcher.
5DisableChunkingbool
6}
實現的介面叫做 ListWatcher
1typeListerWatcherinterface{
2Lister
3Watcher
4}
這裡的 Lister 是
1typeListerinterface{
2//List的返回值應該是一個list型別物件,也就是裡面有Items欄位,裡面的ResourceVersion可以用來watch
3List(optionsmetav1.ListOptions)(runtime.Object,error)
4}
這裡的 Watcher 是
1typeWatcherinterface{
2//從指定的資源版本開始watch
3Watch(optionsmetav1.ListOptions)(watch.Interface,error)
4}
List() & Watch()
最後 ListWatch 物件的 List() 和 Watch() 的實現就沒有太多新內容了:
- client-go/tools/cache/listwatch.go:103
1func(lw*ListWatch)List(optionsmetav1.ListOptions)(runtime.Object,error){//ListWatch在Reflector中使用,在Reflector中已經有了分頁邏輯,所以這裡不能再新增分頁相關程式碼returnlw.ListFunc(options)}func(lw*ListWatch)Watch(optionsmetav1.ListOptions)(watch.Interface,error){returnlw.WatchFunc(options)}
(轉載請保留本文原始連結 https://www.danielhu.cn)