久久久久久久视色,久久电影免费精品,中文亚洲欧美乱码在线观看,在线免费播放AV片

<center id="vfaef"><input id="vfaef"><table id="vfaef"></table></input></center>

    <p id="vfaef"><kbd id="vfaef"></kbd></p>

    
    
    <pre id="vfaef"><u id="vfaef"></u></pre>

      <thead id="vfaef"><input id="vfaef"></input></thead>

    1. 站長(zhǎng)資訊網(wǎng)
      最全最豐富的資訊網(wǎng)站

      方便好用的Golang配置庫(kù)(Viper)

      下面由golang教程欄目給大家介紹一個(gè)輕便好用的Golang配置庫(kù)viper,希望對(duì)需要的朋友有所幫助!

      正文

      viper 的功能

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

      示例代碼

      定義一個(gè)類型:

      type config struct { 	v  *viper.Viper; }

      用于測(cè)試的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();  	//設(shè)置配置文件的名字 	c.v.SetConfigName("config")  	//添加配置文件所在的路徑,注意在Linux環(huán)境下%GOPATH要替換為$GOPATH 	c.v.AddConfigPath("%GOPATH/src/") 	c.v.AddConfigPath("./")  	//設(shè)置配置文件類型 	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去指定路徑,它會(huì)在程序執(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); 		//注意,通常寫(xiě)c++的習(xí)慣害怕讀取字符串的時(shí)候越界,都會(huì)多留出一個(gè)NULL在末尾,但是在這里不行,會(huì)報(bào)出如下錯(cuò)誤: 		//While parsing config: yaml: control characters are not allowed 		//錯(cuò)誤參考網(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("%sn", 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ù)導(dǎo)入IO,然后再?gòu)腎O中讀取

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

      //讀取本地的環(huán)境變量 func EnvConfigPrefix(c *config) error { 	c.v = viper.New();  	//BindEnv($1,$2) 	// 如果只傳入一個(gè)參數(shù),則會(huì)提取指定的環(huán)境變量$1,如果設(shè)置了前綴,則會(huì)自動(dòng)補(bǔ)全 前綴_$1 	//如果傳入兩個(gè)參數(shù)則不會(huì)補(bǔ)全前綴,直接獲取第二參數(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ì)獲取所有的環(huán)境變量,同時(shí)如果過(guò)設(shè)置了前綴則會(huì)自動(dòng)補(bǔ)全前綴名 	c.v.AutomaticEnv(); 	//環(huán)境變量前綴大小寫(xiě)不區(qū)分 	os.Setenv("DEV_ADDONES","none"); 	log.Printf("DEV_ADDONES: %s", c.v.Get("dev_addones"));  	//SetEnvPrefix會(huì)設(shè)置一個(gè)環(huán)境變量的前綴名 	c.v.SetEnvPrefix("DEV");  	os.Setenv("DEV_MODE", "true"); 	//此時(shí)會(huì)自動(dòng)補(bǔ)全前綴,實(shí)際去獲取的是DEV_DEV_MODE 	if nil ==  c.v.Get("dev_mode"){ 		log.Printf("DEV_MODE is nil") ; 	}else { 		return ErrorNotMacth; 	}                  //此時(shí)我們直接指定了loglevel所對(duì)應(yīng)的環(huán)境變量,則不會(huì)去補(bǔ)全前綴 	c.v.BindEnv("loglevel", "LOG_LEVEL"); 	log.Printf("LOG_LEVEL: %s", c.v.Get("loglevel")) ;  	return nil }

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

      方便的替換符

      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一起使用可能會(huì)沖突,比如我下面的例子 	//因?yàn)闀?huì)自動(dòng)補(bǔ)全前綴最終由獲取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; }

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

      別名功能

      //設(shè)置重載 和別名 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)當(dāng)別名對(duì)應(yīng)的值修改之后,原本的key也發(fā)生變化 	log.Printf("id %s, name %s",c.v.Get("id"),c.v.Get("name") ); 	return nil; }

      ? 我們可以為key設(shè)置別名,當(dāng)別名的值被重置后,原key對(duì)應(yīng)的值也會(huì)發(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對(duì)象 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) }

      ? 超級(jí)實(shí)惠的一個(gè)功能,直接把配置反序列化到一個(gè)結(jié)構(gòu)體,爽歪歪有木有?也可以把設(shè)置直接序列化為我們想要的類型: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"); 	//獲取標(biāo)準(zhǔn)包的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")); }

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

      //監(jiān)聽(tīng)配置文件的修改和變動(dòng) func WatchConfig(c *config) error { 	if err := LoadConfigFromYaml(c); err !=nil{ 		return err; 	} 	ctx, cancel := context.WithCancel(context.Background());  	c.v.WatchConfig()                  //監(jiān)聽(tīng)回調(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; }

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

      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")); }

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

      //測(cè)試各種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{}類型,這樣我們還要手動(dòng)轉(zhuǎn)化一下,可以直接指定類型去獲取,方便快捷。

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

      有趣的應(yīng)用

      ? 雖然Unmarshal Struct已經(jīng)足夠好用了,但有作者還是想開(kāi)發(fā)一下新的玩法,比如說(shuō)這個(gè)配置文件和當(dāng)前的新版本不是很匹配,當(dāng)然實(shí)際生產(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); 		} 	}

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

      贊(0)
      分享到: 更多 (0)
      網(wǎng)站地圖   滬ICP備18035694號(hào)-2    滬公網(wǎng)安備31011702889846號(hào)