配置工具的选择
通常,在一个或多个项目中会有多种格式的配置文件,比如PHP的php.ini文件、Nginx的server.conf文件,那么使用Golang怎么去读取这些不同格式的配置文件呢?
比如常见的有 JSON文件、INI文件、YAML文件和TOML文件等等。其中这些文件,对应的Golang处理库如下:
- encoding/json – 标准库中的包,可以处理JSON配置文件,缺点是不能加注释
- gcfg&goconfig – 处理INI配置文件
- toml – 处理TOML配置文件
- viper – 处理JSON, TOML, YAML, HCL以及Java properties配置文件
通常情况下,推荐使用viper库来读取配置文件,虽然它不支持ini格式的配置文件,但我们可以使用goconfig 或gcfg.v1库读取ini 格式配置文件。
viper 支持以下功能:
- 支持Yaml、Json、 TOML、HCL 等格式的配置文件
- 可以从文件、 io.Reader 、环境变量、cli命令行读取配置
- 支持自动转换的类型解析
- 可以远程从Key/Value中读取配置,需要导入 viper/remote 包
- 监听配置文件。以往我们修改配置文件后需要重启服务生效,而Viper使用watch函数可以让配置自动生效。
安装viper
go get github.com/spf13/vipergo get github.com/fsnotify/fsnotify
使用viper读取JSON配置文件
假设现在有一份 json 格式的配置文件 config.json
{"date": "2019-04-30","mysql": {"url": "127.0.0.1:3306","username": "root","password": "123456"}}
读取json配置文件
package mainimport ("fmt""github.com/spf13/viper""os")func main() {viper.SetConfigName("config") //设置配置文件的名字viper.AddConfigPath(".") //添加配置文件所在的路径viper.SetConfigType("json") //设置配置文件类型,可选err := viper.ReadInConfig()if err != nil {fmt.Printf("config file error: %s\n", err)os.Exit(1)}urlValue := viper.Get("mysql.url")fmt.Println("mysql url:", urlValue)fmt.Printf("mysql url: %s\n mysql username: %s\n mysql password: %s", viper.Get("mysql.url"), viper.Get("mysql.username"), viper.Get("mysql.password"))}
运行程序,查看效果
$ go run viper_json.gomysql url: 127.0.0.1:3306mysql url: 127.0.0.1:3306mysql username: rootmysql password: 123456
使用viper读取yaml配置文件
假设现在有一份yaml格式的配置文件 config_yaml.yaml
port: 10666mysql:url: "127.0.0.1:3306"username: rootpassword: 123456
读取yaml配置文件
package mainimport ("fmt""github.com/spf13/viper""github.com/fsnotify/fsnotify""os")func main() {viper.SetConfigName("config_yaml") //把json文件换成yaml文件,只需要配置文件名 (不带后缀)即可viper.AddConfigPath(".") //添加配置文件所在的路径//viper.SetConfigType("json") //设置配置文件类型err := viper.ReadInConfig()if err != nil {fmt.Printf("config file error: %s\n", err)os.Exit(1)}viper.WatchConfig() //监听配置变化viper.OnConfigChange(func(e fsnotify.Event) {fmt.Println("配置发生变更:", e.Name)})urlValue := viper.Get("mysql.url")fmt.Println("mysql url:", urlValue)fmt.Printf("mysql url: %s\nmysql username: %s\nmysql password: %s", viper.Get("mysql.url"), viper.Get("mysql.username"), viper.GetString("mysql.password"))}
viper其他重要功能
获取子级配置
当配置层级关系较多的时候,有时候我们需要直接获取某个子级的所有配置,可以这样操作:
app:cache1:max-items: 100item-size: 64cache2:max-items: 200item-size: 80
如果要读取cache1下的max-items,只需要执行viper.Get(“app.cache1.max-items”)就可以了。
解析配置
可以将配置绑定到某个结构体、map上,有两个方法可以做到这一点:
Unmarshal(rawVal interface{}) : errorUnmarshalKey(key string, rawVal interface{}) : errorvar config Configvar mysql MySQLerr := Unmarshal(&config) // 将配置解析到 config 变量if err != nil {t.Fatalf("unable to decode into struct, %v", err)}err := UnmarshalKey("mysql", &mysql) // 将配置解析到 mysql 变量if err != nil {t.Fatalf("unable to decode into struct, %v", err)}
获取值
在Viper中,有一些根据值的类型获取值的方法,存在以下方法:
Get(key string) : interface{}GetBool(key string) : boolGetFloat64(key string) : float64GetInt(key string) : intGetString(key string) : stringGetStringMap(key string) : map[string]interface{}GetStringMapString(key string) : map[string]stringGetStringSlice(key string) : []stringGetTime(key string) : time.TimeGetDuration(key string) : time.DurationIsSet(key string) : bool
如果 Get 函数未找到值,则返回对应类型的一个零值。可以通过 IsSet() 方法来检测一个健是否存在。
viper.GetString("logfile")if viper.GetBool("verbose") {fmt.Println("verbose enabled")}
修改对应的配置
viper.Set("Verbose", true)viper.Set("LogFile", LogFile)
使用goconfig读取ini配置文件
安装goconfig
go get github.com/Unknwon/goconfig
假设database.conf配置文件,如下所示
[mysql]username=rootpassword=123456url=127.0.0.1:3306[redis]address=127.0.0.1:6379
使用goconfig读取ini格式配置文件
package mainimport ("fmt""github.com/Unknwon/goconfig""os")var cfg *goconfig.ConfigFilefunc init() {config, err := goconfig.LoadConfigFile("database.conf") //加载配置文件if err != nil {fmt.Println("get config file error")os.Exit(-1)}cfg = config}func GlobalConfig() {glob, _ := cfg.GetSection("mysql") //读取全部mysql配置fmt.Println(glob)}func main() {password, _ := cfg.GetValue("mysql", "password") //读取单个值fmt.Println(password)username, _ := cfg.GetValue("mysql", "username") //读取单个值fmt.Println(username)err := cfg.Reload() //重载配置if err != nil {fmt.Printf("reload config file error: %s", err)}GlobalConfig()}
加载完全局配置后,该配置长驻内存,需要动态加载的话,使用cfg.Reload()方法。
运行程序,效果如下。
$ go run goconfig.go123456rootmap[password:123456 url:127.0.0.1:3306 username:root]
