目的:使用viper解析yaml格式的配置文件,进行学习总结
一、yaml配置文件:
db:
mysql:
host-and-port: "127.0.0.1:3306"
user: "root"
password: "123456"
db-name: "test"
max-idle-conns: 50
max-open-conns: 200
show-log: true
clickhouse:
hostAndPort: "127.0.0.1:9192"
user: "default"
dbName: "default"
comConf:
maxIdleConns: 50
maxOpenConns: 200
此处配置了mysql和clickhouse两个数据库
二、string、map转struct工具类
package common
import (
"encoding/json"
"errors"
"github.com/mitchellh/mapstructure"
"reflect"
)
//parse interface to struct
func ParseInterface2Struct(in interface{}, out interface{}) error {
kind := reflect.TypeOf(in).Kind()
if reflect.Map == kind {
err := mapstructure.Decode(in, out)
if err != nil {
return err
}
} else if reflect.String == kind {
err := json.Unmarshal([]byte(in.(string)), out)
if err != nil {
return err
}
} else {
return errors.New(fmt.Sprintf("Can not parse this type : %s ! ", kind.String()))
}
return nil
}
此处只对json字符串和map进行转换(输入为interface,输出为指针)
三、读取配置文件并进行初始化
package common
import (
"flag"
"fmt"
"github.com/spf13/viper"
"sort"
)
type DbConf struct {
HostAndPort string `mapstructure:"hostAndPort"`
User string `mapstructure:"user"`
Password string `mapstructure:"password"`
DbName string `mapstructure:"dbName"`
ComConf commonConf `mapstructure:"comConf"`
}
type commonConf struct {
MaxIdleConns int `mapstructure:"maxIdleConns"`
MaxOpenConns int `mapstructure:"maxOpenConns"`
}
func InitConf() {
cond := flag.String("cond", ".", "conf dir ,conf file type is yaml")
flag.Parse()
viper.AddConfigPath(*cond)
viper.SetConfigName("conf")
viper.SetConfigType("yaml")
viper.AddConfigPath("config")
viper.AddConfigPath("/etc/feeds/")
viper.AddConfigPath("$HOME/.feeds")
err := viper.ReadInConfig()
if err != nil {
panic(fmt.Errorf("Fatal error config file: %s \n", err))
}
keys := viper.AllKeys()
sort.Strings(keys)
for _, key := range keys {
fmt.Printf("%s = %s\n", key, viper.GetString(key))
}
}
func InitDbConf(dbType string) *DbConf {
var dbConf DbConf
confKey := fmt.Sprintf("%s%s", "db.", dbType)
dbConfMap := viper.GetStringMap(confKey)
if dbConfMap == nil {
fmt.Printf("The conf of %+v db is not exist", dbType)
return nil
}
err := ParseInterface2Struct(dbConfMap, &dbConf)
if err != nil {
fmt.Printf("Parse confStr : %+v to struct err , err : %+v", dbConfMap, err)
return nil
}
return &dbConf
}
此处将数据库类型作为key值传入
四、main函数
package main
import (
"data/common"
"fmt"
)
func main() {
common.InitConf()
dbc := common.InitDbConf("clickhouse")
if dbc != nil {
fmt.Println(dbc)
}
}
五、总结
- viper的底层是map,所以他可以支持直接get的方式获取配置的value,这种方式很便捷,但是如果一个配置使用的地方较多,而且项目配置很多的时候,继续使用直接get的方式就会变得很繁琐
- viper第一次加载的时候,会将配置文件直接读到内存中,后续的读取都是直接从内存获取,不会继续读取文件
- 在初始化的时候使用flag,可以将viper读取配置文件的路径参数化,即可以在启动的时候,通过参数名“cond”对配置文件的路径进行配置(方便在不同环境下启动的时候读取不同的配置文件)
- 参数struct的“mapstructure”属性,需要和配置文件中的key值保持一致,文中的转实体方法是根据mapstructure的名称进行一一对应的
- 参数的实体首字母需要大写,否则配置转为struct后,struct的值都是各个类型的默认值(主要原因是go语言的变量及方法的权限控制导致)
- 文中的参数struct使用了两极的配置方式,即DBConf包含了commonConf,至于第三层甚至第四层应该也是支持
原创不易,转发请注明出处!
欢迎交流,共同进步!