<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          訪問者模式在 Kubernetes 中的使用

          共 7012字,需瀏覽 15分鐘

           ·

          2022-02-13 11:06

          要說有哪些為我打開了高效編程之門的文章,我會(huì)說 Design Pattern by Gang of four[1] 是第一個(gè)對(duì)我?guī)椭浅4蟮?,它幫助我更好地理解各種代碼結(jié)構(gòu),更合理地編碼。當(dāng)然,它和其他很多設(shè)計(jì)模式的文章一樣,都是基于 Java 的,因?yàn)樵O(shè)計(jì)模式是很多 Java 開源框架所奉行的原則,比如常見的工廠模式、代理模式和 springframework 中的訪問者模式。

          不過也不用擔(dān)心,你學(xué)到的東西始終都會(huì)有所幫助的,我從 Java 中獲得的一些鑰匙似乎也可以在 Kubernetes 中發(fā)揮作用,比如當(dāng)我讀完 kubectl 和 k8s 的源碼后,你會(huì)發(fā)現(xiàn)它們有著類似的設(shè)計(jì)模式,雖然在實(shí)現(xiàn)上有所不同,但是思維方式是類似的。

          接下來我們來深入了解下訪問者模式,看看這把鑰匙是如何在 kubectl 和 kubernetes 中工作的,以便提升我們的日常編碼能力。

          訪問者模式被認(rèn)為是最復(fù)雜的設(shè)計(jì)模式,并且使用頻率不高,《設(shè)計(jì)模式》的作者評(píng)價(jià)為:大多情況下,你不需要使用訪問者模式,但是一旦需要使用它時(shí),那就真的需要使用了。

          訪問者模式

          下圖很好地展示了訪問者模式編碼的工作流程。

          在 Gof 中,也有關(guān)于為什么引入訪問者模式的解釋。

          訪問者模式在設(shè)計(jì)跨類層級(jí)結(jié)構(gòu)的異構(gòu)對(duì)象集合的操作時(shí)非常有用。訪問者模式允許在不更改集合中任何對(duì)象的類的情況下定義操作,為達(dá)到該目的,訪問者模式建議在一個(gè)稱為訪問者類(visitor)的單獨(dú)類中定義操作,這將操作與它所操作的對(duì)象集合分開。對(duì)于要定義的每個(gè)新的操作,都要?jiǎng)?chuàng)建一個(gè)新的訪問者類。由于操作將在一組對(duì)象上執(zhí)行,因此訪問者需要一種訪問這些對(duì)象的公共成員的方法。

          用一個(gè)實(shí)際的場(chǎng)景來解釋:基于接口的特點(diǎn),動(dòng)態(tài)地解耦對(duì)象和它們的動(dòng)作,以實(shí)現(xiàn)更多的 SOLID 原則,更少的維護(hù),增加新功能(增加新的 ConcreteVisitor)迭代更快。

          在 Go 中,訪問者模式的應(yīng)用可以做同樣的改進(jìn),因?yàn)?Interface 接口是它的主要特性之一。

          K8s 中的訪問者模式

          Kubernetes 是一個(gè)容器編排平臺(tái),上面有各種不同的資源,而 kubectl 是一個(gè)命令行工具,它使用以下命令格式來操作資源。

          kubectl?get?{resourcetype}?{resource_name}
          kubectl?edit?{resourcetype}?{resource_name}

          kubectl 將這些命令組合成 APIServer 需要的數(shù)據(jù),發(fā)起一個(gè)請(qǐng)求,并返回結(jié)果,實(shí)際上是執(zhí)行了一個(gè) builder[2] 方法,它封裝了各種訪問者來處理請(qǐng)求的參數(shù)和結(jié)果,最后得到我們?cè)诿钚猩峡吹降慕Y(jié)果。

          func?(f?*factoryImpl)?NewBuilder()?*resource.Builder?{
          ??return?resource.NewBuilder(f.clientGetter)
          }

          這里的大部分訪問者都是在 visitor.go[3] 中定義的,通過源文件的文件名也可以看出來是訪問者模式。

          關(guān)于這部分代碼,大概有700多行,它使用建造者模式(builder.go[4])和訪問者模式連接訪問者,并通過調(diào)用各自的 VisitorFunc[5] 方法來實(shí)現(xiàn)對(duì)應(yīng)的功能,同時(shí)在 builder.go 中封裝了 VisitorFunc 的具體實(shí)現(xiàn)。

          type?VisitorFunc?func(*Info,?error)?error
          type?Visitor?interface?{
          ??Visit(VisitorFunc)?error
          }
          type?Info?struct?{
          ??Namespace???string
          ??Name????????string
          ??OtherThings?string
          }
          func?(info?*Info)?Visit(fn?VisitorFunc)?error?{
          ??return?fn(info,?nil)
          }

          由于 Go 接口的特性,只要實(shí)現(xiàn)了 Visit 方法的都會(huì)被認(rèn)為是合格的訪問者,接下來我們來看看幾個(gè)典型的訪問者。

          Selector

          在 kubectl 中,我們默認(rèn)訪問的是 default 這個(gè)命名空間,但是可以使用 -n/-namespace 選項(xiàng)來指定我們要訪問的命名空間,也可以使用 -l/-label 來篩選指定標(biāo)簽的資源,該命令如下所示:

          kubectl?get?pod?pod1?-n?test?-l?abc=true

          我們就可以通過 Selector[6] 訪問者來查看對(duì)應(yīng)的實(shí)現(xiàn)。

          首先定義 Selector 的結(jié)構(gòu)體:

          type?Selector?struct?{
          ??Client????????RESTClient
          ??Mapping???????*meta.RESTMapping
          ??Namespace?????string
          ??LabelSelector?string
          ??FieldSelector?string
          ??LimitChunks???int64
          }

          然后當(dāng)然我們需要去實(shí)現(xiàn) Visit 方法,以便最終能夠構(gòu)建出合理的 Info 對(duì)象供 API 訪問。

          list,?err?:=?helper.List(
          ????r.Namespace,
          ????r.ResourceMapping().GroupVersionKind.GroupVersion().String(),
          ????&options,
          )
          if?err?!=?nil?{
          ????return?nil,?EnhanceListError(err,?options,?r.Mapping.Resource.String())
          }
          resourceVersion,?_?:=?metadataAccessor.ResourceVersion(list)

          info?:=?&Info{
          ????Client:??r.Client,
          ????Mapping:?r.Mapping,

          ????Namespace:???????r.Namespace,
          ????ResourceVersion:?resourceVersion,

          ????Object:?list,
          }

          if?err?:=?fn(info,?nil);?err?!=?nil?{
          ????return?nil,?err
          }

          DecoratedVisitor

          DecoratedVisitor[7] 包含一個(gè) Visitor 和一組裝飾器(VisitorFunc),在執(zhí)行 Visit 方法時(shí)按順序執(zhí)行所有裝飾器。

          //?DecoratedVisitor?will?invoke?the?decorators?in?order?prior?to?invoking?the?visitor?function
          //?passed?to?Visit.?An?error?will?terminate?the?visit.
          type?DecoratedVisitor?struct?{
          ?visitor????Visitor
          ?decorators?[]VisitorFunc
          }

          //?NewDecoratedVisitor?will?create?a?visitor?that?invokes?the?provided?visitor?functions?before
          //?the?user?supplied?visitor?function?is?invoked,?giving?them?the?opportunity?to?mutate?the?Info
          //?object?or?terminate?early?with?an?error.
          func?NewDecoratedVisitor(v?Visitor,?fn?...VisitorFunc)?Visitor?{
          ?if?len(fn)?==?0?{
          ??return?v
          ?}
          ?return?DecoratedVisitor{v,?fn}
          }

          //?Visit?implements?Visitor
          func?(v?DecoratedVisitor)?Visit(fn?VisitorFunc)?error?{
          ?return?v.visitor.Visit(func(info?*Info,?err?error)?error?{
          ??if?err?!=?nil?{
          ???return?err
          ??}
          ??for?i?:=?range?v.decorators?{
          ???if?err?:=?v.decorators[i](info,?nil?"i");?err?!=?nil?{
          ????return?err
          ???}
          ??}
          ??return?fn(info,?nil)
          ?})
          }

          在 builder.go 中初始化訪問者時(shí),訪問者將被添加到由結(jié)果處理的訪問者列表中,你可以在命名空間被處理后直接調(diào)用 Get 方法。

          if?b.latest?{
          ????//?must?set?namespace?prior?to?fetching
          ????if?b.defaultNamespace?{
          ????????visitors?=?NewDecoratedVisitor(visitors,?SetNamespace(b.namespace))
          ????}
          ????visitors?=?NewDecoratedVisitor(visitors,?RetrieveLatest)
          }

          //?visitor.go:?RetrieveLatest?updates?the?Object?on?each?Info?by?invoking?a?standard?client
          //?Get.
          func?RetrieveLatest(info?*Info,?err?error)?error?{
          ?if?err?!=?nil?{
          ??return?err
          ?}
          ?if?meta.IsListType(info.Object)?{
          ??return?fmt.Errorf("watch?is?only?supported?on?individual?resources?and?resource?collections,?but?a?list?of?resources?is?found")
          ?}
          ?if?len(info.Name)?==?0?{
          ??return?nil
          ?}
          ?if?info.Namespaced()?&&?len(info.Namespace)?==?0?{
          ??return?fmt.Errorf("no?namespace?set?on?resource?%s?%q",?info.Mapping.Resource,?info.Name)
          ?}
          ?return?info.Get()
          }

          在代碼中也有一些類似的訪問者,用于處理不同的邏輯,這種設(shè)計(jì)模式的一個(gè)明顯的好處是操作簡(jiǎn)單?;旧?,所有的資源對(duì)象都符合這種基于 GKV 的操作,所以在添加訪問者時(shí),不需要修改 visitor.go,相反,只要實(shí)現(xiàn)了 VisitorFunc 接口,就可以直接添加新的 go 文件,然后在構(gòu)建器構(gòu)建期間添加相關(guān)邏輯即可。

          練習(xí)

          我和同事們定制了很多 CRD,編寫了一些 Operator,并在 Kubernetes 集群中運(yùn)行提供不同的服務(wù),比如安全、RBAC 自動(dòng)添加、SA 自動(dòng)創(chuàng)建等功能。

          這些 CRD 都有不同的字段屬性,例如:

          • GroupRbac:包含組名、電子郵件和用戶列表
          • Identity:包含組名,以及相關(guān)的角色綁定狀態(tài)

          由于厭倦了重復(fù)的使用 kubectl get grouprbac xxxkubectl get identity xxx,所以我決定創(chuàng)建一個(gè) kubectl 插件,用 kubectl groupget {groupName} 來獲取它們。當(dāng)然我們可以直接使用最簡(jiǎn)單的 Bash 來實(shí)現(xiàn),但是如果增加更多的資源,那么慢慢就會(huì)變得難以維護(hù)和擴(kuò)展了,所以我決定使用 Go 來實(shí)現(xiàn)它。

          現(xiàn)在讓我們回到訪問者模式上面來,在處理資源訪問時(shí),我定義了一組訪問者,它們可以用來訪問不同的資源,代碼結(jié)構(gòu)如下所示:

          type?VisitorFunc?func(*Info,?error)?error

          type?GroupRbacVisitor?struct?{
          ??visitor?Visitor
          ??results?map[string]GroupResult
          }

          func?(v?GroupRbacVisitor)?Visit(fn?VisitorFunc)?error?{
          ?return?v.visitor.Visit(func(info?*Info,?err?error)?error?{
          ???//?...
          ?}
          }

          type?IdentityVisitor?struct?{
          ??visitor?Visitor
          ??results?map[string]IdentityResult
          }

          func?(v?IdentityVisitor)?Visit(fn?VisitorFunc)?error?{
          ??return?v.visitor.Visit(func(info?*Info,?err?error)?error?{
          ????//?...
          ??}
          }

          每次得到的結(jié)果都存儲(chǔ)在各自的結(jié)果中,并要最終收集和處理,而每當(dāng)有新的資源要添加時(shí),我只需要定義一個(gè)新的訪問者,編寫相應(yīng)的 Visit 訪問方法,可能還要稍微調(diào)整最終的顯示邏輯即可,是不是超級(jí)方便!

          func?FetchAll(c?*cobra.Command,?visitors?[]Visitor)?error?{
          ??//?...
          ??for?_,?visitor?:=?range?visitors?{
          ????v.Visit(func(*Info,?error)?error?{
          ????//...
          ???})
          ??}
          //?...
          }

          總結(jié)

          我們從來沒有停止過探索編寫更易于閱讀、維護(hù)和擴(kuò)展的代碼的方法,我相信學(xué)習(xí)、理解和實(shí)踐設(shè)計(jì)模式是可以讓我們更接近目標(biāo)的途徑之一,希望本文對(duì)你的也有所幫助。

          原文鏈接:https://medium.com/geekculture/visitor-pattern-in-kubernetes-d1b58c6d5cd5

          參考資料

          [1]

          Design Pattern by Gang of four: https://www.gofpatterns.com/index.php

          [2]

          builder: https://github.com/kubernetes/kubernetes/blob/ea0764452222146c47ec826977f49d7001b0ea8c/staging/src/k8s.io/kubectl/pkg/cmd/util/factory_client_access.go#L94

          [3]

          visitor.go: https://github.com/kubernetes/kubernetes/blob/cea1d4e20b4a7886d8ff65f34c6d4f95efcb4742/staging/src/k8s.io/cli-runtime/pkg/resource/visitor.go

          [4]

          builder.go: https://github.com/kubernetes/kubernetes/blob/fafbe3aa51473a70980e04ae19f7db2d32d7365b/staging/src/k8s.io/cli-runtime/pkg/resource/builder.go

          [5]

          VisitorFunc: https://github.com/kubernetes/kubernetes/blob/ea0764452222146c47ec826977f49d7001b0ea8c/staging/src/k8s.io/cli-runtime/pkg/resource/interfaces.go#L103

          [6]

          Selector: https://github.com/kubernetes/kubernetes/blob/fafbe3aa51473a70980e04ae19f7db2d32d7365b/staging/src/k8s.io/cli-runtime/pkg/resource/selector.go#L27

          [7]

          DecoratedVisitor: https://github.com/kubernetes/kubernetes/blob/cea1d4e20b4a7886d8ff65f34c6d4f95efcb4742/staging/src/k8s.io/cli-runtime/pkg/resource/visitor.go#L309

          瀏覽 71
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  日韩第1页| 99视频在线免费观看视频 | 久久国产精品国语对白 | 色校园第一页 | 欧美成人精品一区二区三区在线观看 |