首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >「Golang 反射实战」 - 我用反射写了一个配置库 - envutils , 应用变更再也不会少变量了

「Golang 反射实战」 - 我用反射写了一个配置库 - envutils , 应用变更再也不会少变量了

作者头像
老麦
发布2023-02-25 20:11:07
发布2023-02-25 20:11:07
5430
举报
文章被收录于专栏:Go与云原生Go与云原生

golang-envutils

大家好, 我是老麦, 我将每天 早上9点 为你分享一篇好文章。

「Golang 反射实战」 - 我用反射写了一个配置库 - envutils , 应用变更再也不会少变量了

原文链接: https://tangx.in/posts/2023/01/27/golang-envutils-config/

用习惯了 struct 之后, 我想所有东西都通过 struct 管理。学习了反射之后, 我总要找点事情做来练习。

于是我整合了 Golang环境变量操作Golang反射, 以及解决了我认为的其他一些配置管理的痛点, 便有了这个项目。

  1. 一篇文章告诉你 golang 环境变量的所有基础操作
  2. 失败99次之后, 我总记了

Github 仓库: https://github.com/tangx/envutils

配置管理的痛点

  1. 配置来源多样性:这个痛点主要来源于容器, 以前容器配置 CofnigMap 或者 Secret 挂载文件还是很麻烦的, 远不如直接使用 环境变量 方便。
  2. 数据映射:使用环境变量又带来了新的问题, 通常在使用的时候, 我习惯把所有变量写在一个 结构体struct 中, 但是如何把 环境变量名称配置结构体 关联起来?
  3. 变量的增减管理:随着项目的不断演进变量可能 增加或者删除 , 要如何在一个 醒目/固定 的位置留档?或者如何每次程序都能导出当前版本的所有配置需求?
  4. 实现多配置叠加管理:这个痛点来自于 CICD 发布的测试环境, 如何使不同的 feature 分支能使用自己的独立的配置, 合并的时候又不影响其他人。

以及一些其他的小地方

于是, 为了解决以上几个痛点, 我自己造了一个轮子。这个轮子支持

  1. 配置结构体 转成一个 有规则的keymap, 以保存到文件中
  2. 通过读取 配置文件 或者 环境变量 重新将值 映射配置结构体 中。
  3. 支持 应用名称前缀 , 方便在同一环境中区分多个应用分。
  4. 支持 默认值 , 减少配置管理的工作量。

实现效果

1. 序列化配置

  1. 定义 Mysql 和 Redis 的连接信息, 并通过 SetDefaults() 方法设置默认值。以下这些配置结构体, 可以是自己本地定义, 也可以是 依赖库 中准备好的。(一般是依赖库提供)
代码语言:javascript
复制
type MysqlServer struct {
 ListenAddr string `env:"listenAddr"`
 Auth       string `env:"auth"`
 DBName     string `env:"dbName"`
}

func (my *MysqlServer) SetDefaults() {
 if my.ListenAddr == "" {
  my.ListenAddr = "localhost:3306"
 }
}

type RedisServer struct {
 DSN string `env:"dsn"`
}

func (r *RedisServer) SetDefaults() {
 if r.DSN == "" {
  r.DSN = "redis://:Password@localhost:6379/0"
 }
}
  1. 随后再将需要的依赖库中的配置通过 config 结构体组合起来, 并通过 CallSetDefaults 初始化默认值。最后序列化成字符串。
代码语言:javascript
复制
func Test_ConfP_Server(t *testing.T) {

// 1. 创建配置结构体
 config := &struct {
  MysqlServer *MysqlServer
  RedisServer *RedisServer
 }{
  MysqlServer: &MysqlServer{},
  RedisServer: &RedisServer{},
 }

// 2. 调用设置默认值
 err := CallSetDefaults(config)
 if err != nil {
  panic(err)
 }

// 3. 序列化配置
 data, err := Marshal(config, "AppName")
 if err != nil {
  panic(err)
 }
 fmt.Printf("%s\n", data)
}

2. 反序列化配置

  1. 复制 default.yml 并另存为 config.yml, 修改实际字段的值。
代码语言:javascript
复制
AppName__MysqlServer_auth: "DbUser:DbPass"
AppName__MysqlServer_dbName: "default_db"
AppName__MysqlServer_listenAddr: 100.100.100.100:3306
AppName__RedisServer_dsn: redis://:YouPass@redis.example.com:6379/0
  1. 使用 UnmarshalFile 从配置文件中读取配置, 并 映射 到结构体中。
代码语言:javascript
复制
func Test_ConfP_Server(t *testing.T) {

 config := &struct {
  MysqlServer *MysqlServer
  RedisServer *RedisServer
 }{
  MysqlServer: &MysqlServer{},
  RedisServer: &RedisServer{},
 }

 err := UnmarshalFile(config, "AppName", "config.yml")
 if err != nil {
  panic(err)
 }

 fmt.Println("my_auth =>", config.MysqlServer.Auth)
 fmt.Println("redis_dsn =>", config.RedisServer.DSN)
}

输出结果与 config 配置一致。

代码语言:javascript
复制
my_auth => DbUser:DbPass
redis_dsn => redis://:YouPass@redis.example.com:6379/0

补充说明

  1. 这是一个 不完善 的库:目前只支持 string, int, uint, bool 几种基础数据类型。
  2. 这是一个 基础 库:这个库只提供了 序列化和反序列化 的能力。如果要实现 多配置管理 或者 多来源管理 需要在此库上进行二次封装。
本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2023-02-05,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 熊猫云原生Go 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 大家好, 我是老麦, 我将每天 早上9点 为你分享一篇好文章。
  • 「Golang 反射实战」 - 我用反射写了一个配置库 - envutils , 应用变更再也不会少变量了
    • 配置管理的痛点
    • 实现效果
      • 1. 序列化配置
      • 2. 反序列化配置
    • 补充说明
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档