什么是Viper
Viper是一個方便Go語言應用程序處理配置信息的庫。它可以處理多種格式的配置。它支持的特性:
- 設置默認值
- 從JSON、TOML、YAML、HCL和Java properties文件中讀取配置數據
- 可以監視配置文件的變動、重新讀取配置文件
- 從環境變量中讀取配置數據
- 從遠端配置系統中讀取數據,並監視它們(比如etcd、Consul)
- 從命令參數中讀物配置
- 從buffer中讀取
- 調用函數設置配置信息
為什么要使用Viper
在構建現代應用程序時,您不必擔心配置文件格式; 你可以專注於構建出色的軟件。
Viper 可以做如下工作:
- 加載並解析JSON、TOML、YAML、HCL 或 Java properties 格式的配置文件
- 可以為各種配置項設置默認值
- 可以在命令行中指定配置項來覆蓋配置值
- 提供了別名系統,可以不破壞現有代碼來實現參數重命名
- 可以很容易地分辨出用戶提供的命令行參數或配置文件與默認相同的區別
Viper讀取配置信息的優先級順序,從高到低,如下:
- 顯式調用Set函數
- 命令行參數
- 環境變量
- 配置文件
- key/value 存儲系統
- 默認值
Viper 的配置項的key不區分大小寫。
項目地址:https://github.com/spf13/viper
使用
設置默認值
默認值不是必須的,如果配置文件、環境變量、遠程配置系統、命令行參數、Set函數都沒有指定時,默認值將起作用。
viper.SetDefault("name", "xiaoming")
viper.SetDefault("age", "12")
viper.SetDefault("notifyList", []string{"xiaohong","xiaoli","xiaowang"})
讀取配置文件
Viper支持JSON、TOML、YAML、HCL和Java properties文件。
Viper可以搜索多個路徑,但目前單個Viper實例僅支持單個配置文件。
Viper默認不搜索任何路徑。
以下是如何使用Viper搜索和讀取配置文件的示例。
路徑不是必需的,但最好至少應提供一個路徑,以便找到一個配置文件。
viper.SetConfigName("dbConfig") // 設置配置文件名 (不帶后綴)
viper.AddConfigPath("/workspace/appName/") // 第一個搜索路徑
viper.AddConfigPath("/workspace/appName1") // 可以多次調用添加路徑
viper.AddConfigPath(".") // 比如添加當前目錄
err := viper.ReadInConfig() // 搜索路徑,並讀取配置數據
if err != nil {
panic(fmt.Errorf("Fatal error config file: %s \n", err))
}
監視配置文件,重新讀取配置數據
Viper支持讓你的應用程序在運行時擁有讀取配置文件的能力。
只需要調用viper實例的WatchConfig函數,你也可以指定一個回調函數來獲得變動的通知。
viper.WatchConfig()
viper.OnConfigChange(func(e fsnotify.Event) {
fmt.Println("Config file changed:", e.Name)
})
從 io.Reader 中讀取配置
Viper預先定義了許多配置源,例如文件、環境變量、命令行參數和遠程K / V存儲系統。也可以實現自己的配置源,並提供給viper。
現在有如下yaml文件:
userName: "xiaoming"
address: "廣州市XXX"
sex: 1
company:
name: "xxx"
employeeId: 1000
department:
- "技術部"
讀取文件的代碼如下:
package main
import (
"fmt"
"github.com/spf13/viper"
)
type UserInfo struct {
UserName string
Address string
Sex byte
Company Company
}
type Company struct {
Name string
EmployeeId int
Department []interface{}
}
func main() {
//讀取yaml文件
v := viper.New()
//設置讀取的配置文件名
v.SetConfigName("userInfo")
//windows環境下為%GOPATH,linux環境下為$GOPATH
v.AddConfigPath("/Users/yangyue/workspace/go/src/webDemo/")
//設置配置文件類型
v.SetConfigType("yaml")
if err := v.ReadInConfig();err != nil {
fmt.Printf("err:%s\n",err)
}
fmt.Printf("userName:%s sex:%s company.name:%s \n", v.Get("userName"), v.Get("sex"), v.Get("company.name"))
//也可以直接反序列化為Struct
var userInfo UserInfo
if err := v.Unmarshal(&userInfo) ; err != nil{
fmt.Printf("err:%s",err)
}
fmt.Println(userInfo)
}
上面的代碼使用兩種方式獲取配置文件:第一種直接解析為key,value;第二種你可以手動的反序列化為Struct。
從命令行參數中讀取
package main
import (
"fmt"
"github.com/spf13/pflag"
"github.com/spf13/viper"
)
func main() {
pflag.String("ip", "127.0.0.1", "Server running address")
pflag.Int64("port", 8080, "Server running port")
pflag.Parse()
viper.BindPFlags(pflag.CommandLine)
fmt.Printf("ip :%s , port:%s", viper.GetString("ip"), viper.GetString("port"))
}
命令行執行上面程序:
# go run test.go --ip=192.168.7.3 --port=3306
可以看到輸出的是我們自定義的參數。
讀取環境變量參數
一般獲取環境變量使用os
包,比如:
getenv := os.Getenv("JAVA_HOME")
fmt.Print(getenv)
Viper也提供了一種方式:
//表示 先預加載匹配的環境變量
viper.AutomaticEnv()
//讀取已經加載到default中的環境變量
if env := viper.Get("JAVA_HOME"); env == nil {
println("error!")
} else {
fmt.Printf("%#v\n", env)
}
由獲取環境變量我們是不是可以想到多環境參數配置呢?針對線上環境,開發環境分別加載不同yml中的參數。
func initConfig() (err error) {
env := os.Getenv("GO_ENV")
viper.SetConfigName(env)
viper.AddConfigPath("./configs")
viper.SetConfigType("yml")
err = viper.ReadInConfig()
return
}
因為無論是線上環境還是測試環境,肯定有一些參數是公共不變的,那么這一部分參數是否可以抽出來作為一個單獨的配置文件呢。所以這樣配置文件可以分為兩個部分:
"github.com/gobuffalo/packr"
func initConfig() (err error) {
box := packr.NewBox("./configs")
configType := "yml"
defaultConfig, _ := box.Find("default.yml")
v := viper.New()
v.SetConfigType(configType)
err = v.ReadConfig(bytes.NewReader(defaultConfig))
if err != nil {
return
}
configs := v.AllSettings()
// 將default中的配置全部以默認配置寫入
for k, v := range configs {
viper.SetDefault(k, v)
}
env := os.Getenv("GO_ENV")
// 根據配置的env讀取相應的配置信息
if env != "" {
envConfig, _ := box.Find(env + ".yml")
viper.SetConfigType(configType)
err = viper.ReadConfig(bytes.NewReader(envConfig))
if err != nil {
return
}
}
return
}
首先讀取default.yml中的參數,將其寫入default中。然后再根據環境變量讀取不同環境中的參數。
這里使用了packr包,packr包的作用在於將靜態資源打包至應用程序中。