您好,登錄后才能下訂單哦!
本文小編為大家詳細(xì)介紹“golang配置和使用Viper”,內(nèi)容詳細(xì),步驟清晰,細(xì)節(jié)處理妥當(dāng),希望這篇“golang配置和使用Viper”文章能幫助大家解決疑惑,下面跟著小編的思路慢慢深入,一起來學(xué)習(xí)新知識吧。
go get github.com/spf13/viper
Viper是適用于Go應(yīng)用程序(包括Twelve-Factor App
)的完整配置解決方案。它被設(shè)計用于在應(yīng)用程序中工作,并且可以處理所有類型的配置需求和格式。它支持以下特性:
設(shè)置默認(rèn)值
從JSON
、TOML
、YAML
、HCL
、envfile
和Java properties
格式的配置文件讀取配置信息
實(shí)時監(jiān)控和重新讀取配置文件(可選)
從環(huán)境變量中讀取
從遠(yuǎn)程配置系統(tǒng)(etcd或Consul)讀取并監(jiān)控配置變化
從命令行參數(shù)讀取配置
從buffer讀取配置
顯式配置值
在構(gòu)建現(xiàn)代應(yīng)用程序時,你無需擔(dān)心配置文件格式;你想要專注于構(gòu)建出色的軟件。Viper的出現(xiàn)就是為了在這方面幫助你的。
Viper能夠?yàn)槟銏?zhí)行下列操作:
查找、加載和反序列化JSON
、TOML
、YAML
、HCL
、INI
、envfile
和Java properties
格式的配置文件。
提供一種機(jī)制為你的不同配置選項(xiàng)設(shè)置默認(rèn)值。
提供一種機(jī)制來通過命令行參數(shù)覆蓋指定選項(xiàng)的值。
提供別名系統(tǒng),以便在不破壞現(xiàn)有代碼的情況下輕松重命名參數(shù)。
當(dāng)用戶提供了與默認(rèn)值相同的命令行或配置文件時,可以很容易地分辨出它們之間的區(qū)別。
Viper會按照下面的優(yōu)先級。每個項(xiàng)目的優(yōu)先級都高于它下面的項(xiàng)目:
顯示調(diào)用Set
設(shè)置值
命令行參數(shù)(flag)
環(huán)境變量
配置文件
key/value存儲
默認(rèn)值
重要: 目前Viper配置的鍵(Key)是大小寫不敏感的。目前正在討論是否將這一選項(xiàng)設(shè)為可選。
一個好的配置系統(tǒng)應(yīng)該支持默認(rèn)值。鍵不需要默認(rèn)值,但如果沒有通過配置文件、環(huán)境變量、遠(yuǎn)程配置或命令行標(biāo)志(flag)設(shè)置鍵,則默認(rèn)值非常有用。
例如:
viper.SetDefault("ContentDir", "content") viper.SetDefault("LayoutDir", "layouts") viper.SetDefault("Taxonomies", map[string]string{"tag": "tags", "category": "categories"})
Viper需要最少知道在哪里查找配置文件的配置。Viper支持JSON
、TOML
、YAML
、HCL
、envfile
和Java properties
格式的配置文件。Viper可以搜索多個路徑,但目前單個Viper實(shí)例只支持單個配置文件。Viper不默認(rèn)任何配置搜索路徑,將默認(rèn)決策留給應(yīng)用程序。
下面是一個如何使用Viper搜索和讀取配置文件的示例。不需要任何特定的路徑,但是至少應(yīng)該提供一個配置文件預(yù)期出現(xiàn)的路徑。
viper.SetConfigFile("./config.yaml") // 指定配置文件路徑 viper.SetConfigName("config") // 配置文件名稱(無擴(kuò)展名) viper.SetConfigType("yaml") // 如果配置文件的名稱中沒有擴(kuò)展名,則需要配置此項(xiàng) viper.AddConfigPath("/etc/appname/") // 查找配置文件所在的路徑 viper.AddConfigPath("$HOME/.appname") // 多次調(diào)用以添加多個搜索路徑 viper.AddConfigPath(".") // 還可以在工作目錄中查找配置 err := viper.ReadInConfig() // 查找并讀取配置文件 if err != nil { // 處理讀取配置文件的錯誤 panic(fmt.Errorf("Fatal error config file: %s \n", err)) }
在加載配置文件出錯時,你可以像下面這樣處理找不到配置文件的特定情況:
if err := viper.ReadInConfig(); err != nil { if _, ok := err.(viper.ConfigFileNotFoundError); ok { // 配置文件未找到錯誤;如果需要可以忽略 } else { // 配置文件被找到,但產(chǎn)生了另外的錯誤 } } // 配置文件找到并成功解析
注意[自1.6起]: 你也可以有不帶擴(kuò)展名的文件,并以編程方式指定其格式。對于位于用戶$HOME
目錄中的配置文件沒有任何擴(kuò)展名,如.bashrc
。
這里補(bǔ)充兩個問題供讀者解答并自行驗(yàn)證
當(dāng)你使用如下方式讀取配置時,viper會從./conf
目錄下查找任何以config
為文件名的配置文件,如果同時存在./conf/config.json
和./conf/config.yaml
兩個配置文件的話,viper
會從哪個配置文件加載配置呢?
viper.SetConfigName("config") viper.AddConfigPath("./conf")
在上面兩個語句下搭配使用viper.SetConfigType("yaml")
指定配置文件類型可以實(shí)現(xiàn)預(yù)期的效果嗎?
從配置文件中讀取配置文件是有用的,但是有時你想要存儲在運(yùn)行時所做的所有修改。為此,可以使用下面一組命令,每個命令都有自己的用途:
WriteConfig - 將當(dāng)前的viper
配置寫入預(yù)定義的路徑并覆蓋(如果存在的話)。如果沒有預(yù)定義的路徑,則報錯。
SafeWriteConfig - 將當(dāng)前的viper
配置寫入預(yù)定義的路徑。如果沒有預(yù)定義的路徑,則報錯。如果存在,將不會覆蓋當(dāng)前的配置文件。
WriteConfigAs - 將當(dāng)前的viper
配置寫入給定的文件路徑。將覆蓋給定的文件(如果它存在的話)。
SafeWriteConfigAs - 將當(dāng)前的viper
配置寫入給定的文件路徑。不會覆蓋給定的文件(如果它存在的話)。
根據(jù)經(jīng)驗(yàn),標(biāo)記為safe
的所有方法都不會覆蓋任何文件,而是直接創(chuàng)建(如果不存在),而默認(rèn)行為是創(chuàng)建或截斷。
一個小示例:
viper.WriteConfig() // 將當(dāng)前配置寫入“viper.AddConfigPath()”和“viper.SetConfigName”設(shè)置的預(yù)定義路徑 viper.SafeWriteConfig() viper.WriteConfigAs("/path/to/my/.config") viper.SafeWriteConfigAs("/path/to/my/.config") // 因?yàn)樵撆渲梦募懭脒^,所以會報錯 viper.SafeWriteConfigAs("/path/to/my/.other_config")
Viper支持在運(yùn)行時實(shí)時讀取配置文件的功能。
需要重新啟動服務(wù)器以使配置生效的日子已經(jīng)一去不復(fù)返了,viper驅(qū)動的應(yīng)用程序可以在運(yùn)行時讀取配置文件的更新,而不會錯過任何消息。
只需告訴viper實(shí)例watchConfig??蛇x地,你可以為Viper提供一個回調(diào)函數(shù),以便在每次發(fā)生更改時運(yùn)行。
確保在調(diào)用WatchConfig()
之前添加了所有的配置路徑。
viper.WatchConfig() viper.OnConfigChange(func(e fsnotify.Event) { // 配置文件發(fā)生變更之后會調(diào)用的回調(diào)函數(shù) fmt.Println("Config file changed:", e.Name) })
Viper預(yù)先定義了許多配置源,如文件、環(huán)境變量、標(biāo)志和遠(yuǎn)程K/V存儲,但你不受其約束。你還可以實(shí)現(xiàn)自己所需的配置源并將其提供給viper。
viper.SetConfigType("yaml") // 或者 viper.SetConfigType("YAML") // 任何需要將此配置添加到程序中的方法。 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"
這些可能來自命令行標(biāo)志,也可能來自你自己的應(yīng)用程序邏輯。
viper.Set("Verbose", true) viper.Set("LogFile", LogFile)
別名允許多個鍵引用單個值
viper.RegisterAlias("loud", "Verbose") // 注冊別名(此處loud和Verbose建立了別名) viper.Set("verbose", true) // 結(jié)果與下一行相同 viper.Set("loud", true) // 結(jié)果與前一行相同 viper.GetBool("loud") // true viper.GetBool("verbose") // true
Viper完全支持環(huán)境變量。這使Twelve-Factor App
開箱即用。有五種方法可以幫助與ENV協(xié)作:
AutomaticEnv()
BindEnv(string...) : error
SetEnvPrefix(string)
SetEnvKeyReplacer(string...) *strings.Replacer
AllowEmptyEnv(bool)
使用ENV變量時,務(wù)必要意識到Viper將ENV變量視為區(qū)分大小寫。
Viper提供了一種機(jī)制來確保ENV變量是惟一的。通過使用SetEnvPrefix
,你可以告訴Viper在讀取環(huán)境變量時使用前綴。BindEnv
和AutomaticEnv
都將使用這個前綴。
BindEnv
使用一個或兩個參數(shù)。第一個參數(shù)是鍵名稱,第二個是環(huán)境變量的名稱。環(huán)境變量的名稱區(qū)分大小寫。如果沒有提供ENV變量名,那么Viper將自動假設(shè)ENV變量與以下格式匹配:前綴+ “_” +鍵名全部大寫。當(dāng)你顯式提供ENV變量名(第二個參數(shù))時,它 不會 自動添加前綴。例如,如果第二個參數(shù)是“id”,Viper將查找環(huán)境變量“ID”。
在使用ENV變量時,需要注意的一件重要事情是,每次訪問該值時都將讀取它。Viper在調(diào)用BindEnv
時不固定該值。
AutomaticEnv
是一個強(qiáng)大的助手,尤其是與SetEnvPrefix
結(jié)合使用時。調(diào)用時,Viper會在發(fā)出viper.Get
請求時隨時檢查環(huán)境變量。它將應(yīng)用以下規(guī)則。它將檢查環(huán)境變量的名稱是否與鍵匹配(如果設(shè)置了EnvPrefix
)。
SetEnvKeyReplacer
允許你使用strings.Replacer
對象在一定程度上重寫 Env 鍵。如果你希望在Get()
調(diào)用中使用-
或者其他什么符號,但是環(huán)境變量里使用_
分隔符,那么這個功能是非常有用的??梢栽?code>viper_test.go中找到它的使用示例。
或者,你可以使用帶有NewWithOptions
工廠函數(shù)的EnvKeyReplacer
。與SetEnvKeyReplacer
不同,它接受StringReplacer
接口,允許你編寫自定義字符串替換邏輯。
默認(rèn)情況下,空環(huán)境變量被認(rèn)為是未設(shè)置的,并將返回到下一個配置源。若要將空環(huán)境變量視為已設(shè)置,請使用AllowEmptyEnv
方法。
SetEnvPrefix("spf") // 將自動轉(zhuǎn)為大寫 BindEnv("id") os.Setenv("SPF_ID", "13") // 通常是在應(yīng)用程序之外完成的 id := Get("id") // 13
Viper 具有綁定到標(biāo)志的能力。具體來說,Viper支持Cobra庫中使用的Pflag
。
與BindEnv
類似,該值不是在調(diào)用綁定方法時設(shè)置的,而是在訪問該方法時設(shè)置的。這意味著你可以根據(jù)需要盡早進(jìn)行綁定,即使在init()
函數(shù)中也是如此。
對于單個標(biāo)志,BindPFlag()
方法提供此功能。
例如:
serverCmd.Flags().Int("port", 1138, "Port to run Application server on") viper.BindPFlag("port", serverCmd.Flags().Lookup("port"))
你還可以綁定一組現(xiàn)有的pflags (pflag.FlagSet):
舉個例子:
pflag.Int("flagname", 1234, "help message for flagname") pflag.Parse() viper.BindPFlags(pflag.CommandLine) i := viper.GetInt("flagname") // 從viper而不是從pflag檢索值
在 Viper 中使用 pflag 并不阻礙其他包中使用標(biāo)準(zhǔn)庫中的 flag 包。pflag 包可以通過導(dǎo)入這些 flags 來處理flag包定義的flags。這是通過調(diào)用pflag包提供的便利函數(shù)AddGoFlagSet()
來實(shí)現(xiàn)的。
例如:
package main import ( "flag" "github.com/spf13/pflag" ) func main() { // 使用標(biāo)準(zhǔn)庫 "flag" 包 flag.Int("flagname", 1234, "help message for flagname") pflag.CommandLine.AddGoFlagSet(flag.CommandLine) pflag.Parse() viper.BindPFlags(pflag.CommandLine) i := viper.GetInt("flagname") // 從 viper 檢索值 ... }
如果你不使用Pflag
,Viper 提供了兩個Go接口來綁定其他 flag 系統(tǒng)。
FlagValue
表示單個flag。這是一個關(guān)于如何實(shí)現(xiàn)這個接口的非常簡單的例子:
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" }
一旦你的 flag 實(shí)現(xiàn)了這個接口,你可以很方便地告訴Viper綁定它:
viper.BindFlagValue("my-flag-name", myFlag{})
FlagValueSet
代表一組 flags 。這是一個關(guān)于如何實(shí)現(xiàn)這個接口的非常簡單的例子:
type myFlagSet struct { flags []myFlag } func (f myFlagSet) VisitAll(fn func(FlagValue)) { for _, flag := range flags { fn(flag) } }
一旦你的flag set實(shí)現(xiàn)了這個接口,你就可以很方便地告訴Viper綁定它:
fSet := myFlagSet{ flags: []myFlag{myFlag{}, myFlag{}}, } viper.BindFlagValues("my-flags", fSet)
在Viper中啟用遠(yuǎn)程支持,需要在代碼中匿名導(dǎo)入viper/remote
這個包。
import _ "github.com/spf13/viper/remote"
Viper將讀取從Key/Value存儲(例如etcd或Consul)中的路徑檢索到的配置字符串(如JSON
、TOML
、YAML
、HCL
、envfile
和Java properties
格式)。這些值的優(yōu)先級高于默認(rèn)值,但是會被從磁盤、flag或環(huán)境變量檢索到的配置值覆蓋。(譯注:也就是說Viper加載配置值的優(yōu)先級為:磁盤上的配置文件>命令行標(biāo)志位>環(huán)境變量>遠(yuǎn)程Key/Value存儲>默認(rèn)值。)
Viper使用crypt從K/V存儲中檢索配置,這意味著如果你有正確的gpg密匙,你可以將配置值加密存儲并自動解密。加密是可選的。
你可以將遠(yuǎn)程配置與本地配置結(jié)合使用,也可以獨(dú)立使用。
crypt
有一個命令行助手,你可以使用它將配置放入K/V存儲中。crypt
默認(rèn)使用在http://127.0.0.1:4001的etcd。
$ go get github.com/bketelsen/crypt/bin/crypt $ crypt set -plaintext /config/hugo.json /Users/hugo/settings/config.json
確認(rèn)值已經(jīng)設(shè)置:
$ crypt get -plaintext /config/hugo.json
有關(guān)如何設(shè)置加密值或如何使用Consul的示例,請參見crypt
文檔。
viper.AddRemoteProvider("etcd", "http://127.0.0.1:4001","/config/hugo.json") viper.SetConfigType("json") // 因?yàn)樵谧止?jié)流中沒有文件擴(kuò)展名,所以這里需要設(shè)置下類型。支持的擴(kuò)展名有 "json", "toml", "yaml", "yml", "properties", "props", "prop", "env", "dotenv" err := viper.ReadRemoteConfig()
你需要 Consul Key/Value存儲中設(shè)置一個Key保存包含所需配置的JSON值。例如,創(chuàng)建一個keyMY_CONSUL_KEY
將下面的值存入Consul key/value 存儲:
{ "port": 8080, "hostname": "liwenzhou.com" } viper.AddRemoteProvider("consul", "localhost:8500", "MY_CONSUL_KEY") viper.SetConfigType("json") // 需要顯示設(shè)置成json err := viper.ReadRemoteConfig() fmt.Println(viper.Get("port")) // 8080 fmt.Println(viper.Get("hostname")) // liwenzhou.com
viper.AddRemoteProvider("firestore", "google-cloud-project-id", "collection/document") viper.SetConfigType("json") // 配置的格式: "json", "toml", "yaml", "yml" err := viper.ReadRemoteConfig()
當(dāng)然,你也可以使用SecureRemoteProvider
。
viper.AddSecureRemoteProvider("etcd","http://127.0.0.1:4001","/config/hugo.json","/etc/secrets/mykeyring.gpg") viper.SetConfigType("json") // 因?yàn)樵谧止?jié)流中沒有文件擴(kuò)展名,所以這里需要設(shè)置下類型。支持的擴(kuò)展名有 "json", "toml", "yaml", "yml", "properties", "props", "prop", "env", "dotenv" err := viper.ReadRemoteConfig()
// 或者你可以創(chuàng)建一個新的viper實(shí)例 var runtime_viper = viper.New() runtime_viper.AddRemoteProvider("etcd", "http://127.0.0.1:4001", "/config/hugo.yml") runtime_viper.SetConfigType("yaml") // 因?yàn)樵谧止?jié)流中沒有文件擴(kuò)展名,所以這里需要設(shè)置下類型。支持的擴(kuò)展名有 "json", "toml", "yaml", "yml", "properties", "props", "prop", "env", "dotenv" // 第一次從遠(yuǎn)程讀取配置 err := runtime_viper.ReadRemoteConfig() // 反序列化 runtime_viper.Unmarshal(&runtime_conf) // 開啟一個單獨(dú)的goroutine一直監(jiān)控遠(yuǎn)端的變更 go func(){ for { time.Sleep(time.Second * 5) // 每次請求后延遲一下 // 目前只測試了etcd支持 err := runtime_viper.WatchRemoteConfig() if err != nil { log.Errorf("unable to read remote config: %v", err) continue } // 將新配置反序列化到我們運(yùn)行時的配置結(jié)構(gòu)體中。你還可以借助channel實(shí)現(xiàn)一個通知系統(tǒng)更改的信號 runtime_viper.Unmarshal(&runtime_conf) } }()
在Viper中,有幾種方法可以根據(jù)值的類型獲取值。存在以下功能和方法:
Get(key string) : interface{}
GetBool(key string) : bool
GetFloat64(key string) : float64
GetInt(key string) : int
GetIntSlice(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
AllSettings() : map[string]interface{}
需要認(rèn)識到的一件重要事情是,每一個Get方法在找不到值的時候都會返回零值。為了檢查給定的鍵是否存在,提供了IsSet()
方法。
例如:
viper.GetString("logfile") // 不區(qū)分大小寫的設(shè)置和獲取 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") // (返回 "127.0.0.1")
這遵守上面建立的優(yōu)先規(guī)則;搜索路徑將遍歷其余配置注冊表,直到找到為止。(譯注:因?yàn)閂iper支持從多種配置來源,例如磁盤上的配置文件>命令行標(biāo)志位>環(huán)境變量>遠(yuǎn)程Key/Value存儲>默認(rèn)值,我們在查找一個配置的時候如果在當(dāng)前配置源中沒找到,就會繼續(xù)從后續(xù)的配置源查找,直到找到為止。)
例如,在給定此配置文件的情況下,datastore.metric.host和datastore.metric.port均已定義(并且可以被覆蓋)。如果另外在默認(rèn)值中定義了datastore.metric.protocol,Viper也會找到它。
然而,如果datastore.metric被直接賦值覆蓋(被flag,環(huán)境變量,set()
方法等等…),那么datastore.metric的所有子鍵都將變?yōu)槲炊x狀態(tài),它們被高優(yōu)先級配置級別“遮蔽”(shadowed)了。
最后,如果存在與分隔的鍵路徑匹配的鍵,則返回其值。例如:
{ "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") // 返回 "0.0.0.0"
從Viper中提取子樹。
例如,viper
實(shí)例現(xiàn)在代表了以下配置:
app: cache1: max-items: 100 item-size: 64 cache2: max-items: 200 item-size: 80
執(zhí)行后:
subv := viper.Sub("app.cache1")
subv
現(xiàn)在就代表:
max-items: 100 item-size: 64
假設(shè)我們現(xiàn)在有這么一個函數(shù):
func NewCache(cfg *Viper) *Cache {...}
它基于subv
格式的配置信息創(chuàng)建緩存?,F(xiàn)在,可以輕松地分別創(chuàng)建這兩個緩存,如下所示:
cfg1 := viper.Sub("app.cache1") cache1 := NewCache(cfg1) cfg2 := viper.Sub("app.cache2") cache2 := NewCache(cfg2)
你還可以選擇將所有或特定的值解析到結(jié)構(gòu)體、map等。
有兩種方法可以做到這一點(diǎn):
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 := viper.Unmarshal(&C) if err != nil { t.Fatalf("unable to decode into struct, %v", err) }
如果你想要解析那些鍵本身就包含.
(默認(rèn)的鍵分隔符)的配置,你需要修改分隔符:
v := viper.NewWithOptions(viper.KeyDelimiter("::")) v.SetDefault("chart::values", map[string]interface{}{ "ingress": map[string]interface{}{ "annotations": map[string]interface{}{ "traefik.frontend.rule.type": "PathPrefix", "traefik.ingress.kubernetes.io/ssl-redirect": "true", }, }, }) type config struct { Chart struct{ Values map[string]interface{} } } var C config v.Unmarshal(&C)
Viper還支持解析到嵌入的結(jié)構(gòu)體:
/* Example config: module: enabled: true token: 89h5f98hbwf987h5f98wenf89ehf */ type config struct { Module struct { Enabled bool moduleConfig `mapstructure:",squash"` } } // moduleConfig could be in a module specific package type moduleConfig struct { Token string } var C config err := viper.Unmarshal(&C) if err != nil { t.Fatalf("unable to decode into struct, %v", err) }
Viper在后臺使用github.com/mitchellh/mapstructure來解析值,其默認(rèn)情況下使用mapstructure
tag。
注意 當(dāng)我們需要將viper讀取的配置反序列到我們定義的結(jié)構(gòu)體變量中時,一定要使用mapstructure
tag哦!
你可能需要將viper中保存的所有設(shè)置序列化到一個字符串中,而不是將它們寫入到一個文件中。你可以將自己喜歡的格式的序列化器與AllSettings()
返回的配置一起使用。
import ( yaml "gopkg.in/yaml.v2" // ... ) func yamlStringSettings() string { c := viper.AllSettings() bs, err := yaml.Marshal(c) if err != nil { log.Fatalf("unable to marshal config to YAML: %v", err) } return string(bs) }
Viper是開箱即用的。你不需要配置或初始化即可開始使用Viper。由于大多數(shù)應(yīng)用程序都希望使用單個中央存儲庫管理它們的配置信息,所以viper包提供了這個功能。它類似于單例模式。
在上面的所有示例中,它們都以其單例風(fēng)格的方法演示了如何使用viper。
你還可以在應(yīng)用程序中創(chuàng)建許多不同的viper實(shí)例。每個都有自己獨(dú)特的一組配置和值。每個人都可以從不同的配置文件,key value存儲區(qū)等讀取數(shù)據(jù)。每個都可以從不同的配置文件、鍵值存儲等中讀取。viper包支持的所有功能都被鏡像為viper實(shí)例的方法。
例如:
x := viper.New() y := viper.New() x.SetDefault("ContentDir", "content") y.SetDefault("ContentDir", "foobar") //...
當(dāng)使用多個viper實(shí)例時,由用戶來管理不同的viper實(shí)例。
假設(shè)我們的項(xiàng)目現(xiàn)在有一個./conf/config.yaml
配置文件,內(nèi)容如下:
port: 8123 version: "v1.2.3"
接下來通過示例代碼演示兩種在項(xiàng)目中使用viper
管理項(xiàng)目配置信息的方式。
這里用一個demo演示如何在gin框架搭建的web項(xiàng)目中使用viper
,使用viper加載配置文件中的信息,并在代碼中直接使用viper.GetXXX()
方法獲取對應(yīng)的配置值。
package main import ( "fmt" "net/http" "github.com/gin-gonic/gin" "github.com/spf13/viper" ) func main() { viper.SetConfigFile("config.yaml") // 指定配置文件 viper.AddConfigPath("./conf/") // 指定查找配置文件的路徑 err := viper.ReadInConfig() // 讀取配置信息 if err != nil { // 讀取配置信息失敗 panic(fmt.Errorf("Fatal error config file: %s \n", err)) } // 監(jiān)控配置文件變化 viper.WatchConfig() r := gin.Default() // 訪問/version的返回值會隨配置文件的變化而變化 r.GET("/version", func(c *gin.Context) { c.String(http.StatusOK, viper.GetString("version")) }) if err := r.Run( fmt.Sprintf(":%d", viper.GetInt("port"))); err != nil { panic(err) } }
除了上面的用法外,我們還可以在項(xiàng)目中定義與配置文件對應(yīng)的結(jié)構(gòu)體,viper
加載完配置信息后使用結(jié)構(gòu)體變量保存配置信息。
package main import ( "fmt" "net/http" "github.com/fsnotify/fsnotify" "github.com/gin-gonic/gin" "github.com/spf13/viper" ) type Config struct { Port int `mapstructure:"port"` Version string `mapstructure:"version"` } var Conf = new(Config) func main() { viper.SetConfigFile("./conf/config.yaml") // 指定配置文件路徑 err := viper.ReadInConfig() // 讀取配置信息 if err != nil { // 讀取配置信息失敗 panic(fmt.Errorf("Fatal error config file: %s \n", err)) } // 將讀取的配置信息保存至全局變量Conf if err := viper.Unmarshal(Conf); err != nil { panic(fmt.Errorf("unmarshal conf failed, err:%s \n", err)) } // 監(jiān)控配置文件變化 viper.WatchConfig() // 注意?。?!配置文件發(fā)生變化后要同步到全局變量Conf viper.OnConfigChange(func(in fsnotify.Event) { fmt.Println("夭壽啦~配置文件被人修改啦...") if err := viper.Unmarshal(Conf); err != nil { panic(fmt.Errorf("unmarshal conf failed, err:%s \n", err)) } }) r := gin.Default() // 訪問/version的返回值會隨配置文件的變化而變化 r.GET("/version", func(c *gin.Context) { c.String(http.StatusOK, Conf.Version) }) if err := r.Run(fmt.Sprintf(":%d", Conf.Port)); err != nil { panic(err) } }
讀到這里,這篇“golang配置和使用Viper”文章已經(jīng)介紹完畢,想要掌握這篇文章的知識點(diǎn)還需要大家自己動手實(shí)踐使用過才能領(lǐng)會,如果想了解更多相關(guān)內(nèi)容的文章,歡迎關(guān)注億速云行業(yè)資訊頻道。
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報,并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。