• Go反射实战:我用 Golang 反射写了一个配置库
  • 发布于 2个月前
  • 261 热度
    0 评论
用习惯了 struct 之后, 我想所有东西都通过 struct 管理。 学习了反射之后, 我总要找点事情做来练习。于是我整合了 Golang环境变量操作 和 Golang反射, 以及解决了我认为的其他一些配置管理的痛点, 便有了这个项目。

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

以及一些其他的小地方,于是, 为了解决以上几个痛点, 我自己造了一个轮子。 这个轮子支持将 配置结构体 转成一个 有规则的key 的 map, 以保存到文件中,通过读取 配置文件 或者 环境变量 重新将值 映射 到 配置结构体 中。支持 应用名称前缀 , 方便在多应用环境区分。支持 默认值 , 减少多配置文件是的工作量。


实现效果
1. 序列化配置
定义 Mysql 和 Redis 的连接信息, 并通过 SetDefaults() 方法设置默认值。 以下这些配置结构体, 可以是自己本地定义, 也可以是 依赖库 中准备好的。 (一般是依赖库提供)
/* 堆代码 www.duidaima.com */
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"
    }
}
随后再将需要的依赖库中的配置通过 config 结构体组合起来, 并通过 CallSetDefaults 初始化默认值。 最后序列化成字符串。
func Test_ConfP_Server(t * testing.T) {
    // 堆代码 www.duidaima.com
    // 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. 反序列化配置
复制 default.yml 并另存为 config.yml, 修改实际字段的值。
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
使用 UnmarshalFile 从配置文件中读取配置, 并 映射 到结构体中。
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 配置一致。
my_auth => DbUser:DbPass
redis_dsn => redis://:YouPass@redis.example.com:6379/0
补充说明:
这是一个 不完善 的库: 目前只支持 string, int, uint, bool 几种基础数据类型。
这是一个 基础 库: 这个库只提供了 序列化和反序列化 的能力。 如果要实现 多配置管理 或者 多来源管理 需要在此库上进行二次封装。
用户评论