您好,登錄后才能下訂單哦!
這篇文章主要介紹Golang中配置信息處理框架Viper有什么用,文中介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們一定要看完!
項目地址:https://github.com/spf13/viper
本文翻譯自該項目里README.md文件中的內容
有很多Go語言項目用到了Viper框架,比如:
Hugo
EMC RexRay
Imgur’s Incus
Nanobox/Nanopack
Docker Notary
BloomApi
doctl
Clairctl
Viper是一個方便Go語言應用程序處理配置信息的庫。它可以處理多種格式的配置。它支持的特性:
設置默認值
從JSON、TOML、YAML、HCL和Java properties文件中讀取配置數據
可以監視配置文件的變動、重新讀取配置文件
從環境變量中讀取配置數據
從遠端配置系統中讀取數據,并監視它們(比如etcd、Consul)
從命令參數中讀物配置
從buffer中讀取
調用函數設置配置信息
在構建現代應用程序時,您不必擔心配置文件格式; 你可以專注于構建出色的軟件。
Viper 可以做如下工作:
加載并解析JSON、TOML、YAML、HCL 或 Java properties 格式的配置文件
可以為各種配置項設置默認值
可以在命令行中指定配置項來覆蓋配置值
提供了別名系統,可以不破壞現有代碼來實現參數重命名
可以很容易地分辨出用戶提供的命令行參數或配置文件與默認相同的區別
Viper讀取配置信息的優先級順序,從高到低,如下:
顯式調用Set函數
命令行參數
環境變量
配置文件
key/value 存儲系統
默認值
Viper 的配置項的key不區分大小寫。
默認值不是必須的,如果配置文件、環境變量、遠程配置系統、命令行參數、Set函數都沒有指定時,默認值將起作用。
例子:
viper.SetDefault("ContentDir", "content") viper.SetDefault("LayoutDir", "layouts") viper.SetDefault("Taxonomies", map[string]string{"tag": "tags", "category": "categories"})
Viper支持JSON、TOML、YAML、HCL和Java properties文件。
Viper可以搜索多個路徑,但目前單個Viper實例僅支持單個配置文件。
Viper默認不搜索任何路徑。
以下是如何使用Viper搜索和讀取配置文件的示例。
路徑不是必需的,但最好至少應提供一個路徑,以便找到一個配置文件。
viper.SetConfigName("config") // 設置配置文件名 (不帶后綴) viper.AddConfigPath("/etc/appname/") // 第一個搜索路徑 viper.AddConfigPath("$HOME/.appname") // 可以多次調用添加路徑 viper.AddConfigPath(".") // 比如添加當前目錄 err := viper.ReadInConfig() // 搜索路徑,并讀取配置數據 if err != nil { panic(fmt.Errorf("Fatal error config file: %s \n", err)) }
Viper支持讓您的應用程序在運行時擁有讀取配置文件的能力。
需要重新啟動服務器以使配置生效的日子已經一去不復返了,由viper驅動的應用程序可以在運行時讀取已更新的配置文件,并且不會錯過任何節拍。
只需要調用viper實例的WatchConfig函數,你也可以指定一個回調函數來獲得變動的通知。
viper.WatchConfig() viper.OnConfigChange(func(e fsnotify.Event) { fmt.Println("Config file changed:", e.Name) })
Viper預先定義了許多配置源,例如文件、環境變量、命令行參數和遠程K / V存儲系統,但您并未受其約束。
您也可以實現自己的配置源,并提供給viper。
viper.SetConfigType("yaml") // or viper.SetConfigType("YAML") // any approach to require this configuration into your program. var yamlExample = []byte(` Hacker: true name: steve hobbies: - skateboarding - snowboarding - go clothing: jacket: leather trousers: denim age: 35 eyes : brown beard: true `) viper.ReadConfig(bytes.NewBuffer(yamlExample)) viper.Get("name") // 返回 "steve"
viper.Set("Verbose", true) viper.Set("LogFile", LogFile)
別名可以實現多個key引用單個值。
viper.RegisterAlias("loud", "Verbose") viper.Set("verbose", true) viper.Set("loud", true) // 這兩句設置的都是同一個值 viper.GetBool("loud") // true viper.GetBool("verbose") // true
Viper 完全支持環境變量,這是的應用程序可以開箱即用。
有四個和環境變量有關的方法:
AutomaticEnv()
BindEnv(string...) : error
SetEnvPrefix(string)
SetEnvKeyReplacer(string...) *strings.Replacer
注意,環境變量時區分大小寫的。
Viper提供了一種機制來確保Env變量是唯一的。通過SetEnvPrefix,在從環境變量讀取時會添加設置的前綴。BindEnv和AutomaticEnv都會使用到這個前綴。
BindEnv需要一個或兩個參數。第一個參數是鍵名,第二個參數是環境變量的名稱。環境變量的名稱區分大小寫。如果未提供ENV變量名稱,則Viper會自動假定該鍵名稱與ENV變量名稱匹配,并且ENV變量為全部大寫。當您顯式提供ENV變量名稱時,它不會自動添加前綴。
使用ENV變量時要注意,當關聯后,每次訪問時都會讀取該ENV值。Viper在BindEnv調用時不讀取ENV值。
AutomaticEnv與SetEnvPrefix結合將會特別有用。當AutomaticEnv被調用時,任何viper.Get請求都會去獲取環境變量。環境變量名為SetEnvPrefix設置的前綴,加上對應名稱的大寫。
SetEnvKeyReplacer允許你使用一個strings.Replacer對象來將配置名重寫為Env名。如果你想在Get()中使用包含-的配置名 ,但希望對應的環境變量名包含_分隔符,就可以使用該方法。使用它的一個例子可以在項目中viper_test.go文件里找到。
例子:
SetEnvPrefix("spf") // 將會自動轉為大寫 BindEnv("id") os.Setenv("SPF_ID", "13") // 通常通過系統環境變量來設置 id := Get("id") // 13
Viper支持綁定pflags參數。
和BindEnv一樣,當綁定方法被調用時,該值沒有被獲取,而是在被訪問時獲取。這意味著應該盡早進行綁定,甚至是在init()函數中綁定。
利用BindPFlag()方法可以綁定單個flag。
例子:
serverCmd.Flags().Int("port", 1138, "Port to run Application server on") viper.BindPFlag("port", serverCmd.Flags().Lookup("port"))
你也可以綁定已存在的pflag集合 (pflag.FlagSet):
pflag.Int("flagname", 1234, "help message for flagname") pflag.Parse() viper.BindPFlags(pflag.CommandLine) i := viper.GetInt("flagname") // 通過viper從pflag中獲取值
使用pflag并不影響其他庫使用標準庫中的flag。通過導入,pflag可以接管通過標準庫的flag定義的參數。這是通過調用pflag包中的AddGoFlagSet()方法實現的。
例子:
package main import ( "flag" "github.com/spf13/pflag" ) func main() { // using standard library "flag" package flag.Int("flagname", 1234, "help message for flagname") pflag.CommandLine.AddGoFlagSet(flag.CommandLine) pflag.Parse() viper.BindPFlags(pflag.CommandLine) i := viper.GetInt("flagname") // retrieve value from viper ... }
如果你不想使用pflag,Viper 提供了兩個接口來實現綁定其他的flag系統。
使用 FlagValue 接口代表單個flag。下面是實現了該接口的簡單的例子:
type myFlag struct {} func (f myFlag) HasChanged() bool { return false } func (f myFlag) Name() string { return "my-flag-name" } func (f myFlag) ValueString() string { return "my-flag-value" } func (f myFlag) ValueType() string { return "string" }
一旦你實現了該接口,就可以綁定它:
viper.BindFlagValue("my-flag-name", myFlag{})
使用 FlagValueSet 接口代表一組flag。下面是實現了該接口的簡單的例子:
type myFlagSet struct { flags []myFlag } func (f myFlagSet) VisitAll(fn func(FlagValue)) { for _, flag := range flags { fn(flag) } }
一旦你實現了該接口,就可以綁定它:
fSet := myFlagSet{ flags: []myFlag{myFlag{}, myFlag{}}, } viper.BindFlagValues("my-flags", fSet)
啟用該功能,需要導入viper/remot包:
import _ "github.com/spf13/viper/remote"
Viper 可以從例如etcd、Consul的遠程Key/Value存儲系統的一個路徑上,讀取一個配置字符串(JSON, TOML, YAML或HCL格式)。
這些值優先于默認值,但會被從磁盤文件、命令行flag、環境變量的配置所覆蓋。
Viper 使用 crypt 來從 K/V 存儲系統里讀取配置,這意味著你可以加密儲存你的配置信息,并且可以自動解密配置信息。加密是可選的。
您可以將遠程配置與本地配置結合使用,也可以獨立使用。
crypt 有一個命令行工具可以幫助你存儲配置信息到K/V存儲系統,crypt默認使用 http://127.0.0.1:4001 上的etcd。
$ go get github.com/xordataexchange/crypt/bin/crypt $ crypt set -plaintext /config/hugo.json /Users/hugo/settings/config.json
確認你的值被設置:
$ crypt get -plaintext /config/hugo.json
有關crypt如何設置加密值或如何使用Consul的示例,請參閱文檔。
viper.AddRemoteProvider("etcd", "http://127.0.0.1:4001","/config/hugo.json") viper.SetConfigType("json") // 因為不知道格式,所以需要指定,支持的格式有"json"、"toml"、"yaml"、"yml"、"properties"、"props"、"prop" err := viper.ReadRemoteConfig()
viper.AddSecureRemoteProvider("etcd","http://127.0.0.1:4001","/config/hugo.json","/etc/secrets/mykeyring.gpg") viper.SetConfigType("json") // 因為不知道格式,所以需要指定,支持的格式有"json"、"toml"、"yaml"、"yml"、"properties"、"props"、"prop" err := viper.ReadRemoteConfig()
// 您可以創建一個新的viper實例 var runtime_viper = viper.New() runtime_viper.AddRemoteProvider("etcd", "http://127.0.0.1:4001", "/config/hugo.yml") runtime_viper.SetConfigType("yaml") // 因為不知道格式,所以需要指定,支持的格式有"json"、"toml"、"yaml"、"yml"、"properties"、"props"、"prop" // 從遠程讀取配置 err := runtime_viper.ReadRemoteConfig() // 解析配置到runtime_conf中 runtime_viper.Unmarshal(&runtime_conf) // 通過一個goroutine遠程的配置變化 go func(){ for { time.Sleep(time.Second * 5) // delay after each request // currently, only tested with etcd support err := runtime_viper.WatchRemoteConfig() if err != nil { log.Errorf("unable to read remote config: %v", err) continue } // 解析新的配置到一個結構體變量中,你也可以使用channel實現一個信號通知的方式 runtime_viper.Unmarshal(&runtime_conf) } }()
在Viper中,有一些根據值的類型獲取值的方法。存在一下方法:
Get(key string) : interface{}
GetBool(key string) : bool
GetFloat64(key string) : float64
GetInt(key string) : int
GetString(key string) : string
GetStringMap(key string) : map[string]interface{}
GetStringMapString(key string) : map[string]string
GetStringSlice(key string) : []string
GetTime(key string) : time.Time
GetDuration(key string) : time.Duration
IsSet(key string) : bool
如果Get函數未找到值,則返回對應類型的一個零值。可以通過 IsSet() 方法來檢測一個健是否存在。
例子:
viper.GetString("logfile") // Setting & Getting 不區分大小寫 if viper.GetBool("verbose") { fmt.Println("verbose enabled") }
訪問方法也接受嵌套的鍵。例如,如果加載了以下JSON文件:
{ "host": { "address": "localhost", "port": 5799 }, "datastore": { "metric": { "host": "127.0.0.1", "port": 3099 }, "warehouse": { "host": "198.0.0.1", "port": 2112 } } }
Viper可以通過.分隔符來訪問嵌套的字段:
GetString("datastore.metric.host") // (returns "127.0.0.1")
這遵守前面確立的優先規則; 會搜索路徑中所有配置,直到找到為止。
例如,上面的文件,datastore.metric.host和 datastore.metric.port都已經定義(并且可能被覆蓋)。如果另外 datastore.metric.protocol的默認值,Viper也會找到它。
但是,如果datastore.metric值被覆蓋(通過標志,環境變量,Set方法,...),則所有datastore.metric的子鍵將會未定義,它們被優先級更高的配置值所“遮蔽”。
最后,如果存在相匹配的嵌套鍵,則其值將被返回。例如:
{ "datastore.metric.host": "0.0.0.0", "host": { "address": "localhost", "port": 5799 }, "datastore": { "metric": { "host": "127.0.0.1", "port": 3099 }, "warehouse": { "host": "198.0.0.1", "port": 2112 } } } GetString("datastore.metric.host") // returns "0.0.0.0"
可以從viper中提取子樹。例如, viper配置為:
app: cache1: max-items: 100 item-size: 64 cache2: max-items: 200 item-size: 80
執行后:
subv := viper.Sub("app.cache1")
subv 就代表:
max-items: 100 item-size: 64
假如我們有如下函數:
func NewCache(cfg *Viper) *Cache {...}
它的功能是根據配置信息創建緩存緩存。現在很容易分別創建這兩個緩存:
cfg1 := viper.Sub("app.cache1") cache1 := NewCache(cfg1) cfg2 := viper.Sub("app.cache2") cache2 := NewCache(cfg2)
您還可以選擇將所有或特定值解析到struct、map等。
有兩個方法可以做到這一點:
Unmarshal(rawVal interface{}) : error
UnmarshalKey(key string, rawVal interface{}) : error
例如:
type config struct { Port int Name string PathMap string `mapstructure:"path_map"` } var C config err := Unmarshal(&C) if err != nil { t.Fatalf("unable to decode into struct, %v", err) }
Viper隨時準備使用開箱即用。沒有任何配置或初始化也可以使用Viper。由于大多數應用程序都希望使用單個存儲中心進行配置,因此viper包提供了此功能。它類似于一個單例模式。
在上面的所有示例中,他們都演示了如何使用viper的單例風格的方式。
您還可以創建多不同的viper實例以供您的應用程序使用。每實例都有自己獨立的設置和配置值。每個實例可以從不同的配置文件,K/V存儲系統等讀取。viper包支持的所有函數也都有對應的viper實例方法。
例子:
x := viper.New() y := viper.New() x.SetDefault("ContentDir", "content") y.SetDefault("ContentDir", "foobar") //...
當使用多個viper實例時,用戶需要自己管理每個實例。
以上是“Golang中配置信息處理框架Viper有什么用”這篇文章的所有內容,感謝各位的閱讀!希望分享的內容對大家有幫助,更多相關知識,歡迎關注億速云行業資訊頻道!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。