欧美一级特黄大片做受成人-亚洲成人一区二区电影-激情熟女一区二区三区-日韩专区欧美专区国产专区

Golang配置庫viper的用法

小編給大家分享一下Golang配置庫viper的用法,相信大部分人都還不怎么了解,因此分享這篇文章給大家參考一下,希望大家閱讀完這篇文章后大有收獲,下面讓我們一起去了解一下吧!

創(chuàng)新互聯(lián)專業(yè)為企業(yè)提供普陀網(wǎng)站建設、普陀做網(wǎng)站、普陀網(wǎng)站設計、普陀網(wǎng)站制作等企業(yè)網(wǎng)站建設、網(wǎng)頁設計與制作、普陀企業(yè)網(wǎng)站模板建站服務,十余年普陀做網(wǎng)站經(jīng)驗,不只是建網(wǎng)站,更提供有價值的思路和整體網(wǎng)絡服務。

正文

viper 的功能

? viper 支持以下功能:
? 1.  支持Yaml、Json、 TOML、HCL 等格式的配置
?  2.  可以從文件、io、環(huán)境變量、command line中提取配置
? 3.  支持自動轉換的類型解析
?  4.  可以遠程從etcd中讀取配置

示例代碼

定義一個類型:

type config struct {
	v  *viper.Viper;
}

用于測試的Yaml配置文件 config.yaml

TimeStamp: "2018-07-16 10:23:19"
Author: "WZP"
PassWd: "Hello"
Information:
   Name: "Harry"
   Age: "37"
   Alise:
   - "Lion"
   - "NK"
   - "KaQS"
   Image: "/path/header.rpg"
   Public: false

Favorite:
  Sport:
  - "swimming"
  - "football"
  Music:
  - "zui xuan min zu feng"
  LuckyNumber: 99

讀取yaml配置文件

func LoadConfigFromYaml (c *config) error  {
	c.v = viper.New();

	//設置配置文件的名字
	c.v.SetConfigName("config")

	//添加配置文件所在的路徑,注意在Linux環(huán)境下%GOPATH要替換為$GOPATH
	c.v.AddConfigPath("%GOPATH/src/")
	c.v.AddConfigPath("./")

	//設置配置文件類型
	c.v.SetConfigType("yaml");

	if err := c.v.ReadInConfig(); err != nil{
		return  err;
	}

	log.Printf("age: %s, name: %s \n", c.v.Get("information.age"), c.v.Get("information.name"));
	return nil;
}

? 注意:如果不用AddConfigPath去指定路徑,它會在程序執(zhí)行的目錄去尋找config.yaml

從IO中讀取配置

//由IO讀取配置
func ReadConfigFormIo(c *config) error {
	c.v = viper.New()
	if f, err := os.Open("config.yaml"); err != nil{
		log.Printf("filure: %s", err.Error());
		return err;
	}else {

		confLength, _ :=f.Seek(0,2);
		//注意,通常寫c++的習慣害怕讀取字符串的時候越界,都會多留出一個NULL在末尾,但是在這里不行,會報出如下錯誤:
		//While parsing config: yaml: control characters are not allowed
		//錯誤參考網(wǎng)址:https://stackoverflow.com/questions/33717799/go-yaml-control-characters-are-not-allowed-error
		configData := make([]byte, confLength);
		f.Seek(0, 0);
		f.Read(configData);
		log.Printf("%s\n", string(configData))

		c.v.SetConfigType("yaml");
		if err := c.v.ReadConfig(bytes.NewBuffer(configData)); err != nil{
			log.Fatalf(err.Error());
		}
	}
	log.Printf("age: %s, name: %s \n", c.v.Get("information.age"), c.v.Get("information.name"));

	return nil;
}

? 上面的代碼是把配置文件中的數(shù)據(jù)導入IO,然后再從IO中讀取

從環(huán)境變量中讀取配置

//讀取本地的環(huán)境變量
func EnvConfigPrefix(c *config) error {
	c.v = viper.New();

	//BindEnv($1,$2)
	// 如果只傳入一個參數(shù),則會提取指定的環(huán)境變量$1,如果設置了前綴,則會自動補全 前綴_$1
	//如果傳入兩個參數(shù)則不會補全前綴,直接獲取第二參數(shù)中傳入的環(huán)境變量$2
	os.Setenv("LOG_LEVEL", "INFO");
	if nil == c.v.Get("LOG_LEVEL ") {
		log.Printf("LOG_LEVEL is nil");
	}else {
		return ErrorNotMacth;
	}
        
        //必須要綁定后才能獲取
	c.v.BindEnv("LOG_LEVEL");
	log.Printf("LOG_LEVEL is %s", os.Getenv("log_level"));


	//會獲取所有的環(huán)境變量,同時如果過設置了前綴則會自動補全前綴名
	c.v.AutomaticEnv();
	//環(huán)境變量前綴大小寫不區(qū)分
	os.Setenv("DEV_ADDONES","none");
	log.Printf("DEV_ADDONES: %s", c.v.Get("dev_addones"));

	//SetEnvPrefix會設置一個環(huán)境變量的前綴名
	c.v.SetEnvPrefix("DEV");

	os.Setenv("DEV_MODE", "true");
	//此時會自動補全前綴,實際去獲取的是DEV_DEV_MODE
	if nil ==  c.v.Get("dev_mode"){
		log.Printf("DEV_MODE is nil") ;
	}else {
		return ErrorNotMacth;
	}
        
        //此時我們直接指定了loglevel所對應的環(huán)境變量,則不會去補全前綴
	c.v.BindEnv("loglevel", "LOG_LEVEL");
	log.Printf("LOG_LEVEL: %s", c.v.Get("loglevel")) ;

	return nil
}

? SetEnvPrefix 和 AutomaticEnv、BindEnv搭配使用很方便,比如說我們把當前程序的環(huán)境變量都設置為xx_ ,這樣方便我們管理,也避免和其他環(huán)境變量沖突,而在讀取的時候又很方便的就可以讀取。

方便的替換符

func EnvCongiReplacer(c *config, setPerfix bool) error {
	c.v = viper.New();
	c.v.AutomaticEnv();
	c.v.SetEnvKeyReplacer(strings.NewReplacer(".","_"));

	os.Setenv("API_VERSION","v0.1.0");
	//Replacer和prefix一起使用可能會沖突,比如我下面的例子
	//因為會自動補全前綴最終由獲取API_VERSION變成API_API_VERSION
	if setPerfix{ c.v.SetEnvPrefix("api");}
	if s := c.v.Get("api.version"); s==nil{
		return ErrorNoxExistKey
	}else {
		log.Printf("%s", c.v.Get("api.version"));
	}

	return nil;
}

? 我們有時候需要去替換key中的某些字符,來轉化為對應的環(huán)境變臉,比如說例子中將' . '替換為'_' ,由獲取api.version變成了api_version,但是有一點需要注意的,SetEnvPrefix和SetEnvKeyReplacer一起用的時候可能會混淆。

別名功能

//設置重載 和別名
func SetAndAliases(c *config) error {
	c.v = viper.New();
	c.v.Set("Name","wzp");
	c.v.RegisterAlias("id","Name");
	c.v.Set("id","Mr.Wang");

	//我們可以發(fā)現(xiàn)當別名對應的值修改之后,原本的key也發(fā)生變化
	log.Printf("id %s, name %s",c.v.Get("id"),c.v.Get("name") );
	return nil;
}

? 我們可以為key設置別名,當別名的值被重置后,原key對應的值也會發(fā)生變化。

序列化和反序列化

type favorite struct {
	Sports []string;
	Music []string;
	LuckyNumber int;
}

type information struct {
	Name string;
	Age  int;
	Alise []string;
	Image string;
	Public bool
}

type YamlConfig struct {
	TimeStamp string
	Author string
	PassWd string
	Information information
	Favorite favorite;
}



//將配置解析為Struct對象
func UmshalStruct(c *config) error  {
	LoadConfigFromYaml(c);
	var cf YamlConfig
	if err := c.v.Unmarshal(&cf); err != nil{
		return err;
	}
        
        
	return nil;
}

func YamlStringSettings(c *config) string {
	c.v = viper.New();
	c.v.Set("name", "wzp");
	c.v.Set("age", 18);
	c.v.Set("aliase",[]string{"one","two","three"})

	cf := c.v.AllSettings()
	bs, err := yaml.Marshal(cf)
	if err != nil {
		log.Fatalf("unable to marshal config to YAML: %v", err)
	}
	return string(bs)
}

func JsonStringSettings(c *config) string {
	c.v = viper.New();
	c.v.Set("name", "wzp");
	c.v.Set("age", 18);
	c.v.Set("aliase",[]string{"one","two","three"})

	cf := c.v.AllSettings()
	bs, err := json.Marshal(cf)
	if err != nil {
		log.Fatalf("unable to marshal config to YAML: %v", err)
	}
	return string(bs)
}

? 超級實惠的一個功能,直接把配置反序列化到一個結構體,爽歪歪有木有?也可以把設置直接序列化為我們想要的類型:yaml、json等等
從command Line中讀取配置

func main()  {
	flag.String("mode","RUN","please input the mode: RUN or DEBUG");
	pflag.Int("port",1080,"please input the listen port");
	pflag.String("ip","127.0.0.1","please input the bind ip");
	//獲取標準包的flag
	pflag.CommandLine.AddGoFlagSet(flag.CommandLine);
	pflag.Parse();

	//BindFlag
	//在pflag.Init key后面使用
	viper.BindPFlag("port", pflag.Lookup("port"));
	log.Printf("set port: %d", viper.GetInt("port"));

	viper.BindPFlags(pflag.CommandLine);
	log.Printf("set ip: %s", viper.GetString("ip"));
}

? 可以使用標準的flag也可以使用viper包中自帶的pflag,作者建議使用pflag。
監(jiān)聽配置文件

//監(jiān)聽配置文件的修改和變動
func WatchConfig(c *config) error {
	if err := LoadConfigFromYaml(c); err !=nil{
		return err;
	}
	ctx, cancel := context.WithCancel(context.Background());

	c.v.WatchConfig()
        
        //監(jiān)聽回調(diào)函數(shù)
	watch := func(e fsnotify.Event) {
		log.Printf("Config file is changed: %s \n", e.String())
		cancel();
	}

	c.v.OnConfigChange(watch);
	<-ctx.Done();
	return nil;
}

? 重點來了啊,這個可以說是非常非常實用的一個功能,以往我們修改配置文件要么重啟服務,要么搞一個api去修改,Viper把這個功能幫我們實現(xiàn)了。只要配置文件被修改保存后,我們事先注冊的watch函數(shù)就回被觸發(fā),只要我們在這里面添加更新操作就ok了。不過美中不足的是,它目前只監(jiān)聽配置文件。
拷貝子分支

func TestSubConfig(t *testing.T)  {
	c := config{};
	LoadConfigFromYaml(&c);
	sc := c.v.Sub("information");
	sc.Set("age", 80);
	scs,_:=yaml.Marshal(sc.AllSettings())
	t.Log(string(scs));
	t.Logf("age: %d", c.v.GetInt("information.age"));
}

? 拷貝一個子分支最大的用途就是我們可以復制一份配置,這樣在修改拷貝的時候原配置不會被修改,如果修改的配置出現(xiàn)了問題,我們可以方便的回滾。
獲取配置項的方法

//測試各種get類型
func TestGetValues(t *testing.T)  {
	c := &config{}
	if err := LoadConfigFromYaml(c); err != nil{
		t.Fatalf("%s: %s",t.Name(), err.Error());
	}

	if info := c.v.GetStringMap("information"); info != nil{
		t.Logf("%T", info);
	}

	if aliases := c.v.GetStringSlice("information.aliases"); aliases != nil{
		for _, a := range  aliases{
			t.Logf("%s",a);
		}
	}

	timeStamp := c.v.GetTime("timestamp");
	t.Logf("%s", timeStamp.String());

	if public := c.v.GetBool("information.public"); public{
		t.Logf("the information is public");
	}

	age := c.v.GetInt("information.age");
	t.Logf("%s age  is %d", c.v.GetString("information.name"), age);
}

?如果我們直接用Get獲取的返回值都是interface{}類型,這樣我們還要手動轉化一下,可以直接指定類型去獲取,方便快捷。

?除了以上所說的功能外,viper還有從etcd提取配置以及自定義flage的功能,這些大家感興趣可以自己去了解一下。

有趣的應用

? 雖然Unmarshal Struct已經(jīng)足夠好用了,但有作者還是想開發(fā)一下新的玩法,比如說這個配置文件和當前的新版本不是很匹配,當然實際生產(chǎn)中我們是要講究向下兼容的。

        var yamlConfig =  YamlConfig{};
	ycType := reflect.TypeOf(yamlConfig);

	for i := 0 ; i < ycType.NumField();i++{
		name := ycType.Field(i).Name;
		element := reflect.ValueOf(yamlConfig).Field(i).Interface();
		if err = config.UnmarshalKey(name, element); err != nil{
			logger.Errorf("Error reading configuration:", err);
		}
	}

? 如上代碼所示,我們從最外圍的結構體中找出子元素的名稱和interface,然后分別解析,這樣及時某一項缺失了我們也可以及時提醒用戶,或者設置缺省配置,還有很多好玩的方法,大家可以互相參考哦。

以上是“Golang配置庫viper的用法”這篇文章的所有內(nèi)容,感謝各位的閱讀!相信大家都有了一定的了解,希望分享的內(nèi)容對大家有所幫助,如果還想學習更多知識,歡迎關注創(chuàng)新互聯(lián)行業(yè)資訊頻道!

網(wǎng)站名稱:Golang配置庫viper的用法
當前網(wǎng)址:http://aaarwkj.com/article16/pegodg.html

成都網(wǎng)站建設公司_創(chuàng)新互聯(lián),為您提供定制網(wǎng)站、用戶體驗、網(wǎng)站設計自適應網(wǎng)站、外貿(mào)建站定制開發(fā)

廣告

聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉載內(nèi)容為主,如果涉及侵權請盡快告知,我們將會在第一時間刪除。文章觀點不代表本網(wǎng)站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉載,或轉載時需注明來源: 創(chuàng)新互聯(lián)

成都做網(wǎng)站
丰满人妻在线一区二区三区| 久久一二三四区中文字幕| 欧美成人极品一区二区三区| 久久亚洲综合色一区二区三区| 四虎精品视频在线免费| 欧美内射专区精品一区| 国产精品国产三级国产av野外| 国产精品大片一区二区三区四区| 欧美精品一区二区精品久久| 91中文在线观看一区| 九九热九九热九九热九| 国产精品97久久97久久久久| 国产精品兄妹在线观看91| 日本免费一区二区三区手机在线| 一区二区三区蜜桃91| 国产亚洲精品久久综合阿香| 欧美日韩精品久久影院| 久久99久久久国产精品| 亚洲欧洲日产国码一区| 日本午夜诱惑在线观看| 亚洲国产欲色有一二欲色| 久久亚洲一区二区内射| 天天操天天干夜夜骑| 熟妇人妻内射一区二区三区| 在线观看男人的天堂av| 97国产精品成人免费视频| 亚洲乱码中文字幕在线观看| 久久国产欧美日韩精品免费 | 亚洲精品在线观看午夜福利| 熟妇高潮一区二区三区| 国产日产精品久久一区| 亚洲国产精品成人久久蜜臀| 一区二区精品人妻av| 亚洲欧美日韩一区二区视频| 国产精品久久久久久久av三级| 高清欧美精品一区二区三区| 五月婷婷丁香噜噜噜噜| 精品亚洲一区二区在线| 蜜桃传媒18传媒在线| 日韩精品视频性色首页| 日韩精品中文字幕有码|