再回首数据结构—数组(Golang实现)

  数组为线性数据结构,通常编程语言都有自带了数组数据类型结构,数组存放的是有个相同数据类型的数据集;
  为什么称数组为线性数据结构:因为数组在内存中是连续存储的数据结构,数组中每个元素最多只有左右两个方向有相邻的元素;数组中的每个元素都有一个索引(或称下标)标识,这个索引在编程语言中通常都是从0开始,通过索引可访问到数组中对应的元素;数组的数据结构如下所示:

enter image description here

数组的优势

  从上文中我们知道数组在内存中是连续存储的也就是说数组中元素在内存块为连续,此时我们利用此特性通过索引快速访问数据元素;因此也称数组支持随机访问
  下面我们通过一个示例来说明数组的随机访问特性;

  在Go中定义一个长度为7的数组:
  var array [7] int
  此时我们可以通过索引随机访问数组中元素如:array[5]即可访问到数组中的第六个元素,这背后又是怎样的呢,上面我们说过数组在内存中的存储结构是连续的上面我们定义的数组结构如下所示:

enter image description here

  此处假设该数组内存空间首地址为:000,由于该数据类型为int因此每个数据元素占4个字节,所以上面定义7个长度数组占用的内存地址为:000~024连续的地址空间;所以通过下面的计算公式即可实现数组的随机访问,比如访问数组中第五个元素内存地址的计算公式如下:

  所访问元素内存地址=数组首地址 + index * 数据类型大小
  array[5]内存地址 = 000 + 5 * 4
  所以数组的优势为:1、简单;2、支持随机访问,通过索引随机访问时间复杂度为O(1)

数组插入与删除

  数组的插入与删除其实并不高效,由于数组存储是连续的内存空间,所以我们在对数组进行操作时都需要去维护这个连续性,因此也就牺牲了一些效率,当插入或删除数组中元素时数组中元素都需要大量移动如下图所示:

  enter image description here

  在索引为3的位置插入g,需把索引3~n元素后移一位

  enter image description here

  删除数组索引为1的元素,需把索引2~n元素前移一位

  因此数组的插入、删除平均时间复杂度为:O(n),这里说的是平均复杂度是因为还有例外情况,比如在数组末尾插入元素、删除数组末尾元素这些情况时间复杂度为O(1);
  每种编程语言中都存在数组,数组其实还分为静态数组动态数组;静态数组是指数组创建后存储空间固定不变的、而动态数组为在使用数组过程中当存储空间不足时可动态扩容;下面为使用Golang实现的动态数组封装,动态数组的扩容、缩容需要注意扩容的大小与缩容的零界点,此处可能会影响到数组性能;

  type Array struct {
    data []interface{}
    size int
  }

  func NewArray(capacity int) *Array {
    array := new(Array)
    array.data = make([]interface{}, capacity)
    array.size = 0
    return array
  }

  func NewDefaultArray() *Array {
    return NewArray(10)
  }

  /**
  获取元素个数
   */
  func (a *Array) Size() int {
    return a.size
  }

  /**
  获取容量大小
   */
  func (a *Array) Capacity() int {
    return len(a.data)
  }

  /**
  是否为空
   */
  func (a *Array) IsEmpty() bool {
    return a.size == 0
  }

  /**
  往数组末尾添加元素
   */
  func (a *Array) AddLast(e interface{}) error {
    return a.Add(a.size, e)
  }

  /**
  清空数组
   */
  func (a *Array) Clear() {
    a.data = make([]interface{}, a.size)
    a.size = 0
  }

  /**
  往第一个位置添加元素
   */
  func (a *Array) AddFirst(e interface{}) error {
    return a.Add(0, e)
  }

  /**
  往指定索引添加元素
   */
  func (a *Array) Add(index int, e interface{}) error {
if index < 0 || index > a.size {
    return errors.New("Add failed, Require index >= 0 and index< size")
}

if a.size == len(a.data) {
    a.resize(2 * len(a.data))
}

for i := a.size - 1; i >= index; i-- {
    a.data[i+1] = a.data[i]
}
a.data[index] = e
a.size++
return nil
  }

  /**
  更新指定位置元素
   */
  func (a *Array) Update(index int, e interface{}) error {
    if index < 0 || index > a.size {
        return errors.New("update failed, Require index >= 0 and index< size")
    }
    a.data[index] = e
    return nil
  }

  /**
  获取指定位置元素
   */
  func (a *Array) FindElement(index int) interface{} {
    if index < 0 || index > a.size {
        return errors.New("update failed, Require index >= 0 and index< size")
    }
    return a.data[index]
  }

  /**
  删除数组指定索引位置的元素,返回删除的元素
   */
  func (a *Array) Remove(index int) (e interface{}) {
    if index < 0 || index > a.size {
        return errors.New("remove failed, Require index >= 0 and index< size")
    }
    e = a.data[index]
    for i := index + 1; i < a.size; i++ {
        a.data[i-1] = a.data[i]
    }
    a.size--
    //删除元素后数组缩小一位,将该位置元素置nil
    a.data[a.size] = nil
    return
  }

  /**
  删除数组首个元素
   */
  func (a *Array) RemoveFirst() (e interface{}) {
    return a.Remove(0)
  }

  /**
  数组扩容
   */
  func (a *Array) resize(newCapacity int) {
    newData := make([]interface{}, newCapacity)
    for i := 0; i < a.size; i++ {
        newData[i] = a.data[i]
    }
    a.data = newData
  }

参考资料: https://zh.wikipedia.org/wiki/%E6%95%B0%E7%BB%84

Helm 入门指南

  Helm 为Kubernetes的软件包管理工具,Helm有两部分组成:Helm客户端、Tiller服务端,Helm三个主要部件:Chart、仓库、Release;

Chart:为Kubernetes中应用程序所需要的资源的定义。
仓库:为存储Helm chart的仓库,可从仓库中下载chart直接使用
Release: Kubernetes中运行的chart实例,每个chart可多次安装,每次安装都是一个新版本;

Helm 常见指令:

  helm search 在仓库中查找chart
enter image description here
enter image description here

  helm inspect stable/mongodb 可查看该chart的介绍信息;
  helm install stable/mongodb 可直接下载该chart并安装该chart;

版本升级:
  helm upgrade releaseName .
enter image description here

  helm rollback releaseName 1 回滚到版本1

  从零开始通过helm发布Kubernetes项目:

一、结构介绍

初始化项目:

helm create nginx  

  执行完后创建名为nginx的chart,现在查看nginx目录中的文件:

enter image description here

charts:目录用于存放所依赖的子chart
chart.yaml:为当前chart的说明文件,也可在模板中访问该文件;
templates:目录为nginx项目的模板目录,通常会使用values.yaml配置内容进行填充,板引擎渲染此目录的文件后Tiller将渲染得到的结果 提交给Kubernetes创建响应的对象;
values.yaml:为值文件,定义模板中需要使用的值,在templates的模板文件中将访问改值;
enter image description here

  在templates目录中有可以看到deployment.yaml、ingress.yaml、service.yaml文件这些为定义Kubernetes deployment、ingress、service对象的模板文件;
NOTES.txt:为安装chart成功后的说明文件
_helpers.tpl:模板助手文件,定义的值可在模板中使用;

二、通过helm在Kubernetes中部署Nginx

1、helm 初步使用

  现在删除template目录中的所有文件、清空values.yaml文件中所有内容

enter image description here

  部署nginx需要一个deployment控制器与对外提供服务的service,现在我们分别手动创建这个两个文件并编写相关内容;

deployment.yaml :

 apiVersion: apps/v1
 kind: Deployment
 metadata:
   name: nginx-deployment
   labels:
     app: nginx
 spec:
   replicas: 3
   selector:
     matchLabels:
       app: nginx
   template:
     metadata:
       labels:
         app: nginx
     spec:
       containers:
       - name: nginx
         image: nginx:1.7.9
         ports:
         - containerPort: 80      

service.yaml

 apiVersion: v1
 kind: Service
 metadata:
  name: nginx-service
 spec:
  selector:
    app: nginx
  type: NodePort
  ports:
  - port: 80
    nodePort: 30078

  此时我们在上级目录执行:helm install nginx 已经可以将nginx部署到Kubernetes中;

enter image description here

  但如果只是这样使用Helm,那它存在的意义也不大,既然说template目录是模板目录当然它里面的模板文件也不能只是这么使用,要让模板文件体现出它的模型性质;下面我们将使用Helm的模板等功能,让模板文件体现出他的模板性质;

2、helm 模板
  目前为止deployment.yaml与service.yaml虽然是放在模板目录下但是完全没有体现出他的模板性质,现在我们开始这两个文件的模板化;

模板语法基础:

  内置对象:helm中有这么这个常用内置对象:Release、Values、Chart、Files、Capabilities、Template;

Release:此对象描述当前release,内置有:Release.Name release名称、Release.Time 发布时间等等等对面;
Values:此对象为从values.yaml文件与用户提供的文件中传递到模板的对值对象;
Chart:此对象为chart.yaml文件所提供的数据对象;
Files:提供了对chart非特殊文件的访问;
Capabilities:这提供了Kubernetes集群信息的访问;
Template:正在执行的当前模板信息

  下面我们通过helm内置对象Values来实现deployment.yaml、service.yaml文件的模板化,Values对象的取值范围:
1、当前chart的values.yaml文件;
2、如当前为子chart,则来自父chart的values.yaml文件;
3、在helm install或helm update执行是通过-f参数传递的文件;
4、通过helm install 时通过 –set设置的值;

  如values.yaml文件中存在replicaCount: 1对象,则在模板文件中通过{{ .Values.replicaCount }}即可访问到values.yaml文件定义的对象值1;

  通过_helpers.tpl可定义命名模板,下面定义模板deploy.name

 {{- define "deploy.name" -}}
 demo-deploy-nginx
 {{- end }}

  其中:{{ – 表示删除左侧的空格 – }} 删除右侧的空格
  此时可在模板文件中通过{{ template “deploy.name” . }} 引用该命名模板;

实现模板化:

  提取需要模板化的数据,这里提取出deployment对象的name、labels、replicas、matchLabels、template的labels、template spec的name、image;service对象的name、selector labels、type、port、nodePort等配置;
  我们再从提取出来的配置中挑选出name类与labels类配置定义为命名模板存放在_helpers.tpl文件中,其他的配置项定义在Values.yaml文件中;

_helpers.tpl文件内容如下:存放了deploy名称、service名称、deploy的lables、deploy中pod的lables;

{{- define "deploy.name" -}}
 demo-deploy-nginx
{{- end }}

{{- define "service.name" -}}
demo-service-nginx
{{- end }}

{{- define "labels" -}}
demo: nginx
{{- end}}

{{- define "deploy.labels" -}}
deploy: nginx
{{- end}}

values.yaml 文件,此文件定义了副本数、镜像名称与版本、service类型、端口等:

# 定义模板默认值
replicaCount: 1
image:
  repository: nginx
  tag: 1.7.9

service:
  type: NodePort
  port: 80
  nodePort: 30078

  值文件定义好了此时需要在模板deployment、service文件中引用上述两个文件所定义的值,下面开始重构deployment.yaml与service.yaml文件:

service.yaml文件:

apiVersion: v1
kind: Service
metadata: 
 name: {{ template "service.name" . }}
spec: 
 selector:
  {{ template "labels" . }}
 type: {{ .Values.service.type }}
 ports: 
 - port: {{ .Values.service.port }}
   nodePort: {{ .Values.service.nodePort }}

deployment.yaml

apiVersion: apps/v1
kind: Deployment
metadata: 
  name: {{ template "deploy.name" . }}
  labels: 
    {{ template "deploy.labels" .}}
spec:
  replicas: {{ .Values.replicaCount }}
  selector:
   matchLabels:
      {{ template "labels" . }}
 template: 
    metadata:
      labels:
        {{ template "labels" . }}
    spec:
      containers:
      - name: {{ .Chart.Name}}
        image: {{ .Values.image.repository }}:{{ .Values.image.tag }}
        ports:
        - containerPort: 80

  模板文件编写好后可以再重新使用Helm install部署nginx试试,执行前先执行helm delete running-chipmunk卸载掉之前安装的;

enter image description here

参考资料: https://docs.helm.sh/