当前位置: 首页 > news >正文

使用Golang反射技术实现一套有默认值的配置解析库

在实际开发中,我们往往会给一个逻辑设计一套配置文件,用于根据不同环境加载不同配置。
比如生产环境和测试环境数据库的地址不一样,我们就需要在配置文件中设置不同的值。但是配置文件中又有一些相同值的配置项,比如数据库的名称等。难道相同的配置要像下面这样写多次吗?

version: 1
pro:write_pool:url: pro-write.comport: 11port: 5432user_name: write_pool_user_namepassword: write_pool_passworddbname: dbnamemax_idle_Conns: 1max_open_conns: 1conn_max_lifetime_seconds: 3user:username: unpassword: pwdread_pool:url: pro-read.comport: 5432user_name: read_pool_user_namepassword: read_pool_passworddbname: dbnamemax_idle_Conns: 1max_open_conns: 1conn_max_lifetime_seconds: 3user:username: unpassword: pwd
dev:write_pool:url: dev-write.comport: 11port: 5432user_name: write_pool_user_namepassword: write_pool_passworddbname: dbnamemax_idle_Conns: 1max_open_conns: 1conn_max_lifetime_seconds: 3user:username: unpassword: pwdread_pool:url: dev-read.comport: 5432user_name: read_pool_user_namepassword: read_pool_passworddbname: dbnamemax_idle_Conns: 1max_open_conns: 1conn_max_lifetime_seconds: 3user:username: unpassword: pwd

一种简单的办法是:我们设置一个默认项(default)用于填充相同的值,然后在不同环境中填充不同的值。比如下例:

# db.yaml
version: 1
pro:write_pool:url: pro-write.comport: 11read_pool:url: pro-read.com
pre:write_pool:url: pre-write.comread_pool:url: pre-read.com
test:write_pool:url: test-write.comread_pool:url: test-read.com
dev:write_pool:url: dev-write.comread_pool:url: dev-read.com
default:write_pool:port: 5432user_name: write_pool_user_namepassword: write_pool_passworddbname: dbnamemax_idle_Conns: 1max_open_conns: 1conn_max_lifetime_seconds: 3user:username: unpassword: pwdread_pool:port: 5432user_name: read_pool_user_namepassword: read_pool_passworddbname: dbnamemax_idle_Conns: 1max_open_conns: 1conn_max_lifetime_seconds: 3user:username: unpassword: pwd

这样我们在取pro、pre、dev和test环境的配置时,会让它们和default取合集,从而变成一个完整的配置。

实现

具体实现如下:

package configparserimport ("fmt""os""reflect""gopkg.in/yaml.v3"
)type Config struct {Version string      `yaml:"version"`Pro     interface{} `yaml:"pro"`Pre     interface{} `yaml:"pre"`Dev     interface{} `yaml:"dev"`Test    interface{} `yaml:"test"`Default interface{} `yaml:"default"`
}const (ErrorEnvNotfound = "env [%s] not found"KeyFieldTag      = "yaml"
)func LoadConfigFromFile(filePath string, env string) (string, error) {data, err := os.ReadFile(filePath)if err != nil {return "", err}return LoadConfigFromMemory(data, env)
}func LoadConfigFromMemory(configure []byte, env string) (string, error) {var config Configerr := yaml.Unmarshal(configure, &config)if err != nil {return "", err}configReflectType := reflect.TypeOf(config)for i := 0; i < configReflectType.NumField(); i++ {structTag := configReflectType.Field(i).Tag.Get(KeyFieldTag)if structTag == env {envConfigReflect := reflect.ValueOf(config).Field(i).Interface()defauleConfigReflectType := config.Defaultif envConfigReflect == nil && defauleConfigReflectType == nil {return "", fmt.Errorf(ErrorEnvNotfound, env)}if envConfigReflect == nil {defaultConf, err := yaml.Marshal(config.Default)if err != nil {return "", err}return string(defaultConf), nil}if defauleConfigReflectType == nil {envConf, err := yaml.Marshal(reflect.ValueOf(config).Field(i).Interface())if err != nil {return "", err}return string(envConf), nil}merged := mergeMapStringInterface(reflect.ValueOf(config).Field(i).Interface().(map[string]interface{}), config.Default.(map[string]interface{}))mergedConf, err := yaml.Marshal(merged)if err != nil {return "", err}return string(mergedConf), nil}}return "", fmt.Errorf(ErrorEnvNotfound, env)
}func mergeMapStringInterface(cover map[string]interface{}, base map[string]interface{}) map[string]interface{} {for k, v := range cover {switch v.(type) {case map[string]interface{}:if base[k] == nil {base[k] = v} else {mergeMapStringInterface(v.(map[string]interface{}), base[k].(map[string]interface{}))}default:base[k] = v}}return base
}

调用例子

package mainimport ("fmt"configparser "gconf""os""path""gopkg.in/yaml.v3"
)type PostgresSqlConnConfigs struct {WritePool PostgresSqlConnPoolConf `yaml:"write_pool"`ReadPool  PostgresSqlConnPoolConf `yaml:"read_pool"`
}type PostgresSqlConnPoolConf struct {Url                    *string `yaml:"url"`Port                   *string `yaml:"port"`UserName               *string `yaml:"user_name"`Password               *string `yaml:"password"`DbName                 *string `yaml:"dbname"`MaxIdleConn            *int    `yaml:"max_idle_Conns"`MaxOpenConn            *int    `yaml:"max_open_conns"`ConnMaxLifetimeSeconds *int64  `yaml:"conn_max_lifetime_seconds"`UserA                  *User   `yaml:"user"`
}type User struct {Username *string `yaml:"username"`Password *string `yaml:"password"`
}func main() {runPath, _ := os.Getwd()confPath := path.Join(runPath, "conf/db.yaml")env := []string{"dev", "test", "pre", "pro"}for _, v := range env {var conf ExampleConfigcurConfig, err := configparser.LoadConfigFromFile(confPath, v)if err != nil {fmt.Printf("load config file failed, err: %v", err)}err = yaml.Unmarshal([]byte(curConfig), &conf)if err != nil {fmt.Printf("unmarshal config file failed, err: %v", err)}fmt.Printf("%s\nconfig: %v\n", v, conf)}
}
http://www.lryc.cn/news/107982.html

相关文章:

  • 数据安全能力框架模型-详细解读(二)
  • 【BASH】回顾与知识点梳理(八)
  • rust报错“Utf8Error { valid_up_to: 1, error_len: Some(1) } }”
  • 【Linux】节点之间配置免密登录
  • 【13】STM32·HAL库-正点原子SYSTEM文件夹 | SysTick工作原理、寄存器介绍 | printf函数使用、重定向
  • ansible配置文件案例
  • 【大数据】Flink 从入门到实践(一):初步介绍
  • 大数据课程F4——HIve的其他操作
  • React Native详解和代码实例
  • CAD随机球体颗粒过渡区3D插件
  • 【项目 进程12】2.25 sigprocmask函数使用 2.26sigaction信号捕捉函数 2.27SIGCHILD信号
  • 【无标题】面试题 02.07. 链表相交
  • Zotero ubuntu2023安装 关联 ubuntu文献翻译
  • Stable Diffusion教程(7) - PS安装AI绘画插件教程
  • 如何学技术
  • 【云存储】使用OSS快速搭建个人网盘教程(阿里云)
  • 微信小程序iconfont真机渲染失败
  • 万界星空/推出生产制造执行MES系统/开源MES/免费下载
  • 【VxWorks】Vxworks、QNX、Xenomai、Intime、Sylixos、Ucos等实时操作系统的性能特点
  • 17、YML配置文件及让springboot启动时加载我们自定义的yml配置文件的几种方式
  • 18、springboot默认的配置文件及导入额外配置文件
  • Conda换源(Linux)
  • 【C语言学习】数据类型转换
  • 深入了解PostgreSQL:高级查询和性能优化技巧
  • 【C#学习笔记】值类型(1)
  • 二十三种设计模式第二十二篇--中介者模式
  • 小研究 - 微服务系统服务依赖发现技术综述(二)
  • javaee 泛型的上下边界和通配符的使用
  • 【TypeScript】类型声明及应用(二)
  • rust from_utf8_lossy怎么使用?