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

Golang配置解析神器go?viper使用詳解

 更新時(shí)間:2022年05月19日 15:18:05   作者:程序員讀書  
viper是一個(gè)很完善的Go項(xiàng)目配置解決方案,很多著名的開源項(xiàng)目都在使用,比如Hugo,Docker都使用了該庫(kù),使用viper可以讓我們專注于自己的項(xiàng)目代碼,而不用自己寫那些配置解析代碼,本文給大家介紹Golang配置解析神器go?viper使用,感興趣的朋友一起看看吧

前言

對(duì)于現(xiàn)代應(yīng)用程序,尤其大中型的項(xiàng)目來說,在程序啟動(dòng)和運(yùn)行時(shí),往往需要傳入很多參數(shù)來控制程序的行為,這些參數(shù)可以通過以下幾種方式傳遞給程序:

  • 命令行參數(shù)
  • 環(huán)境變量
  • 配置文件

顯然,對(duì)于Go項(xiàng)目而言,單個(gè)去讀取命令行、環(huán)境變量、配置文件并不難,但一個(gè)個(gè)讀取卻是很麻煩,有沒有一個(gè)第三方庫(kù)可以幫我們一次性讀取上面幾種數(shù)據(jù)源的配置呢?

有的,這里推薦使用viper庫(kù),viper支持讀取不同數(shù)據(jù)源和不同格式的配置文件,是Go項(xiàng)目讀取配置的神器,今天跟著這篇文章,一起來探究一下吧!~

viper簡(jiǎn)介

viper是一個(gè)很完善的Go項(xiàng)目配置解決方案,很多著名的開源項(xiàng)目都在使用,比如Hugo,Docker都使用了該庫(kù),使用viper可以讓我們專注于自己的項(xiàng)目代碼,而不用自己寫那些配置解析代碼。

功能

  • 支持配置key默認(rèn)值設(shè)置
  • 支持讀取JSON,TOML,YAML,HCL,envfile和java properties等多種不同類型配置文件
  • 可以監(jiān)聽配置文件的變化,并重新加載配置文件
  • 讀取系統(tǒng)環(huán)境變量的值
  • 讀取存儲(chǔ)在遠(yuǎn)程配置中心的配置數(shù)據(jù),如ectd,Consul,firestore等系統(tǒng),并監(jiān)聽配置的變化
  • 從命令行讀取配置
  • 從buffer讀取配置
  • 可以顯示設(shè)置配置的值

viper配置優(yōu)先級(jí)

viper支持從多個(gè)數(shù)據(jù)源讀取配置值,因此當(dāng)同一個(gè)配置key在多個(gè)數(shù)據(jù)源有值時(shí),viper讀取的優(yōu)先級(jí)如下:

  • 顯示使用Set函數(shù)設(shè)置值
  • flag:命令行參數(shù)
  • env:環(huán)境變量
  • config:配置文件
  • key/value store:key/value存儲(chǔ)系統(tǒng),如(etcd)
  • default:默認(rèn)值

優(yōu)先級(jí)示意圖

安裝viper

viper的安裝非常簡(jiǎn)單,如同其他Go第三方包一樣,只需要go get命令即可安裝,如:

安裝

go get github.com/spf13/viper

使用

import "github.com/spf13/viper"

支持哪些文件格式

我們一直在說,viper支持多種不同格式的配置文件,到底是哪些格式呢?如下:

  • json
  • toml
  • yaml
  • yml
  • properties
  • props
  • prop
  • hcl
  • tfvars
  • dotenv
  • env
  • ini

key大小寫問題

viper的配置的key值是不區(qū)分大小寫,如:

# 小寫的key
viper.set("test","this is a test value")
# 大寫的key,也可以讀到值
fmt.Println(viper.get("TEST"))//輸出"this is a test value"

使用指南

在了解了viper是什么之后,下面我們來看看要怎么使用viper去幫我們讀取配置。

如何訪問viper的功能

使用包名viper,如:

viper.SetDefault("key1","value")//調(diào)用包級(jí)別下的函數(shù)

使用viper.New()函數(shù)創(chuàng)建一個(gè)Viper Struct,如:

viper := viper.New()
viper.SetDefault("key2","value2")

其實(shí),這就是Go包的編程慣例,對(duì)實(shí)現(xiàn)功能對(duì)象再進(jìn)行封裝,并通過包名來調(diào)用。

因此,下面所有示例中調(diào)用函數(shù)使用viper,可以是指包名viper,或者通過viper.New()返回的對(duì)象。

配置默認(rèn)值

viper.SetDefault("key1","value1")
viper.SetDefault("key2","value2")

讀取配置文件

直接指定文件路徑

viper.SetConfigFile("./config.yaml")
viper.ReadInConfig()
fmt.Println(viper.Get("test"))

多路徑查找

viper.SetConfigName("config")     // 配置文件名,不需要后綴名
viper.SetConfigType("yml")            // 配置文件格式
viper.AddConfigPath("/etc/appname/")  // 查找配置文件的路徑
viper.AddConfigPath("$HOME/.appname") // 查找配置文件的路徑
viper.AddConfigPath(".")              // 查找配置文件的路徑
err := viper.ReadInConfig()           // 查找并讀取配置文件
if err != nil {                       // 處理錯(cuò)誤
    panic(fmt.Errorf("Fatal error config file: %w \n", err))
}

讀取配置文件時(shí),可能會(huì)出現(xiàn)錯(cuò)誤,如果我們想判斷是否是因?yàn)檎也坏轿募鴪?bào)錯(cuò)的,可以判斷err是否為ConfigFileNotFoundError。

if err := viper.ReadInConfig(); err != nil {
	if _, ok := err.(viper.ConfigFileNotFoundError); ok {
		
	} else {
		
	}
}

寫配置文件

除了讀取配置文件外,viper也支持將配置值寫入配置文件,viper提供了四個(gè)函數(shù),用于將配置寫回文件。

WriteConfig

WriteConfig函數(shù)會(huì)將配置寫入預(yù)先設(shè)置好路徑的配置文件中,如果配置文件存在,則覆蓋,如果沒有,則創(chuàng)建。

SafeWriteConfig

SafeWriterConfig與WriteConfig函數(shù)唯一的不同是如果配置文件存在,則會(huì)返回一個(gè)錯(cuò)誤。

WriteConfigAs

WriteConfigAs與WriteConfig函數(shù)的不同是需要傳入配置文件保存路徑,viper會(huì)根據(jù)文件后綴判斷寫入格式。

SafeWriteConfigAs

SafeWriteConfigAs與WriteConfigAs的唯一不同是如果配置文件存在,則返回一個(gè)錯(cuò)誤。

監(jiān)聽配置文件

viper支持監(jiān)聽配置文件,并會(huì)在配置文件發(fā)生變化,重新讀取配置文件和回調(diào)函數(shù),這樣可以避免每次配置變化時(shí),都需要重啟啟動(dòng)應(yīng)用的麻煩。

viper.OnConfigChange(func(e fsnotify.Event) {
	fmt.Println("Config file changed:", e.Name)
})
viper.WatchConfig()

從io.Reader讀取配置

除了支持從配置文件讀取配置外,viper也支持從實(shí)現(xiàn)了io.Reader接口的實(shí)例中讀取配置(其實(shí)配置文件也實(shí)現(xiàn)了io.Reader),如:

viper.SetConfigType("json") //設(shè)置格式

var yamlExample = []byte(`
{
	"name":"小明"
}
`)
viper.ReadConfig(bytes.NewBuffer(yamlExample))
fmt.Println(viper.Get("name")) //輸出“小明”

顯示設(shè)置配置項(xiàng)

也可以使用Set函數(shù)顯示為某個(gè)key設(shè)置值,這種方式的優(yōu)先級(jí)最高,會(huì)覆蓋該key在其他地方的值,如:

viper.SetConfigType("json") //設(shè)置格式
var yamlExample = []byte(`
{
	"name":"小明"
}
`)
viper.ReadConfig(bytes.NewBuffer(yamlExample))
fmt.Println(viper.Get("name")) //輸出:小明
viper.Set("name","test")
fmt.Println(viper.Get("name"))//輸出:test

注冊(cè)和使用別名

為某個(gè)配置key設(shè)置別名,這樣可以方便我們?cè)诓桓淖僰ey的情況下,使用別的名稱訪問該配置。

viper.Set("name", "test")
//為name設(shè)置一個(gè)username的別名
viper.RegisterAlias("username", "name")
//通過username可以讀取到name的值
fmt.Println(viper.Get("username"))
//修改name的配置值,username的值也發(fā)生改變
viper.Set("name", "小明")
fmt.Println(viper.Get("username"))
//修改username的值,name的值也發(fā)生改變
viper.Set("username", "測(cè)試")
fmt.Println(viper.Get("name"))

讀取環(huán)境變量

對(duì)于讀取操作系統(tǒng)環(huán)境變量,viper提供了下面五個(gè)函數(shù):

  • AutomaticEnv()
  • BindEnv(string...) : error
  • SetEnvPrefix(string)
  • SetEnvKeyReplacer(string...) *strings.Replacer
  • AllowEmptyEnv(bool)

要讓viper讀取環(huán)境變量,有兩種方式:

  • 調(diào)用AutomaticEnv函數(shù),開啟環(huán)境變量讀取
fmt.Println(viper.Get("path"))
//開始讀取環(huán)境變量,如果沒有調(diào)用這個(gè)函數(shù),則下面無法讀取到path的值
viper.AutomaticEnv()
//會(huì)從環(huán)境變量讀取到該值,注意不用區(qū)分大小寫
fmt.Println(viper.Get("path"))
  • 使用BindEnv綁定某個(gè)環(huán)境變量
//將p綁定到環(huán)境變量PATH,注意這里第二個(gè)參數(shù)是環(huán)境變量,這里是區(qū)分大小寫的
viper.BindEnv("p", "PATH")
//錯(cuò)誤綁定方式,path為小寫,無法讀取到PATH的值
//viper.BindEnv("p","path")
fmt.Println(viper.Get("p"))//通過p可以讀取PATH的值

使用函數(shù)SetEnvPrefix可以為所有環(huán)境變量設(shè)置一個(gè)前綴,這個(gè)前綴會(huì)影響AutomaticEnvBindEnv函數(shù)

os.Setenv("TEST_PATH","test")
viper.SetEnvPrefix("test")
viper.AutomaticEnv()
//無法讀取path的值,因?yàn)榇藭r(shí)加上前綴,viper會(huì)去讀取TEST_PATH這個(gè)環(huán)境變量的值
fmt.Println(viper.Get("path"))//輸出:nil
fmt.Println(viper.Get("test_path"))//輸出:test

環(huán)境變量大多是使用下劃號(hào)(_)作為分隔符的,如果想替換,可以使用SetEnvKeyReplacer函數(shù),如:

//設(shè)置一個(gè)環(huán)境變量
os.Setenv("USER_NAME", "test")
//將下線號(hào)替換為-和.
viper.SetEnvKeyReplacer(strings.NewReplacer("-", "_", ".", "_"))
//讀取環(huán)境變量
viper.AutomaticEnv()
fmt.Println(viper.Get("user.name"))//通過.訪問
fmt.Println(viper.Get("user-name"))//通過-訪問
fmt.Println(viper.Get("user_name"))//原來的下劃線也可以訪問

默認(rèn)的情況下,如果讀取到的環(huán)境變量值為空(注意,不是環(huán)境變量不存在,而是其值為空),會(huì)繼續(xù)向優(yōu)化級(jí)更低數(shù)據(jù)源去查找配置,如果想阻止這一行為,讓空的環(huán)境變量值有效,則可以調(diào)用AllowEmptyEnv函數(shù):

viper.SetDefault("username", "admin")
viper.SetDefault("password", "123456")
//默認(rèn)是AllowEmptyEnv(false),這里設(shè)置為true
viper.AllowEmptyEnv(true)
viper.BindEnv("username")
os.Setenv("USERNAME", "")
fmt.Println(viper.Get("username"))//輸出為空,因?yàn)榄h(huán)境變量USERNAME空
fmt.Println(viper.Get("password"))//輸出:123456

與命令行參數(shù)搭配使用

viper可以和解析命令行庫(kù)相關(guān)flag庫(kù)一起工作,從命令行讀取配置,其內(nèi)置了對(duì)pflag庫(kù)的支持,同時(shí)也留有接口讓我們可以支持?jǐn)U展其他的flag庫(kù)

pflag

pflag.Int("port", 8080, "server http port")
pflag.Parse()
viper.BindPFlags(pflag.CommandLine)
fmt.Println(viper.GetInt("port"))//輸出8080

擴(kuò)展其他flag

如果我們沒有使用pflag庫(kù),但又想讓viper幫我們讀取命令行參數(shù)呢?

package main
import (
	"flag"
	"fmt"
	"github.com/spf13/viper"
)
type myFlag struct {
	f *flag.Flag
}
func (m *myFlag) HasChanged() bool {
	return false
}
func (m *myFlag) Name() string {
	return m.f.Name
}
func (m *myFlag) ValueString() string {
	return m.f.Value.String()
}
func (m *myFlag) ValueType() string {
	return "string"
}
func NewMyFlag(f *flag.Flag) *myFlag {
	return &myFlag{f: f}
}
func main() {
	flag.String("username", "defaultValue", "usage")
	m := NewMyFlag(flag.CommandLine.Lookup("username"))
	viper.BindFlagValue("myFlagValue", m)
	flag.Parse()
	fmt.Println(viper.Get("myFlagValue"))
}

遠(yuǎn)程key/value存儲(chǔ)支持

viper支持存儲(chǔ)或者讀取遠(yuǎn)程配置存儲(chǔ)中心和NoSQL(目前支持etcd,Consul,firestore)的配置,并可以實(shí)時(shí)監(jiān)聽配置的變化,不過需要在代碼中引入下面的包:

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

現(xiàn)在遠(yuǎn)程配置中心存儲(chǔ)著以下JSON的配置信息

{
	"hostname":"localhost",
	"port":"8080"
}

那么我們可以通過下面的方面連接到系統(tǒng),并讀取配置,也可以單獨(dú)開啟一個(gè)Goroutine實(shí)時(shí)監(jiān)聽配置的變化。

連接Consul

viper.AddRemoteProvider("consul", "localhost:8500", "MY_CONSUL_KEY")

連接etcd

viper.AddRemoteProvider("etcd", "http://127.0.0.1:4001","/config/hugo.json")

連接firestore

viper.AddRemoteProvider("firestore", "google-cloud-project-id", "collection/document")

連接上配置中心后,就可以像讀取配置文件讀取其中的配置了,如:

viper.SetConfigType("json")
err := viper.ReadRemoteConfig()
fmt.Println(viper.Get("port")) // 輸出:8080
fmt.Println(viper.Get("hostname")) // 輸出:localhost

監(jiān)聽配置系統(tǒng),如:

go func(){
	for {
		time.Sleep(time.Second * 5) 
		err := viper.WatchRemoteConfig()
		if err != nil {
			log.Errorf("unable to read remote config: %v", err)
			continue
		}
	}
}()

另外,viper連接etcd,Consul,firestore進(jìn)行配置傳輸時(shí),也支持加解密,這樣可以更加安全,如果想要實(shí)現(xiàn)加密傳輸可以把AddRemoteProvider函數(shù)換為SecureRemoteProvider。

viper.SecureRemoteProvider("etcd", "http://127.0.0.1:4001","/config/hugo.json","/etc/secrets/mykeyring.gpg")

訪問配置

viper可以幫我們讀取各個(gè)地方的配置,那讀到配置之后,要怎么用呢?

直接訪問

{
  "mysql":{
    "db":"test"
  },
  "host":{
	  "address":"localhost"
	  "ports":[
		  "8080",
		  "8081"
	  ]
  }
}

對(duì)于多層級(jí)配置key,可以用逗號(hào)隔號(hào),如:

viper.Get("mysql.db")
viper.GetString("user.db")
viper.Get("host.address")//輸出:localhost

數(shù)組,可以用序列號(hào)訪問,如:

viper.Get("host.posts.1")//輸出: 8081

也可以使用sub函數(shù)解析某個(gè)key的下級(jí)配置,如:

hostViper := viper.Sub("host")
fmt.Println(hostViper.Get("address"))
fmt.Println(hostViper.Get("posts.1"))

viper提供了以下訪問配置的的函數(shù):

  • 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

序列化

讀取了配置之后,除了使用上面列舉出來的函數(shù)訪問配置,還可以將配置序列化到struct或map之中,這樣可以更加方便訪問配置。

示例代碼

配置文件:config.yaml

host: localhost
username: test
password: test
port: 3306
charset: utf8
dbName: test

解析代碼:

type MySQL struct {
	Host     string
	DbName   string
	Port     string
	Username string
	Password string
	Charset  string
}
func main() {

	viper.SetConfigName("config")
	viper.SetConfigType("yaml")
	viper.AddConfigPath(".")
	viper.ReadInConfig()
	var mysql MySQL
	viper.Unmarshal(&mysql)//序列化

	fmt.Println(mysql.Username)
	fmt.Println(mysql.Host)
}

對(duì)于多層級(jí)的配置,viper也支持序列化到一個(gè)復(fù)雜的struct中,如:

我們將config.yaml改為如下結(jié)構(gòu):

mysql: 
  host: localhost
  username: test
  password: test
  port: 3306
  charset: utf8
  dbName: test
redis: 
  host: localhost
  port: 6379

示例程序

type MySQL struct {
	Host     string
	DbName   string
	Username string
	Password string
	Charset  string
}
type Redis struct {
	Host string
	Port string
}
type Config struct {
	MySQL MySQL
	Redis Redis
}
func main() {
	viper.SetConfigName("config")
	viper.SetConfigType("yaml")
	viper.AddConfigPath(".")
	viper.ReadInConfig()
	var config Config
	viper.Unmarshal(&config)
	fmt.Println(config.MySQL.Username)
	fmt.Println(config.Redis.Host)
}

判斷配置key是否存在

if viper.IsSet("user"){
	fmt.Println("key user is not exists")
}

打印所有配置

m := viper.AllSettings()
fmt.Println(m)

小結(jié)

到此這篇關(guān)于Golang配置解析神器go viper使用詳解的文章就介紹到這了,更多相關(guān)go viper使用內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • golang使用viper加載配置文件實(shí)現(xiàn)自動(dòng)反序列化到結(jié)構(gòu)

    golang使用viper加載配置文件實(shí)現(xiàn)自動(dòng)反序列化到結(jié)構(gòu)

    這篇文章主要為大家介紹了golang使用viper加載配置文件實(shí)現(xiàn)自動(dòng)反序列化到結(jié)構(gòu)示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-08-08
  • Golang易錯(cuò)知識(shí)點(diǎn)匯總

    Golang易錯(cuò)知識(shí)點(diǎn)匯總

    這篇文章匯總了在開發(fā)和刷面試題過程中遇到的Golang容易搞錯(cuò)的知識(shí)點(diǎn),關(guān)鍵部分也都為大家寫了代碼示例,感興趣的小伙伴可以了解一下
    2022-09-09
  • Go雪花算法的作用領(lǐng)域及實(shí)現(xiàn)方法示例

    Go雪花算法的作用領(lǐng)域及實(shí)現(xiàn)方法示例

    這篇文章主要為大家介紹了Go雪花算法的作用領(lǐng)域及實(shí)現(xiàn)方法示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-10-10
  • 詳解Go 并發(fā)

    詳解Go 并發(fā)

    這篇文章主要介紹了Go 并發(fā)的相關(guān)資料,幫助大家更好的理解和學(xué)習(xí)go語言,感興趣的朋友可以了解下
    2020-09-09
  • Go使用proto3的踩坑實(shí)戰(zhàn)記錄

    Go使用proto3的踩坑實(shí)戰(zhàn)記錄

    這篇文章主要給大家介紹了關(guān)于Go使用proto3的踩坑記錄,文中通過實(shí)例代碼介紹的非常詳細(xì),對(duì)大家學(xué)習(xí)或者會(huì)用Go語言具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2023-02-02
  • Go語言interface 與 nil 的比較

    Go語言interface 與 nil 的比較

    在golang中,nil只能賦值給指針、channel、func、interface、map或slice類型的變量。如果未遵循這個(gè)規(guī)則,則會(huì)引發(fā)panic。
    2017-08-08
  • golang中實(shí)現(xiàn)graphql請(qǐng)求的方法

    golang中實(shí)現(xiàn)graphql請(qǐng)求的方法

    這篇文章主要介紹了如何在golang中實(shí)現(xiàn)graphql請(qǐng)求,在本文中,我們介紹了如何使用gqlgen來構(gòu)建GraphQL服務(wù),需要的朋友可以參考下
    2023-04-04
  • go 判斷兩個(gè) slice/struct/map 是否相等的實(shí)例

    go 判斷兩個(gè) slice/struct/map 是否相等的實(shí)例

    這篇文章主要介紹了go 判斷兩個(gè) slice/struct/map 是否相等的實(shí)例,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧
    2020-12-12
  • Go語言中同一個(gè)package中函數(shù)互相調(diào)用為undefined的解決

    Go語言中同一個(gè)package中函數(shù)互相調(diào)用為undefined的解決

    這篇文章主要介紹了Go語言中同一個(gè)package中函數(shù)互相調(diào)用為undefined的解決方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2023-03-03
  • golang的時(shí)區(qū)和神奇的time.Parse的使用方法

    golang的時(shí)區(qū)和神奇的time.Parse的使用方法

    這篇文章主要介紹了golang的時(shí)區(qū)和神奇的time.Parse的使用方法,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2021-04-04

最新評(píng)論