欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

golang配置管理神器Viper使用教程

 更新時(shí)間:2022年04月16日 11:33:46   作者:Jeff的技術(shù)棧  
這篇文章主要為大家介紹了golang配置管理神器Viper使用教程,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步早日升職加薪

Viper

Viper是適用于Go應(yīng)用程序的完整配置解決方案。它被設(shè)計(jì)用于在應(yīng)用程序中工作,并且可以處理所有類型的配置需求和格式。

鑒于viper庫本身的README已經(jīng)寫得十分詳細(xì),這里就將其翻譯成中文,并在最后附上兩個(gè)項(xiàng)目中使用viper的示例代碼以供參考。

安裝

go get github.com/spf13/viper

什么是Viper?

Viper是適用于Go應(yīng)用程序(包括Twelve-Factor App)的完整配置解決方案。它被設(shè)計(jì)用于在應(yīng)用程序中工作,并且可以處理所有類型的配置需求和格式。它支持以下特性:

  • 設(shè)置默認(rèn)值
  • JSON、TOML、YAMLHCL、envfileJava properties格式的配置文件讀取配置信息
  • 實(shí)時(shí)監(jiān)控和重新讀取配置文件(可選)
  • 從環(huán)境變量中讀取
  • 從遠(yuǎn)程配置系統(tǒng)(etcd或Consul)讀取并監(jiān)控配置變化
  • 從命令行參數(shù)讀取配置
  • 從buffer讀取配置
  • 顯式配置值

為什么選擇Viper?

在構(gòu)建現(xiàn)代應(yīng)用程序時(shí),你無需擔(dān)心配置文件格式;你想要專注于構(gòu)建出色的軟件。Viper的出現(xiàn)就是為了在這方面幫助你的。

Viper能夠?yàn)槟銏?zhí)行下列操作:

  • 查找、加載和反序列化JSONTOML、YAMLHCL、INI、envfileJava properties格式的配置文件。
  • 提供一種機(jī)制為你的不同配置選項(xiàng)設(shè)置默認(rèn)值。
  • 提供一種機(jī)制來通過命令行參數(shù)覆蓋指定選項(xiàng)的值。
  • 提供別名系統(tǒng),以便在不破壞現(xiàn)有代碼的情況下輕松重命名參數(shù)。
  • 當(dāng)用戶提供了與默認(rèn)值相同的命令行或配置文件時(shí),可以很容易地分辨出它們之間的區(qū)別。

Viper會(huì)按照下面的優(yōu)先級(jí)。每個(gè)項(xiàng)目的優(yōu)先級(jí)都高于它下面的項(xiàng)目:

  • 顯示調(diào)用Set設(shè)置值
  • 命令行參數(shù)(flag)
  • 環(huán)境變量
  • 配置文件
  • key/value存儲(chǔ)
  • 默認(rèn)值

重要: 目前Viper配置的鍵(Key)是大小寫不敏感的。目前正在討論是否將這一選項(xiàng)設(shè)為可選。

把值存入Viper

建立默認(rèn)值

一個(gè)好的配置系統(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、HCLenvfileJava properties格式的配置文件。Viper可以搜索多個(gè)路徑,但目前單個(gè)Viper實(shí)例只支持單個(gè)配置文件。Viper不默認(rèn)任何配置搜索路徑,將默認(rèn)決策留給應(yīng)用程序。

下面是一個(gè)如何使用Viper搜索和讀取配置文件的示例。不需要任何特定的路徑,但是至少應(yīng)該提供一個(gè)配置文件預(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)用以添加多個(gè)搜索路徑
viper.AddConfigPath(".")               // 還可以在工作目錄中查找配置
err := viper.ReadInConfig() // 查找并讀取配置文件
if err != nil { // 處理讀取配置文件的錯(cuò)誤
	panic(fmt.Errorf("Fatal error config file: %s \n", err))
}

在加載配置文件出錯(cuò)時(shí),你可以像下面這樣處理找不到配置文件的特定情況:

if err := viper.ReadInConfig(); err != nil {
    if _, ok := err.(viper.ConfigFileNotFoundError); ok {
        // 配置文件未找到錯(cuò)誤;如果需要可以忽略
    } else {
        // 配置文件被找到,但產(chǎn)生了另外的錯(cuò)誤
    }
}

// 配置文件找到并成功解析

注意[自1.6起]: 你也可以有不帶擴(kuò)展名的文件,并以編程方式指定其格式。對(duì)于位于用戶$HOME目錄中的配置文件沒有任何擴(kuò)展名,如.bashrc。

這里補(bǔ)充兩個(gè)問題供讀者解答并自行驗(yàn)證

當(dāng)你使用如下方式讀取配置時(shí),viper會(huì)從./conf目錄下查找任何以config為文件名的配置文件,如果同時(shí)存在./conf/config.json./conf/config.yaml兩個(gè)配置文件的話,viper會(huì)從哪個(gè)配置文件加載配置呢?

viper.SetConfigName("config")
viper.AddConfigPath("./conf")

在上面兩個(gè)語句下搭配使用viper.SetConfigType("yaml")指定配置文件類型可以實(shí)現(xiàn)預(yù)期的效果嗎?

寫入配置文件

從配置文件中讀取配置文件是有用的,但是有時(shí)你想要存儲(chǔ)在運(yùn)行時(shí)所做的所有修改。為此,可以使用下面一組命令,每個(gè)命令都有自己的用途:

  • WriteConfig - 將當(dāng)前的viper配置寫入預(yù)定義的路徑并覆蓋(如果存在的話)。如果沒有預(yù)定義的路徑,則報(bào)錯(cuò)。
  • SafeWriteConfig - 將當(dāng)前的viper配置寫入預(yù)定義的路徑。如果沒有預(yù)定義的路徑,則報(bào)錯(cuò)。如果存在,將不會(huì)覆蓋當(dāng)前的配置文件。
  • WriteConfigAs - 將當(dāng)前的viper配置寫入給定的文件路徑。將覆蓋給定的文件(如果它存在的話)。
  • SafeWriteConfigAs - 將當(dāng)前的viper配置寫入給定的文件路徑。不會(huì)覆蓋給定的文件(如果它存在的話)。

根據(jù)經(jīng)驗(yàn),標(biāo)記為safe的所有方法都不會(huì)覆蓋任何文件,而是直接創(chuàng)建(如果不存在),而默認(rèn)行為是創(chuàng)建或截?cái)唷?/p>

一個(gè)小示例:

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)樵撆渲梦募懭脒^,所以會(huì)報(bào)錯(cuò)
viper.SafeWriteConfigAs("/path/to/my/.other_config")

監(jiān)控并重新讀取配置文件

Viper支持在運(yùn)行時(shí)實(shí)時(shí)讀取配置文件的功能。

需要重新啟動(dòng)服務(wù)器以使配置生效的日子已經(jīng)一去不復(fù)返了,viper驅(qū)動(dòng)的應(yīng)用程序可以在運(yùn)行時(shí)讀取配置文件的更新,而不會(huì)錯(cuò)過任何消息。

只需告訴viper實(shí)例watchConfig。可選地,你可以為Viper提供一個(gè)回調(diào)函數(shù),以便在每次發(fā)生更改時(shí)運(yùn)行。

確保在調(diào)用WatchConfig()之前添加了所有的配置路徑。

viper.WatchConfig()
viper.OnConfigChange(func(e fsnotify.Event) {
  // 配置文件發(fā)生變更之后會(huì)調(diào)用的回調(diào)函數(shù)
	fmt.Println("Config file changed:", e.Name)
})

從io.Reader讀取配置

Viper預(yù)先定義了許多配置源,如文件、環(huán)境變量、標(biāo)志和遠(yuǎn)程K/V存儲(chǔ),但你不受其約束。你還可以實(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") // 這里會(huì)得到 "steve"

覆蓋設(shè)置

這些可能來自命令行標(biāo)志,也可能來自你自己的應(yīng)用程序邏輯。

viper.Set("Verbose", true)
viper.Set("LogFile", LogFile)

注冊(cè)和使用別名

別名允許多個(gè)鍵引用單個(gè)值

viper.RegisterAlias("loud", "Verbose")  // 注冊(cè)別名(此處loud和Verbose建立了別名)
viper.Set("verbose", true) // 結(jié)果與下一行相同
viper.Set("loud", true)   // 結(jié)果與前一行相同
viper.GetBool("loud") // true
viper.GetBool("verbose") // true

使用環(huán)境變量

Viper完全支持環(huán)境變量。這使Twelve-Factor App開箱即用。有五種方法可以幫助與ENV協(xié)作:

AutomaticEnv()

BindEnv(string...) : error

SetEnvPrefix(string)

SetEnvKeyReplacer(string...) *strings.Replacer

AllowEmptyEnv(bool)

使用ENV變量時(shí),務(wù)必要意識(shí)到Viper將ENV變量視為區(qū)分大小寫。

Viper提供了一種機(jī)制來確保ENV變量是惟一的。通過使用SetEnvPrefix,你可以告訴Viper在讀取環(huán)境變量時(shí)使用前綴。BindEnvAutomaticEnv都將使用這個(gè)前綴。

BindEnv使用一個(gè)或兩個(gè)參數(shù)。第一個(gè)參數(shù)是鍵名稱,第二個(gè)是環(huán)境變量的名稱。環(huán)境變量的名稱區(qū)分大小寫。如果沒有提供ENV變量名,那么Viper將自動(dòng)假設(shè)ENV變量與以下格式匹配:前綴+ “_” +鍵名全部大寫。當(dāng)你顯式提供ENV變量名(第二個(gè)參數(shù))時(shí),它 不會(huì) 自動(dòng)添加前綴。例如,如果第二個(gè)參數(shù)是“id”,Viper將查找環(huán)境變量“ID”。

在使用ENV變量時(shí),需要注意的一件重要事情是,每次訪問該值時(shí)都將讀取它。Viper在調(diào)用BindEnv時(shí)不固定該值。

AutomaticEnv是一個(gè)強(qiáng)大的助手,尤其是與SetEnvPrefix結(jié)合使用時(shí)。調(diào)用時(shí),Viper會(huì)在發(fā)出viper.Get請(qǐng)求時(shí)隨時(shí)檢查環(huán)境變量。它將應(yīng)用以下規(guī)則。它將檢查環(huán)境變量的名稱是否與鍵匹配(如果設(shè)置了EnvPrefix)。

SetEnvKeyReplacer允許你使用strings.Replacer對(duì)象在一定程度上重寫 Env 鍵。如果你希望在Get()調(diào)用中使用-或者其他什么符號(hào),但是環(huán)境變量里使用_分隔符,那么這個(gè)功能是非常有用的??梢栽?code>viper_test.go中找到它的使用示例。

或者,你可以使用帶有NewWithOptions工廠函數(shù)的EnvKeyReplacer。與SetEnvKeyReplacer不同,它接受StringReplacer接口,允許你編寫自定義字符串替換邏輯。

默認(rèn)情況下,空環(huán)境變量被認(rèn)為是未設(shè)置的,并將返回到下一個(gè)配置源。若要將空環(huán)境變量視為已設(shè)置,請(qǐng)使用AllowEmptyEnv方法。

Env 示例:

SetEnvPrefix("spf") // 將自動(dòng)轉(zhuǎn)為大寫
BindEnv("id")
os.Setenv("SPF_ID", "13") // 通常是在應(yīng)用程序之外完成的
id := Get("id") // 13

使用Flags

Viper 具有綁定到標(biāo)志的能力。具體來說,Viper支持Cobra庫中使用的Pflag。

BindEnv類似,該值不是在調(diào)用綁定方法時(shí)設(shè)置的,而是在訪問該方法時(shí)設(shè)置的。這意味著你可以根據(jù)需要盡早進(jìn)行綁定,即使在init()函數(shù)中也是如此。

對(duì)于單個(gè)標(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):

舉個(gè)例子:

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 檢索值
	...
}

flag接口

如果你不使用Pflag,Viper 提供了兩個(gè)Go接口來綁定其他 flag 系統(tǒng)。

FlagValue表示單個(gè)flag。這是一個(gè)關(guān)于如何實(shí)現(xiàn)這個(gè)接口的非常簡(jiǎ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)了這個(gè)接口,你可以很方便地告訴Viper綁定它:

viper.BindFlagValue("my-flag-name", myFlag{})

FlagValueSet代表一組 flags 。這是一個(gè)關(guān)于如何實(shí)現(xiàn)這個(gè)接口的非常簡(jiǎn)單的例子:

type myFlagSet struct {
	flags []myFlag
}
func (f myFlagSet) VisitAll(fn func(FlagValue)) {
	for _, flag := range flags {
		fn(flag)
	}
}

一旦你的flag set實(shí)現(xiàn)了這個(gè)接口,你就可以很方便地告訴Viper綁定它:

fSet := myFlagSet{
	flags: []myFlag{myFlag{}, myFlag{}},
}
viper.BindFlagValues("my-flags", fSet)

遠(yuǎn)程Key/Value存儲(chǔ)支持

在Viper中啟用遠(yuǎn)程支持,需要在代碼中匿名導(dǎo)入viper/remote這個(gè)包。

import _ "github.com/spf13/viper/remote"

Viper將讀取從Key/Value存儲(chǔ)(例如etcd或Consul)中的路徑檢索到的配置字符串(如JSON、TOMLYAML、HCL、envfileJava properties格式)。這些值的優(yōu)先級(jí)高于默認(rèn)值,但是會(huì)被從磁盤、flag或環(huán)境變量檢索到的配置值覆蓋。(譯注:也就是說Viper加載配置值的優(yōu)先級(jí)為:磁盤上的配置文件>命令行標(biāo)志位>環(huán)境變量>遠(yuǎn)程Key/Value存儲(chǔ)>默認(rèn)值。)

Viper使用crypt從K/V存儲(chǔ)中檢索配置,這意味著如果你有正確的gpg密匙,你可以將配置值加密存儲(chǔ)并自動(dòng)解密。加密是可選的。

你可以將遠(yuǎn)程配置與本地配置結(jié)合使用,也可以獨(dú)立使用。

crypt有一個(gè)命令行助手,你可以使用它將配置放入K/V存儲(chǔ)中。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的示例,請(qǐng)參見crypt文檔。

遠(yuǎn)程Key/Value存儲(chǔ)示例-未加密

etcd

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

你需要 Consul Key/Value存儲(chǔ)中設(shè)置一個(gè)Key保存包含所需配置的JSON值。例如,創(chuàng)建一個(gè)keyMY_CONSUL_KEY將下面的值存入Consul key/value 存儲(chǔ):

{
    "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

Firestore

viper.AddRemoteProvider("firestore", "google-cloud-project-id", "collection/document")
viper.SetConfigType("json") // 配置的格式: "json", "toml", "yaml", "yml"
err := viper.ReadRemoteConfig()

當(dāng)然,你也可以使用SecureRemoteProvider。

遠(yuǎn)程Key/Value存儲(chǔ)示例-加密

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()

監(jiān)控etcd中的更改-未加密

// 或者你可以創(chuàng)建一個(gè)新的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)
// 開啟一個(gè)單獨(dú)的goroutine一直監(jiān)控遠(yuǎn)端的變更
go func(){
	for {
	    time.Sleep(time.Second * 5) // 每次請(qǐng)求后延遲一下
	    // 目前只測(cè)試了etcd支持
	    err := runtime_viper.WatchRemoteConfig()
	    if err != nil {
	        log.Errorf("unable to read remote config: %v", err)
	        continue
	    }
	    // 將新配置反序列化到我們運(yùn)行時(shí)的配置結(jié)構(gòu)體中。你還可以借助channel實(shí)現(xiàn)一個(gè)通知系統(tǒng)更改的信號(hào)
	    runtime_viper.Unmarshal(&runtime_conf)
	}
}()

從Viper獲取值

在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)識(shí)到的一件重要事情是,每一個(gè)Get方法在找不到值的時(shí)候都會(huì)返回零值。為了檢查給定的鍵是否存在,提供了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ī)則;搜索路徑將遍歷其余配置注冊(cè)表,直到找到為止。(譯注:因?yàn)閂iper支持從多種配置來源,例如磁盤上的配置文件>命令行標(biāo)志位>環(huán)境變量>遠(yuǎn)程Key/Value存儲(chǔ)>默認(rèn)值,我們?cè)诓檎乙粋€(gè)配置的時(shí)候如果在當(dāng)前配置源中沒找到,就會(huì)繼續(xù)從后續(xù)的配置源查找,直到找到為止。)

例如,在給定此配置文件的情況下,datastore.metric.host和datastore.metric.port均已定義(并且可以被覆蓋)。如果另外在默認(rèn)值中定義了datastore.metric.protocol,Viper也會(huì)找到它。

然而,如果datastore.metric被直接賦值覆蓋(被flag,環(huán)境變量,set()方法等等…),那么datastore.metric的所有子鍵都將變?yōu)槲炊x狀態(tài),它們被高優(yōu)先級(jí)配置級(jí)別“遮蔽”(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)在有這么一個(gè)函數(shù):

func NewCache(cfg *Viper) *Cache {...}

它基于subv格式的配置信息創(chuàng)建緩存?,F(xiàn)在,可以輕松地分別創(chuàng)建這兩個(gè)緩存,如下所示:

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

舉個(gè)例子:

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: 89h4f98hbwf987h4f98wenf89ehf
*/
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在后臺(tái)使用github.com/mitchellh/mapstructure來解析值,其默認(rèn)情況下使用mapstructuretag。

注意 當(dāng)我們需要將viper讀取的配置反序列到我們定義的結(jié)構(gòu)體變量中時(shí),一定要使用mapstructuretag哦!

序列化成字符串

你可能需要將viper中保存的所有設(shè)置序列化到一個(gè)字符串中,而不是將它們寫入到一個(gè)文件中。你可以將自己喜歡的格式的序列化器與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)
}

使用單個(gè)還是多個(gè)Viper實(shí)例?

Viper是開箱即用的。你不需要配置或初始化即可開始使用Viper。由于大多數(shù)應(yīng)用程序都希望使用單個(gè)中央存儲(chǔ)庫管理它們的配置信息,所以viper包提供了這個(gè)功能。它類似于單例模式。

在上面的所有示例中,它們都以其單例風(fēng)格的方法演示了如何使用viper。

使用多個(gè)viper實(shí)例

你還可以在應(yīng)用程序中創(chuàng)建許多不同的viper實(shí)例。每個(gè)都有自己獨(dú)特的一組配置和值。每個(gè)人都可以從不同的配置文件,key value存儲(chǔ)區(qū)等讀取數(shù)據(jù)。每個(gè)都可以從不同的配置文件、鍵值存儲(chǔ)等中讀取。viper包支持的所有功能都被鏡像為viper實(shí)例的方法。

例如:

x := viper.New()
y := viper.New()
x.SetDefault("ContentDir", "content")
y.SetDefault("ContentDir", "foobar")

//...

當(dāng)使用多個(gè)viper實(shí)例時(shí),由用戶來管理不同的viper實(shí)例。

使用Viper示例

假設(shè)我們的項(xiàng)目現(xiàn)在有一個(gè)./conf/config.yaml配置文件,內(nèi)容如下:

port: 8123
version: "v1.2.3"

接下來通過示例代碼演示兩種在項(xiàng)目中使用viper管理項(xiàng)目配置信息的方式。

直接使用viper管理配置

這里用一個(gè)demo演示如何在gin框架搭建的web項(xiàng)目中使用viper,使用viper加載配置文件中的信息,并在代碼中直接使用viper.GetXXX()方法獲取對(duì)應(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的返回值會(huì)隨配置文件的變化而變化
	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)
	}
}

使用結(jié)構(gòu)體變量保存配置信息

除了上面的用法外,我們還可以在項(xiàng)目中定義與配置文件對(duì)應(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的返回值會(huì)隨配置文件的變化而變化
	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使用教程的詳細(xì)內(nèi)容,更多關(guān)于golang配置管理Viper教程的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • Go語言獲取數(shù)組長度的方法

    Go語言獲取數(shù)組長度的方法

    這篇文章主要介紹了Go語言獲取數(shù)組長度的方法,實(shí)例分析了len函數(shù)的使用技巧,具有一定參考借鑒價(jià)值,需要的朋友可以參考下
    2015-02-02
  • 詳解Go語言中Goroutine退出機(jī)制的原理及使用

    詳解Go語言中Goroutine退出機(jī)制的原理及使用

    goroutine是Go語言提供的語言級(jí)別的輕量級(jí)線程,在我們需要使用并發(fā)時(shí),我們只需要通過?go?關(guān)鍵字來開啟?goroutine?即可。本文就來詳細(xì)講講Goroutine退出機(jī)制的原理及使用,感興趣的可以了解一下
    2022-07-07
  • 利用Go語言實(shí)現(xiàn)簡(jiǎn)單Ping過程的方法

    利用Go語言實(shí)現(xiàn)簡(jiǎn)單Ping過程的方法

    相信利用各種語言實(shí)現(xiàn)Ping已經(jīng)是大家喜聞樂見的事情了,網(wǎng)絡(luò)上利用Golang實(shí)現(xiàn)Ping已經(jīng)有比較詳細(xì)的代碼示例,但大多是僅僅是實(shí)現(xiàn)了Request過程,而對(duì)Response的回顯內(nèi)容并沒有做接收。而Ping程序不僅僅是發(fā)送一個(gè)ICMP,更重要的是如何接收并進(jìn)行統(tǒng)計(jì)。
    2016-09-09
  • golang中命令行庫cobra的使用方法示例

    golang中命令行庫cobra的使用方法示例

    這篇文章主要給大家介紹了關(guān)于golang中命令行庫cobra的使用方法,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2018-08-08
  • Go實(shí)現(xiàn)SMTP郵件發(fā)送訂閱功能(包含163郵箱、163企業(yè)郵箱、谷歌gmail郵箱)

    Go實(shí)現(xiàn)SMTP郵件發(fā)送訂閱功能(包含163郵箱、163企業(yè)郵箱、谷歌gmail郵箱)

    這篇文章給大家介紹了Go實(shí)現(xiàn)SMTP郵件發(fā)送訂閱功能(包含163郵箱、163企業(yè)郵箱、谷歌gmail郵箱),需求很簡(jiǎn)單,就是用戶輸入自己的郵箱后,使用官方郵箱給用戶發(fā)送替郵件模版,文中有詳細(xì)的代碼示例供大家參考,需要的朋友可以參考下
    2023-10-10
  • Go?語言開發(fā)環(huán)境搭建過程

    Go?語言開發(fā)環(huán)境搭建過程

    這篇文章主要介紹了Go?語言開發(fā)環(huán)境搭建過程,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友參考下吧
    2024-03-03
  • Windows+Linux系統(tǒng)下Go語言環(huán)境安裝配置過程

    Windows+Linux系統(tǒng)下Go語言環(huán)境安裝配置過程

    Go 語言被設(shè)計(jì)成一門應(yīng)用于搭載 Web 服務(wù)器,存儲(chǔ)集群或類似用途的巨型中央服務(wù)器的系統(tǒng)編程語言。這篇文章主要介紹了Windows+Linux系統(tǒng)下Go語言環(huán)境搭建配置過程,針對(duì)每種系統(tǒng)給大家講解的非常詳細(xì),需要的朋友可以參考下
    2021-06-06
  • Golang極簡(jiǎn)入門教程(一):基本概念

    Golang極簡(jiǎn)入門教程(一):基本概念

    這篇文章主要介紹了Golang極簡(jiǎn)入門教程(一):基本概念,本文講解了Golang的基本知識(shí)、基礎(chǔ)語法、相關(guān)術(shù)語等,需要的朋友可以參考下
    2014-10-10
  • GO 反射對(duì)性能的影響分析

    GO 反射對(duì)性能的影響分析

    這篇文章主要為大家介紹了GO 反射對(duì)性能的影響分析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-01-01
  • Go的gin參數(shù)校驗(yàn)中的validator庫詳解

    Go的gin參數(shù)校驗(yàn)中的validator庫詳解

    這篇文章主要介紹了Go的gin參數(shù)校驗(yàn)之validator庫,使用 validator 以后,只需要在定義結(jié)構(gòu)體時(shí)使用 binding 或 validate tag標(biāo)識(shí)相關(guān)校驗(yàn)規(guī)則,就可以進(jìn)行參數(shù)校驗(yàn)了,而不用自己?jiǎn)为?dú)去寫常見的校驗(yàn)規(guī)則,需要的朋友可以參考下
    2023-08-08

最新評(píng)論