Viper--方便好用的Golang 配置庫

來源:互聯網
上載者:User

前言


本文主要是為讀者介紹一個輕便好用的Golang配置庫viper

本文

viper 的功能

  viper 支援以下功能:
  1. 支援Yaml、Json、 TOML、HCL 等格式的配置
  2. 可以從檔案、io、環境變數、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: falseFavorite:  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環境下%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去指定路徑,它會在程式執行的目錄去尋找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        //錯誤參考網址: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;}

  上面的代碼是把設定檔中的資料匯入IO,然後再從IO中讀取


從環境變數中讀取配置

//讀取本地的環境變數func EnvConfigPrefix(c *config) error {    c.v = viper.New();    //BindEnv($1,$2)    // 如果只傳入一個參數,則會提取指定的環境變數$1,如果設定了首碼,則會自動補全 首碼_$1    //如果傳入兩個參數則不會補全首碼,直接擷取第二參數中傳入的環境變數$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"));    //會擷取所有的環境變數,同時如果過設定了首碼則會自動補全首碼名    c.v.AutomaticEnv();    //環境變數首碼大小寫不區分    os.Setenv("DEV_ADDONES","none");    log.Printf("DEV_ADDONES: %s", c.v.Get("dev_addones"));    //SetEnvPrefix會設定一個環境變數的首碼名    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所對應的環境變數,則不會去補全首碼    c.v.BindEnv("loglevel", "LOG_LEVEL");    log.Printf("LOG_LEVEL: %s", c.v.Get("loglevel")) ;    return nil}

  SetEnvPrefix 和 AutomaticEnv、BindEnv搭配使用很方便,比如說我們把當前程式的環境變數都設定為xx_ ,這樣方便我們管理,也避免和其他環境變數衝突,而在讀取的時候又很方便的就可以讀取。

方便的替換符

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中的某些字元,來轉化為對應的環境變臉,比如說例子中將' . '替換為'_' ,由擷取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");    //我們可以發現當別名對應的值修改之後,原本的key也發生變化    log.Printf("id %s, name %s",c.v.Get("id"),c.v.Get("name") );    return nil;}

  我們可以為key設定別名,當別名的值被重設後,原key對應的值也會發生變化。

'

序列化和還原序列化

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。

監聽設定檔

//監聽設定檔的修改和變動func WatchConfig(c *config) error {    if err := LoadConfigFromYaml(c); err !=nil{        return err;    }    ctx, cancel := context.WithCancel(context.Background());    c.v.WatchConfig()                //監聽回呼函數    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把這個功能幫我們實現了。只要設定檔被修改儲存後,我們事先註冊的watch函數就回被觸發,只要我們在這裡面添加更新操作就ok了。不過美中不足的是,它目前只監聽設定檔。

拷貝子分支

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

  拷貝一個子分支最大的用途就是我們可以複製一份配置,這樣在修改拷貝的時候原配置不會被修改,如果修改的配置出現了問題,我們可以方便的復原。

擷取配置項的方法

//測試各種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的功能,這些大家感興趣可以自己去瞭解一下。

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.