第一部分 前景提要
一、背景
用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
}
}
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))
}