Visitor设计模式及发送pod创建请求实现详解

目录
  • 确立目标
  • visitor design pattern
  • Visitor
  • Chained
    • VisitorList
    • EagerVisitorList
    • DecoratedVisitor
    • ContinueOnErrorVisitor
    • FlattenListVisitor
    • FilteredVisitor
  • Implements
    • StreamVisitor
    • FileVisitor
    • URLVisitor
    • KustomizeVisitor
  • 发送创建Pod请求的实现细节
    • send request
    • RESTful Client
  • Object
  • Post
  • Summary

确立目标

  • 理解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 ClientObject的大致产生逻辑后,我们再回过头来看发送的方法

// 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实现各类资源对象的解析和校验的核心;

初步了解RESTClientObject这两个对象,它们是贯穿kubernetes的核心概念;

调用逻辑

  • cobra匹配子命令
  • 用Visitor模式构建Builder
  • 用RESTClient将Object发送到kube-apiserver

以上就是Visitor设计模式及发送pod创建请求实现详解的详细内容,更多关于Visitor 发送pod创建请求的资料请关注我们其它相关文章!

(0)

相关推荐

  • kubernetes k8s入门定义一个Pod

    目录 什么是Pod? 为什么要引入Pod? 定义一个Pod:pod.yaml Pod探针: Pod探针检测方式: Pod退出流程 什么是Pod? pod是kubernetes中最小的单元,由一组.一个或多个容器组成,每个pod中包含了一个pause容器. pause容器是pod的父容器,主要负责僵尸进程的回收管理,通过pause容器可以使同一个pod中多个容器共享存储.网络.PID.IPC等,同一个Pod中多容器间访问仅通过localhost就可以通信. 为什么要引入Pod? 将有强依赖性的容器

  • 如何在kubernetes中创建Pod

    目录 如何创建Pod? kubectl工具 如何创建Pod? 在之前的文章中,我们介绍了容器和Pod的区别和关系.我们知道Pod是k8s调度的最小单位,而一个Pod中可以有多个容器,那么我们如何来定义一个我们自己的Pod呢? 在k8s中,我们通常使用编写配置文件的方式创建一个Pod,配置文件的格式通常采用yaml格式,(yaml格式如何表示list.key-value键值对,这些知识在前一篇文章中说过了),编写好yaml文件之后,通过下面的办法来启动一个Pod: kubectl create -

  • 云原生技术kubernetes调度单位pod的使用详解

    k8s中的最小调度单位---pod 之前的文章中,我们对k8s能够解决的问题做了简单介绍,简单来说,它解决的问题是容器的编排与调度,它的核心价值在于:运行在大规模集群的任务之间,实际上存在着各种各样的关系,这些关系的处理,才是任务编排和系统管理最困难的地方,k8s就是为了这个问题而生的. 这句话比较难理解,我们从已有的知识入手,抽丝剥茧,慢慢理解它.我们已经知道,容器的本质是一个进程,它包含三个部分: 如果说容器是云环境的一个进程,那么你可以将k8s理解成云环境中的一个操作系统. 在一个操作系统

  • kubernetes中的namespace、node、pod介绍

    namepace.node.pod? 当我们讨论 k8s 时总是会讨论集群,k8s 中的每个集群由多个机器/虚拟机组成,集群也被称为 命名空间(namespace),命名空间是虚拟的,因此也叫虚拟集群. Namespace 是对一组资源和对象的抽象集合. node 是集群中的单个机器/虚拟机,node 有两种,一种是 master ,一种是 worker.master 用来运行 kubernetes 服务,例如 API Server:worker 是真正工作的节点,用来运行你的容器. maste

  • 详解kubernetes pod的编排和生命周期

    目录 K8S Master基本架构 Pod的编排思想 Pod对象的属性和容器的属性? Pod的生命周期 K8S Master基本架构 K8S的集群运行依赖Master节点和Node节点的通信,为了更好的理解第4部分的Pod生命周期,我们这里先给出K8S Master的简单架构图,后续的文章中,我们会分析Master.Node和Pod之间的关系. Master的架构图: 其中: API Server提供了HTTP Rest接口,它是k8s中的所有资源增删改查的唯一入口,也是集群控制的入口: Sch

  • Visitor设计模式及发送pod创建请求实现详解

    目录 确立目标 visitor design pattern Visitor Chained VisitorList EagerVisitorList DecoratedVisitor ContinueOnErrorVisitor FlattenListVisitor FilteredVisitor Implements StreamVisitor FileVisitor URLVisitor KustomizeVisitor 发送创建Pod请求的实现细节 send request RESTfu

  • 静态pod 创建使用示例详解

    目录 一.系统环境 二.前言 三.静态pod 3.1 何为静态pod 3.2 创建静态pod 3.2.1 使用--pod-manifest-path指定静态pod目录 3.2.2 静态pod默认目录/etc/kubernetes/manifests 一.系统环境 服务器版本 docker软件版本 Kubernetes(k8s)集群版本 CPU架构 CentOS Linux release 7.4.1708 (Core) Docker version 20.10.12 v1.21.9 x86_64

  • Django处理Ajax发送的Get请求代码详解

    Django处理Ajax发送的Get请求实例,Ajax优点在一是异步请求,无需等待响应就可以再次发起请求,而是局部刷新,避免整个页面刷新的网页闪动. 打开命令行窗口,输入命令,创建django工程: django-admin startproject djajaxget cd djajaxget python manage.py startapp demo 使用PyCharm打开上一步创建的工程,在工程同名文件夹的settings.py文件,修改部分代码,注册应用,配置模板路径.静态文件路径,修

  • java爬虫之使用HttpClient模拟浏览器发送请求方法详解

    0. 摘要 0.1 添加依赖 <dependency> <groupId>org.apache.httpcomponents</groupId> <artifactId>httpclient</artifactId> <version>4.5.2</version> </dependency> 0.2 代码 //1. 打开浏览器 创建httpclient对象 CloseableHttpClient httpCl

  • 对Python发送带header的http请求方法详解

    简单的header import urllib2 request = urllib2.Request('http://example.com/') request.add_header('User-Agent', 'fake-client') response = urllib2.urlopen(request) print request.read() 包含较多元素的header import urllib,urllib2 url = 'http://example.com/' headers

  • python爬虫使用requests发送post请求示例详解

    简介 HTTP协议规定post提交的数据必须放在消息主体中,但是协议并没有规定必须使用什么编码方式.服务端通过是根据请求头中的Content-Type字段来获知请求中的消息主体是用何种方式进行编码,再对消息主体进行解析.具体的编码方式包括: application/x-www-form-urlencoded 最常见post提交数据的方式,以form表单形式提交数据. application/json 以json串提交数据. multipart/form-data 一般使用来上传文件. 一. 以f

  • python 发送get请求接口详解

    简介 如果想用python做接口测试,我们首先有不得不了解和学习的模块.它就是第三方模块:Requests. 虽然Python内置的urllib模块,用于访问网络资源.但是,它用起来比较麻烦,而且,缺少很多实用的高级功能.更好的方案是使用 requests.它是一个Python第三方库,处理URL资源特别方便.查看其中文官网:http://cn.python-requests.org/zh_CN/latest/index.html英文官网:http://www.python-requests.o

  • PHP设计模式之状态模式定义与用法详解

    本文实例讲述了PHP设计模式之状态模式定义与用法.分享给大家供大家参考,具体如下: 什么是状态设计模式 当一个对象的内在状态改变时允许改变其行为,这个对象看起来像是改变了其类. 状态模式主要解决的是当控制一个对象状态的条件表达式过于复杂时的情况.把状态的判断逻辑转移到表示不同状态的一系列类中,可以把复杂的判断逻辑简化. 什么时候使用状态模式 对象中频繁改变非常依赖于条件语句. 就其自身来说, 条件语句本身没有什么问题(如switch语句或带else子句的语句),不过, 如果选项太多, 以到程序开

  • PHP设计模式之工厂模式定义与用法详解

    本文实例讲述了PHP设计模式之工厂模式定义与用法.分享给大家供大家参考,具体如下: 工厂模式(Factory Design Pattern)作为一种创建型设计模式, 遵循了开放-封闭原则, 对修改封闭, 对扩展开放. 工厂方法(Factory Method)模式就是要创建"某种东西". 对于工厂方法模式, 要创建的"东西"是一个产品,这个产品与创建它的类之间不存在绑定.实际上,为了保持这种松耦合,客户会通过一个工厂发出请求. 再由工厂创建所请求的产品.也可以换种方式

  • Java实现邮件发送的过程及代码详解

    Java实现邮件发送 一.邮件服务器与传输协议 要在网络上实现邮件功能,必须要有专门的邮件服务器.这些邮件服务器类似于现实生活中的邮局,它主要负责接收用户投递过来的邮件,并把邮件投递到邮件接收者的电子邮箱中. SMTP服务器地址:一般是 smtp.xxx.com,比如163邮箱是smtp.163.com,qq邮箱是smtp.qq.com. SMTP协议 通常把处理用户smtp请求(邮件发送请求)的服务器称之为SMTP服务器(邮件发送服务器). POP3协议 通常把处理用户pop3请求(邮件接收请

随机推荐