網站首頁 編程語言 正文
確立目標
- 理解kubectl的核心實現之一:
Visitor Design Pattern
?訪問者模式 - 理解發送pod創建請求的細節
visitor design pattern
在設計模式中,訪問者模式的定義為:
允許一個或者多個操作應用到對象上,解耦操作和對象本身
那么,對一個程序來說,具體的表現就是:
- 表面:某個對象執行了一個方法
- 內部:對象內部調用了多個方法,最后統一返回結果
舉個例子,
- 表面:調用一個查詢訂單的接口
- 內部:先從緩存中查詢,沒查到再去熱點數據庫查詢,還沒查到則去歸檔數據庫里查詢
Visitor
我們來看看kubeadm中的訪問者模式的定義:
// Visitor 即為訪問者這個對象 type Visitor interface { Visit(VisitorFunc) error } // VisitorFunc對應這個對象的方法,也就是定義中的“操作” type VisitorFunc func(*Info, error) error
基本的數據結構很簡單,但從當前的數據結構來看,有兩個問題:
- 單個操作?可以直接調用
Visit
方法,那多個操作如何實現呢? - 在應用多個操作時,如果出現了error,該退出還是繼續應用下一個操作呢?
Chained
以下內容在staging/src/k8s.io/cli-runtime/pkg/resource
VisitorList和EagerVisitorList是將多個對象聚合為一個對象
DecoratedVisitor和ContinueOnErrorVisitor是將多個方法聚合為一個方法
FlattenListVisitor和FilteredVisitor是將對象抽象為多個底層對象,逐個調用方法
VisitorList
封裝多個Visitor為一個,出現錯誤就立刻中止并返回
// VisitorList定義為[]Visitor,又實現了Visit方法,也就是將多個[]Visitor封裝為一個Visitor type VisitorList []Visitor // 發生error就立刻返回,不繼續遍歷 func (l VisitorList) Visit(fn VisitorFunc) error { for i := range l { if err := l[i].Visit(fn); err != nil { return err } } return nil }
EagerVisitorList
封裝多個Visitor為一個,出現錯誤暫存下來,全部遍歷完再聚合所有的錯誤并返回
// EagerVisitorList 也是將多個[]Visitor封裝為一個Visitor type EagerVisitorList []Visitor // 返回的錯誤暫存到[]error中,統一聚合 func (l EagerVisitorList) Visit(fn VisitorFunc) error { errs := []error(nil) for i := range l { if err := l[i].Visit(func(info *Info, err error) error { if err != nil { errs = append(errs, err) return nil } if err := fn(info, nil); err != nil { errs = append(errs, err) } return nil }); err != nil { errs = append(errs, err) } } return utilerrors.NewAggregate(errs) }
DecoratedVisitor
這里借鑒了裝飾器的設計模式,將一個Visitor調用多個VisitorFunc方法,封裝為調用一個VisitorFunc
// 裝飾器Visitor type DecoratedVisitor struct { visitor Visitor decorators []VisitorFunc } // visitor遍歷調用decorators中所有函數,有失敗立即返回 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); err != nil { return err } } return fn(info, nil) }) }
ContinueOnErrorVisitor
// 報錯依舊繼續 type ContinueOnErrorVisitor struct { Visitor } // 報錯不立即返回,聚合所有錯誤后返回 func (v ContinueOnErrorVisitor) Visit(fn VisitorFunc) error { errs := []error{} err := v.Visitor.Visit(func(info *Info, err error) error { if err != nil { errs = append(errs, err) return nil } if err := fn(info, nil); err != nil { errs = append(errs, err) } return nil }) if err != nil { errs = append(errs, err) } if len(errs) == 1 { return errs[0] } return utilerrors.NewAggregate(errs) }
FlattenListVisitor
將runtime.ObjectTyper解析成多個runtime.Object,再轉換為多個Info,逐個調用VisitorFunc
type FlattenListVisitor struct { visitor Visitor typer runtime.ObjectTyper mapper *mapper }
FilteredVisitor
對Info資源的檢驗
// 過濾的Info type FilteredVisitor struct { visitor Visitor filters []FilterFunc } func (v FilteredVisitor) Visit(fn VisitorFunc) error { return v.visitor.Visit(func(info *Info, err error) error { if err != nil { return err } for _, filter := range v.filters { // 檢驗Info是否滿足條件,出錯則退出 ok, err := filter(info, nil) if err != nil { return err } if !ok { return nil } } return fn(info, nil) }) }
Implements
StreamVisitor
最基礎的Visitor
type StreamVisitor struct { // 讀取信息的來源,實現了Read這個接口,這個"流式"的概念,包括了常見的HTTP、文件、標準輸入等各類輸入 io.Reader *mapper Source string Schema ContentValidator }
FileVisitor
文件的訪問,包括標準輸入,底層調用StreamVisitor來訪問
type FileVisitor struct { // 表示文件路徑或者STDIN Path string *StreamVisitor }
URLVisitor
HTTP用GET方法獲取數據,底層也是復用StreamVisitor
type URLVisitor struct { URL *url.URL *StreamVisitor // 提供錯誤重試次數 HttpAttemptCount int }
KustomizeVisitor
自定義的Visitor,針對自定義的文件系統,Customize 定制,是將C轉成了K
type KustomizeVisitor struct { Path string *StreamVisitor }
發送創建Pod請求的實現細節
kubectl是怎么向kube-apiserver發送請求的呢?
send request
// 在RunCreate函數中,關鍵的發送函數 obj, err := resource. NewHelper(info.Client, info.Mapping). DryRun(o.DryRunStrategy == cmdutil.DryRunServer). WithFieldManager(o.fieldManager). Create(info.Namespace, true, info.Object) // 進入create函數,查看到 m.createResource(m.RESTClient, m.Resource, namespace, obj, options) // 對應的實現為 func (m *Helper) createResource(c RESTClient, resource, namespace string, obj runtime.Object, options *metav1.CreateOptions) (runtime.Object, error) { return c.Post(). NamespaceIfScoped(namespace, m.NamespaceScoped). Resource(resource). VersionedParams(options, metav1.ParameterCodec). Body(obj). Do(context.TODO()). Get() } /* 到這里,我們發現了2個關鍵性的定義: 1. RESTClient 與kube-apiserver交互的RESTful風格的客戶端 這個RESTClient是來自于Builder時的傳入,生成的Result,底層是一個NewClientWithOptions生成的 2. runtime.Object 資源對象的抽象,包括Pod/Deployment/Service等各類資源 3. 我們是傳入的文件,是FileVisitor來執行的,底層Builder.mapper調用Decode來生成obj(Unstructured()) */
RESTful Client
我們先來看看,與kube-apiserver交互的Client是怎么創建的
// 從傳入參數來看,數據來源于Info這個結構 r.Visit(func(info *resource.Info, err error) error{}) // 而info來源于前面的Builder,前面部分都是將Builder參數化,核心的生成為Do函數 r := f.NewBuilder(). Unstructured(). Schema(schema). ContinueOnError(). NamespaceParam(cmdNamespace).DefaultNamespace(). FilenameParam(enforceNamespace, &o.FilenameOptions). LabelSelectorParam(o.Selector). Flatten(). Do() // 大致看一下這些函數,我們可以在Unstructured()中看到getClient函數,其實這就是我們要找的函數 func (b *Builder) getClient(gv schema.GroupVersion) (RESTClient, error) // 從返回值來看,client包括默認的REST client和配置選項 NewClientWithOptions(client, b.requestTransforms...) // 這個Client會在kubernetes項目中大量出現,它是與kube-apiserver交互的核心組件,以后再深入。
Object
Object
這個對象是怎么獲取到的呢?因為我們的數據源是來自文件的,那么我們最直觀的想法就是FileVisitor
func (v *FileVisitor) Visit(fn VisitorFunc) error { // 省略讀取這塊的代碼,底層調用的是StreamVisitor的邏輯 return v.StreamVisitor.Visit(fn) } func (v *StreamVisitor) Visit(fn VisitorFunc) error { d := yaml.NewYAMLOrJSONDecoder(v.Reader, 4096) for { // 這里就是返回info的地方 info, err := v.infoForData(ext.Raw, v.Source) } } // 再往下一層看,來到mapper層,也就是kubernetes的資源對象映射關系 func (m *mapper) infoForData(data []byte, source string) (*Info, error){ // 這里就是我們返回Object的地方,其中GVK是Group/Version/Kind的縮寫,后續我們會涉及 obj, gvk, err := m.decoder.Decode(data, nil, nil) }
這時,我們想回頭去看,這個mapper是在什么時候被定義的?
// 在Builder初始化中,我們就找到了 func (b *Builder) Unstructured() *Builder { b.mapper = &mapper{ localFn: b.isLocal, restMapperFn: b.restMapperFn, clientFn: b.getClient, // 我們查找資源用到的是這個decoder decoder: &metadataValidatingDecoder{unstructured.UnstructuredJSONScheme}, } return b } // 逐層往下找,對應的Decode方法的實現,就是對應的數據解析成data: func (s unstructuredJSONScheme) decode(data []byte) (runtime.Object, error) { // 細節暫時忽略 }
Post
了解了REST Client
和Object
的大致產生邏輯后,我們再回過頭來看發送的方法
// RESTful接口風格中,POST請求對應的就是CREATE方法 c.Post(). NamespaceIfScoped(namespace, m.NamespaceScoped). Resource(resource). VersionedParams(options, metav1.ParameterCodec). Body(obj). Do(context.TODO()). Get() // Do方法,發送請求 err := r.request(ctx, func(req *http.Request, resp *http.Response) { result = r.transformResponse(resp, req) }) // Get方法,獲取請求的返回結果,用來打印狀態 switch t := out.(type) { case *metav1.Status: if t.Status != metav1.StatusSuccess { return nil, errors.FromObject(t) } }
站在前人的肩膀上,向前輩致敬,Respect!
Summary
通過Visitor的設計模式,從傳入的參數中解析出內容,然后在Factory進行NewBuilder的時候進行配置實現RESTClient,mapper,obj的生成,Do()拿到Result,組裝好POST請求發送到ApiServer。
到這里我們對kubectl的功能有了初步的了解,以下是關鍵內容所在:
命令行采用了cobra
庫,主要支持7個大類的命令;
掌握Visitor設計模式,這個是kubectl實現各類資源對象的解析和校驗的核心;
初步了解RESTClient
和Object
這兩個對象,它們是貫穿kubernetes的核心概念;
調用邏輯
- cobra匹配子命令
- 用Visitor模式構建Builder
- 用RESTClient將Object發送到kube-apiserver
原文鏈接:https://juejin.cn/post/7153983871745261582
相關推薦
- 2023-03-20 C#?獲取XML文件內容的多種方式總結_C#教程
- 2022-11-22 Kotlin對象的懶加載方式by?lazy?與?lateinit?異同詳解_Android
- 2022-11-30 Python?Django教程之實現天氣應用程序_python
- 2021-12-10 Oracle數據庫備份還原詳解_oracle
- 2022-01-11 小程序 ------ [mini-css-extract-plugin] css沖突問題
- 2023-02-23 Android開發之BottomSheetDialog組件的使用_Android
- 2022-11-10 Android拍攝照片后返回縮略圖的方法_Android
- 2023-08-16 使用el-row和el-col實現快速布局
- 最近更新
-
- window11 系統安裝 yarn
- 超詳細win安裝深度學習環境2025年最新版(
- Linux 中運行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎操作-- 運算符,流程控制 Flo
- 1. Int 和Integer 的區別,Jav
- spring @retryable不生效的一種
- Spring Security之認證信息的處理
- Spring Security之認證過濾器
- Spring Security概述快速入門
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權
- redisson分布式鎖中waittime的設
- maven:解決release錯誤:Artif
- restTemplate使用總結
- Spring Security之安全異常處理
- MybatisPlus優雅實現加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務發現-Nac
- Spring Security之基于HttpR
- Redis 底層數據結構-簡單動態字符串(SD
- arthas操作spring被代理目標對象命令
- Spring中的單例模式應用詳解
- 聊聊消息隊列,發送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠程分支