golang 的不可变(Immutable)编程

简介: 不可变编程是一种编程思想。简单地说,就是对象的属性只能set一次。

不可变编程是一种编程思想。简单地说,就是对象的属性只能set一次。

ImmutableEphemeralVolume(immutable secret)

kubernetes 最近(2019年年底)的一个 ImmutableEphemeralVolume 为例。

我看了一下源代码,大意就是说,configmapsecret 在创建后不可更新。

以 secret 为例,目前(2020-04-16)secret 的定义是这样的:


// Secret holds secret data of a certain type. The total bytes of the values in
// the Data field must be less than MaxSecretSize bytes.
type Secret struct {
    metav1.TypeMeta `json:",inline"`
    // Standard object's metadata.
    // More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata
    // +optional
    metav1.ObjectMeta `json:"metadata,omitempty" protobuf:"bytes,1,opt,name=metadata"`

    // Immutable, if set to true, ensures that data stored in the Secret cannot
    // be updated (only object metadata can be modified).
    // If not set to true, the field can be modified at any time.
    // Defaulted to nil.
    // This is an alpha field enabled by ImmutableEphemeralVolumes feature gate.
    // +optional
    Immutable *bool `json:"immutable,omitempty" protobuf:"varint,5,opt,name=immutable"`

    // Data contains the secret data. Each key must consist of alphanumeric
    // characters, '-', '_' or '.'. The serialized form of the secret data is a
    // base64 encoded string, representing the arbitrary (possibly non-string)
    // data value here. Described in https://tools.ietf.org/html/rfc4648#section-4
    // +optional
    Data map[string][]byte `json:"data,omitempty" protobuf:"bytes,2,rep,name=data"`

    // stringData allows specifying non-binary secret data in string form.
    // It is provided as a write-only convenience method.
    // All keys and values are merged into the data field on write, overwriting any existing values.
    // It is never output when reading from the API.
    // +k8s:conversion-gen=false
    // +optional
    StringData map[string]string `json:"stringData,omitempty" protobuf:"bytes,4,rep,name=stringData"`

    // Used to facilitate programmatic handling of secret data.
    // +optional
    Type SecretType `json:"type,omitempty" protobuf:"bytes,3,opt,name=type,casttype=SecretType"`
}

其实只看

Immutable *bool `json:"immutable,omitempty"`

就可以了。可以看到,这是一个 bool 的指针。因为这个字段目前处于alpha 的阶段,所以用了 omitempty 这个标签忽略掉了。

判断是否已经注入

Secret 有个 String 方法有点意思。

简单地说就是通过反射判断字段是否已经注入。


func (this *Secret) String() string {
    ......
    s := strings.Join([]string{`&Secret{`,
        `ObjectMeta:` + strings.Replace(strings.Replace(fmt.Sprintf("%v", this.ObjectMeta), "ObjectMeta", "v1.ObjectMeta", 1), `&`, ``, 1) + `,`,
        `Data:` + mapStringForData + `,`,
        `Type:` + fmt.Sprintf("%v", this.Type) + `,`,
        `StringData:` + mapStringForStringData + `,`,
        `Immutable:` + valueToStringGenerated(this.Immutable) + `,`,
        `}`,
    }, "")
    return s
}

valueToStringGenerated 方法展开是这样的:

func valueToStringGenerated(v interface{}) string {
    rv := reflect.ValueOf(v)
    if rv.IsNil() {
        return "nil"
    }
    pv := reflect.Indirect(rv).Interface()
    return fmt.Sprintf("*%v", pv)
}

我简化了一下模型,写了个例子。

例子

package main

import (
    "fmt"
    "reflect"
)

type Secret struct {
    Immutable *bool `json:"immutable,omitempty"`
}

func main() {
    s := Secret{Immutable: &[]bool{true}[0]}
    fmt.Println(valueToStringGenerated(s.Immutable)) // *true
    s = Secret{}
    fmt.Println(valueToStringGenerated(s.Immutable)) // nil
}

func valueToStringGenerated(v interface{}) string {
    rv := reflect.ValueOf(v)
    if rv.IsNil() {
        return "nil"
    }
    pv := reflect.Indirect(rv).Interface()
    return fmt.Sprintf("*%v", pv)
}

结论

struct 增加一个字段,这个字段是一个指针。

通过反射获取 struct 的成员(比如字段),进而判断是否已经注入。

有些情况(比如string),用私有字段,struct 暴露一个单例模式的 Set 方法也行。我猜是 bool 类型比较特殊,所以 kubernetes 官方才用了 *bool 这个数据结构。

参考链接

  1. Kubernetes: What is Immutable Infrastructure?
  2. Image volumes and container volume
  3. How to set bool pointer to true in struct literal?
相关实践学习
深入解析Docker容器化技术
Docker是一个开源的应用容器引擎,让开发者可以打包他们的应用以及依赖包到一个可移植的容器中,然后发布到任何流行的Linux机器上,也可以实现虚拟化,容器是完全使用沙箱机制,相互之间不会有任何接口。Docker是世界领先的软件容器平台。开发人员利用Docker可以消除协作编码时“在我的机器上可正常工作”的问题。运维人员利用Docker可以在隔离容器中并行运行和管理应用,获得更好的计算密度。企业利用Docker可以构建敏捷的软件交付管道,以更快的速度、更高的安全性和可靠的信誉为Linux和Windows Server应用发布新功能。 在本套课程中,我们将全面的讲解Docker技术栈,从环境安装到容器、镜像操作以及生产环境如何部署开发的微服务应用。本课程由黑马程序员提供。     相关的阿里云产品:容器服务 ACK 容器服务 Kubernetes 版(简称 ACK)提供高性能可伸缩的容器应用管理能力,支持企业级容器化应用的全生命周期管理。整合阿里云虚拟化、存储、网络和安全能力,打造云端最佳容器化应用运行环境。 了解产品详情: https://www.aliyun.com/product/kubernetes
目录
相关文章
|
6月前
|
设计模式 缓存 算法
Go如何进行高质量编程与性能调优实践
本文介绍了Go语言高质量编程与性能调优的实践方法。高质量编程包括良好的编码习惯(如清晰注释、命名规范)、代码风格与设计(如MVC模式)、简洁明了的代码原则,以及单元测试与代码重构的重要性。性能调优方面,涵盖算法优化、数据结构选择、I/O优化、内存管理、并行与并发处理优化及代码层面的改进。通过这些方法,可有效提升代码质量和系统性能。
146 13
|
6月前
|
分布式计算 Go C++
初探Go语言RPC编程手法
总的来说,Go语言的RPC编程是一种强大的工具,让分布式计算变得简单如同本地计算。如果你还没有试过,不妨挑战一下这个新的编程领域,你可能会发现新的世界。
173 10
|
数据采集 监控 Java
go语言编程学习
【11月更文挑战第3天】
215 7
|
数据库连接 Go 数据库
Go语言中的错误注入与防御编程。错误注入通过模拟网络故障、数据库错误等,测试系统稳定性
本文探讨了Go语言中的错误注入与防御编程。错误注入通过模拟网络故障、数据库错误等,测试系统稳定性;防御编程则强调在编码时考虑各种错误情况,确保程序健壮性。文章详细介绍了这两种技术在Go语言中的实现方法及其重要性,旨在提升软件质量和可靠性。
191 1
|
Unix Linux Go
go进阶编程:Golang中的文件与文件夹操作指南
本文详细介绍了Golang中文件与文件夹的基本操作,包括读取、写入、创建、删除和遍历等。通过示例代码展示了如何使用`os`和`io/ioutil`包进行文件操作,并强调了错误处理、权限控制和路径问题的重要性。适合初学者和有经验的开发者参考。
215 4
|
Java 大数据 Go
Go语言:高效并发的编程新星
【10月更文挑战第21】Go语言:高效并发的编程新星
432 7
|
Go 数据处理 调度
Go语言中的并发模型:解锁高效并行编程的秘诀
本文将探讨Go语言中独特的并发模型及其在现代软件开发中的应用。通过深入分析 Goroutines 和 Channels,我们将揭示这一模型如何简化并行编程,提升应用性能,并改变开发者处理并发任务的方式。不同于传统多线程编程,Go的并发方法以其简洁性和高效性脱颖而出,为开发者提供了一种全新的编程范式。
|
存储 缓存 Go
go语言编程系列(五)
go语言编程系列(五)
|
搜索推荐 Java 编译器
go语言编程系列(四)
go语言编程系列(四)
|
存储 JSON 安全
go语言编程系列(七)
go语言编程系列(七)

推荐镜像

更多