第一部分 前景提要
一、背景
用Go搭一个小项目, 按传统的套路把开发环境和生产环境配置信息写在配置文件config.json中, 初始化加载配置,解析出数据。看着是解析一个JSON的小事,过程中却发生了3次演进:
1、定义一个配置文件对应的结构体。
2、递归加载配置的全部节点。
3、使用viper
思路1: 是最初的想法,考虑到动态加配置的话,还需要改结构体,这就有点low了,故而并没有实施,仅停留在思考层面;
思路2: 参考网上的一个实现,拿来简单调整了下,发现挺好用,但是发现不能获取一个子节点下的全部key, 要自己实现的话,看看项目的排期,是肯定来不及的;
思路3: 是用现有的开源包viper, 确实好用。
本文就把没有实现和实现了的方案都在这里模拟实现下,希望顺便解释了为什么读个JOSN文件还要用包viper
config.json内容大致如下
{ "dev": { "dockerAddr": "xxx", "appName": "office", "version": "20210315115158", "tag": "1", "etcdEnv": { "endpoint": "https://xxx,https://xxx", "namespace": "v10", "prefix": "v10", "user": "xxx", "password": "xxx" } }, "online": { "dockerAddr": "xxx", "appName": "office", "version": "20210315115158", "tag": "1", "etcdEnv": { "endpoint": "https://xxx,https://xxx", "namespace": "v10", "prefix": "v10", "user": "xxx", "password": "xxx" } }, }
第二部分 定义结构体
一、Go 处理JSON标准库
Go内置处理JSON标准库是encoding/json
其中,两个常用的方法为Marshal和Unmarshal, 分别实现数据的序列号和反序列化。
1、Marshal
func Marshal(v interface{}) ([]byte, error)
2、Unmarshal
func Unmarshal(data []byte, v interface{}) error
二、具体实现
为什么要定义一个结构体,定义一个map不行咩
1、map可转json
https://www.jianshu.com/p/f7f930152482
2、json转map
https://blog.csdn.net/lanyang123456/article/details/78930071
3、struct转json
https://www.cnblogs.com/liuhe688/p/10971327.html
package main import ( "encoding/json" "fmt" "io/ioutil" ) type EtcdConfig struct { Endpoint string Namespace string Prefix string User string Password string } type BaseConfig struct { DockerAddr string AppName string Version int Tag string EtcdEnv EtcdConfig } // Dev和Online必须和json中同名,不然返回空 type Config struct { Dev BaseConfig Online BaseConfig } func main() { var config Config data, err := ioutil.ReadFile("conf/config.json") if err != nil { panic(err) } err = json.Unmarshal(data, &config) if err != nil { panic(err) } fmt.Println(config) / }
以上输出
{{xxx office-dev 101 20210315115158 {https://xxx,https://xxx v10 v10 xxx xxx}} {xxx office-online 1001 20210315115158 {https://xxx,https://xxx v10 v10 xxx xxx}}}
第三部分 递归加载配置
1、加载配置的config包
package config import ( "encoding/json" "os" ) const ( defaultConfigPath = "conf/config.json" ) var ( raw map[string]interface{} config = make(map[string]interface{}) ) func loadConfigs(prefix string, o interface{}) { if m, ok := o.(map[string]interface{}); ok { if len(prefix) > 0 { prefix = prefix + ":" } for k, v := range m { key := prefix + k loadConfigs(key, v) } } else { config[prefix] = o } } func LoadConfigs() (err error) { file, err := os.Open(defaultConfigPath) if err != nil { panic(err) return } decoder := json.NewDecoder(file) err = decoder.Decode(&raw) loadConfigs("", raw) return } func String(key string, defaultValue string) (value string) { value, _ = getString(key, defaultValue) return } func getString(key string, defaultValue string) (value string, found bool) { v, found := config[key] if !found { return defaultValue, false } if value, ok := v.(string); ok { return value, ok } else { return defaultValue, false } }
完整内容参考:Go 项目如何加载 config.json 格式的配置文件
2、用法
用冒号分割key, 直接取值即可
func init() { config.LoadConfigs() addr := config.String("dev:dockerAddr", "") fmt.Println(addr) }
第四部分 使用包viper
一、关于viper
1、一个优秀的读取配置Go包
2、github地址:https://github.com/spf13/viper
二、用法
viper.AddConfigPath("./conf/") viper.SetConfigName("server") err := viper.ReadInConfig() // 读取配置数据 if err != nil { panic(fmt.Errorf("Fatal error config file: %s \n", err)) }
三、其他详细用法参考
在 Go 中使用 Viper 加载配置文件
Go语言配置管理神器——Viper中文教程
Go 每日一库之 viper