<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
Visitor Design Pattern
存取者模式在設計模式中,存取者模式的定義為:
允許一個或者多個操作應用到物件上,解耦操作和物件本身
那麼,對一個程式來說,具體的表現就是:
舉個例子,
我們來看看kubeadm中的存取者模式的定義:
// Visitor 即為存取者這個物件 type Visitor interface { Visit(VisitorFunc) error } // VisitorFunc對應這個物件的方法,也就是定義中的「操作」 type VisitorFunc func(*Info, error) error
基本的資料結構很簡單,但從當前的資料結構來看,有兩個問題:
Visit
方法,那多個操作如何實現呢?以下內容在staging/src/k8s.io/cli-runtime/pkg/resource
VisitorList和EagerVisitorList是將多個物件聚合為一個物件
DecoratedVisitor和ContinueOnErrorVisitor是將多個方法聚合為一個方法
FlattenListVisitor和FilteredVisitor是將物件抽象為多個底層物件,逐個呼叫方法
封裝多個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 }
封裝多個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) }
這裡借鑑了裝飾器的設計模式,將一個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) }) }
// 報錯依舊繼續 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) }
將runtime.ObjectTyper解析成多個runtime.Object,再轉換為多個Info,逐個呼叫VisitorFunc
type FlattenListVisitor struct { visitor Visitor typer runtime.ObjectTyper mapper *mapper }
對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) }) }
最基礎的Visitor
type StreamVisitor struct { // 讀取資訊的來源,實現了Read這個介面,這個"流式"的概念,包括了常見的HTTP、檔案、標準輸入等各類輸入 io.Reader *mapper Source string Schema ContentValidator }
檔案的存取,包括標準輸入,底層呼叫StreamVisitor來存取
type FileVisitor struct { // 表示檔案路徑或者STDIN Path string *StreamVisitor }
HTTP用GET方法獲取資料,底層也是複用StreamVisitor
type URLVisitor struct { URL *url.URL *StreamVisitor // 提供錯誤重試次數 HttpAttemptCount int }
自定義的Visitor,針對自定義的檔案系統,Customize 客製化,是將C轉成了K
type KustomizeVisitor struct { Path string *StreamVisitor }
kubectl是怎麼向kube-apiserver傳送請求的呢?
// 在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()) */
我們先來看看,與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
這個物件是怎麼獲取到的呢?因為我們的資料來源是來自檔案的,那麼我們最直觀的想法就是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) { // 細節暫時忽略 }
瞭解了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!
通過Visitor的設計模式,從傳入的引數中解析出內容,然後在Factory進行NewBuilder的時候進行設定實現RESTClient,mapper,obj的生成,Do()拿到Result,組裝好POST請求傳送到ApiServer。
到這裡我們對kubectl的功能有了初步的瞭解,以下是關鍵內容所在:
命令列採用了cobra
庫,主要支援7個大類的命令;
掌握Visitor設計模式,這個是kubectl實現各類資源物件的解析和校驗的核心;
初步瞭解RESTClient
和Object
這兩個物件,它們是貫穿kubernetes的核心概念;
呼叫邏輯
以上就是Kubernetes Visitor設計模式及傳送pod建立請求解析的詳細內容,更多關於Kubernetes Visitor傳送pod請求的資料請關注it145.com其它相關文章!
相關文章
<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
综合看Anker超能充系列的性价比很高,并且与不仅和iPhone12/苹果<em>Mac</em>Book很配,而且适合多设备充电需求的日常使用或差旅场景,不管是安卓还是Switch同样也能用得上它,希望这次分享能给准备购入充电器的小伙伴们有所
2021-06-01 09:31:42
除了L4WUDU与吴亦凡已经多次共事,成为了明面上的厂牌成员,吴亦凡还曾带领20XXCLUB全队参加2020年的一场音乐节,这也是20XXCLUB首次全员合照,王嗣尧Turbo、陈彦希Regi、<em>Mac</em> Ova Seas、林渝植等人全部出场。然而让
2021-06-01 09:31:34
目前应用IPFS的机构:1 谷歌<em>浏览器</em>支持IPFS分布式协议 2 万维网 (历史档案博物馆)数据库 3 火狐<em>浏览器</em>支持 IPFS分布式协议 4 EOS 等数字货币数据存储 5 美国国会图书馆,历史资料永久保存在 IPFS 6 加
2021-06-01 09:31:24
开拓者的车机是兼容苹果和<em>安卓</em>,虽然我不怎么用,但确实兼顾了我家人的很多需求:副驾的门板还配有解锁开关,有的时候老婆开车,下车的时候偶尔会忘记解锁,我在副驾驶可以自己开门:第二排设计很好,不仅配置了一个很大的
2021-06-01 09:30:48
不仅是<em>安卓</em>手机,苹果手机的降价力度也是前所未有了,iPhone12也“跳水价”了,发布价是6799元,如今已经跌至5308元,降价幅度超过1400元,最新定价确认了。iPhone12是苹果首款5G手机,同时也是全球首款5nm芯片的智能机,它
2021-06-01 09:30:45