您的位置 首页 > 德语词汇

viper是什么意思(每日一库之)

这篇文章给大家聊聊关于viper是什么意思,以及每日一库之对应的知识点,希望对各位有所帮助,不要忘了收藏本站哦。

上一篇文章介绍cobra的时候提到了viper,今天我们就来介绍一下这个库。viper是一个配置解决方案,拥有丰富的特性:

viper是什么意思(每日一库之)

$gogetgithub.com/spf13/viper\n复制代码

使用:

packagemain\n\nimport(\n"fmt"\n"log"\n\n"github.com/spf13/viper"\n)\n\nfuncmain(){\nviper.SetConfigName("config")\nviper.SetConfigType("toml")\nviper.AddConfigPath(".")\nviper.SetDefault("redis.port",6381)\nerr:=viper.ReadInConfig()\niferr!=nil{\nlog.Fatal("readconfigfailed:%v",err)\n}\n\nfmt.Println(viper.Get("app_name"))\nfmt.Println(viper.Get("log_level"))\n\nfmt.Println("mysqlip:",viper.Get("mysql.ip"))\nfmt.Println("mysqlport:",viper.Get("mysql.port"))\nfmt.Println("mysqluser:",viper.Get("mysql.user"))\nfmt.Println("mysqlpassword:",viper.Get("mysql.password"))\nfmt.Println("mysqldatabase:",viper.Get("mysql.database"))\n\nfmt.Println("redisip:",viper.Get("redis.ip"))\nfmt.Println("redisport:",viper.Get("redis.port"))\n}\n复制代码

我们使用之前Go每日一库之go-ini一文中使用的配置,不过改为toml格式。toml的语法很简单,快速入门请看learnXinYminutes。

app_name="awesomeweb"\n\n#possiblevalues:DEBUG,INFO,WARNING,ERROR,FATAL\nlog_level="DEBUG"\n\n[mysql]\nip="127.0.0.1"\nport=3306\nuser="dj"\npassword=123456\ndatabase="awesome"\n\n[redis]\nip="127.0.0.1"\nport=7381\n复制代码

viper的使用非常简单,它需要很少的设置。设置文件名(SetConfigName)、配置类型(SetConfigType)和搜索路径(AddConfigPath),然后调用ReadInConfig。viper会自动根据类型来读取配置。使用时调用viper.Get方法获取键值。

awesomeweb\nDEBUG\nmysqlip:127.0.0.1\nmysqlport:3306\nmysqluser:dj\nmysqlpassword:123456\nmysqldatabase:awesome\nredisip:127.0.0.1\nredisport:7381\n复制代码

有几点需要注意:

viper提供了多种形式的读取方法。在上面的例子中,我们看到了Get方法的用法。Get方法返回一个interface{}的值,使用有所不便。

GetType系列方法可以返回指定类型的值。其中,Type可以为Bool/Float64/Int/String/Time/Duration/IntSlice/StringSlice。但是请注意,如果指定的键不存在或类型不正确,GetType方法返回对应类型的零值

如果要判断某个键是否存在,使用IsSet方法。另外,GetStringMap和GetStringMapString直接以map返回某个键下面所有的键值对,前者返回map[string]interface{},后者返回map[string]string。AllSettings以map[string]interface{}返回所有设置。

//省略包名和import部分\n\nfuncmain(){\nviper.SetConfigName("config")\nviper.SetConfigType("toml")\nviper.AddConfigPath(".")\nerr:=viper.ReadInConfig()\niferr!=nil{\nlog.Fatal("readconfigfailed:%v",err)\n}\n\nfmt.Println("protocols:",viper.GetStringSlice("server.protocols"))\nfmt.Println("ports:",viper.GetIntSlice("server.ports"))\nfmt.Println("timeout:",viper.GetDuration("server.timeout"))\n\nfmt.Println("mysqlip:",viper.GetString("mysql.ip"))\nfmt.Println("mysqlport:",viper.GetInt("mysql.port"))\n\nifviper.IsSet("redis.port"){\nfmt.Println("redis.portisset")\n}else{\nfmt.Println("redis.portisnotset")\n}\n\nfmt.Println("mysqlsettings:",viper.GetStringMap("mysql"))\nfmt.Println("redissettings:",viper.GetStringMap("redis"))\nfmt.Println("allsettings:",viper.AllSettings())\n}\n复制代码

我们在配置文件config.toml中添加protocols和ports配置:

[server]\nprotocols=["http","https","port"]\nports=[10000,10001,10002]\ntimeout=3s\n复制代码

编译、运行程序,输出:

protocols:[httphttpsport]\nports:[100001000110002]\ntimeout:3s\nmysqlip:127.0.0.1\nmysqlport:3306\nredis.portisset\nmysqlsettings:map[database:awesomeip:127.0.0.1password:123456port:3306user:dj]\nredissettings:map[ip:127.0.0.1port:7381]\nallsettings:map[app_name:awesomeweblog_level:DEBUGmysql:map[database:awesomeip:127.0.0.1password:123456port:3306user:dj]redis:map[ip:127.0.0.1port:7381]server:map[ports:[100001000110002]protocols:[httphttpsport]]]\n复制代码

如果将配置中的redis.port注释掉,将输出redis.portisnotset。

上面的示例中还演示了如何使用time.Duration类型,只要是time.ParseDuration接受的格式都可以,例如3s、2min、1min30s等。

viper支持在多个地方设置,使用下面的顺序依次读取:

如果某个键通过viper.Set设置了值,那么这个值的优先级最高。

viper.Set("redis.port",5381)\n复制代码

如果将上面这行代码放到程序中,运行程序,输出的redis.port将是5381。

如果一个键没有通过viper.Set显示设置值,那么获取时将尝试从命令行选项中读取。如果有,优先使用。viper使用pflag库来解析选项。我们首先在init方法中定义选项,并且调用viper.BindPFlags绑定选项到配置中:

funcinit(){\npflag.Int("redis.port",8381,"Redisporttoconnect")\n\n//绑定命令行\nviper.BindPFlags(pflag.CommandLine)\n}\n复制代码

然后,在main方法开头处调用pflag.Parse解析选项。

$./main.exe--redis.port9381\nawesomeweb\nDEBUG\nmysqlip:127.0.0.1\nmysqlport:3306\nmysqluser:dj\nmysqlpassword:123456\nmysqldatabase:awesome\nredisip:127.0.0.1\nredisport:9381\n复制代码

如何不传入选项:

$./main.exe\nawesomeweb\nDEBUG\nmysqlip:127.0.0.1\nmysqlport:3306\nmysqluser:dj\nmysqlpassword:123456\nmysqldatabase:awesome\nredisip:127.0.0.1\nredisport:7381\n复制代码

注意,这里并不会使用选项redis.port的默认值。

但是,如果通过下面的方法都无法获得键值,那么返回选项默认值(如果有)。试试注释掉配置文件中redis.port看看效果。

如果前面都没有获取到键值,将尝试从环境变量中读取。我们既可以一个个绑定,也可以自动全部绑定。

在init方法中调用AutomaticEnv方法绑定全部环境变量:

funcinit(){\n//绑定环境变量\nviper.AutomaticEnv()\n}\n复制代码

为了验证是否绑定成功,我们在main方法中将环境变量GOPATH打印出来:

funcmain(){\n//省略部分代码\n\nfmt.Println("GOPATH:",viper.Get("GOPATH"))\n}\n复制代码

通过系统->高级设置->新建创建一个名为redis.port的环境变量,值为10381。运行程序,输出的redis.port值为10381,并且输出中有GOPATH信息。

funcinit(){\n//绑定环境变量\nviper.BindEnv("redis.port")\nviper.BindEnv("go.path","GOPATH")\n}\n\nfuncmain(){\n//省略部分代码\nfmt.Println("gopath:",viper.Get("go.path"))\n}\n复制代码

调用BindEnv方法,如果只传入一个参数,则这个参数既表示键名,又表示环境变量名。如果传入两个参数,则第一个参数表示键名,第二个参数表示环境变量名。

还可以通过viper.SetEnvPrefix方法设置环境变量前缀,这样一来,通过AutomaticEnv和一个参数的BindEnv绑定的环境变量,在使用Get的时候,viper会自动加上这个前缀再从环境变量中查找。

如果对应的环境变量不存在,viper会自动将键名全部转为大写再查找一次。所以,使用键名gopath也能读取环境变量GOPATH的值。

如果经过前面的途径都没能找到该键,viper接下来会尝试从配置文件中查找。为了避免环境变量的影响,需要删除redis.port这个环境变量。

在上面的快速使用一节,我们已经看到了如何设置默认值,这里就不赘述了。

viper支持从io.Reader中读取配置。这种形式很灵活,来源可以是文件,也可以是程序中生成的字符串,甚至可以从网络连接中读取的字节流。

packagemain\n\nimport(\n"bytes"\n"fmt"\n"log"\n\n"github.com/spf13/viper"\n)\n\nfuncmain(){\nviper.SetConfigType("toml")\ntomlConfig:=[]byte(`\napp_name="awesomeweb"\n\n#possiblevalues:DEBUG,INFO,WARNING,ERROR,FATAL\nlog_level="DEBUG"\n\n[mysql]\nip="127.0.0.1"\nport=3306\nuser="dj"\npassword=123456\ndatabase="awesome"\n\n[redis]\nip="127.0.0.1"\nport=7381\n`)\nerr:=viper.ReadConfig(bytes.NewBuffer(tomlConfig))\niferr!=nil{\nlog.Fatal("readconfigfailed:%v",err)\n}\n\nfmt.Println("redisport:",viper.GetInt("redis.port"))\n}\n复制代码Unmarshal

viper支持将配置Unmarshal到一个结构体中,为结构体中的对应字段赋值。

packagemain\n\nimport(\n"fmt"\n"log"\n\n"github.com/spf13/viper"\n)\n\ntypeConfigstruct{\nAppNamestring\nLogLevelstring\n\nMySQLMySQLConfig\nRedisRedisConfig\n}\n\ntypeMySQLConfigstruct{\nIPstring\nPortint\nUserstring\nPasswordstring\nDatabasestring\n}\n\ntypeRedisConfigstruct{\nIPstring\nPortint\n}\n\nfuncmain(){\nviper.SetConfigName("config")\nviper.SetConfigType("toml")\nviper.AddConfigPath(".")\nerr:=viper.ReadInConfig()\niferr!=nil{\nlog.Fatal("readconfigfailed:%v",err)\n}\n\nvarcConfig\nviper.Unmarshal(&c)\n\nfmt.Println(c.MySQL)\n}\n复制代码

编译,运行程序,输出:

{127.0.0.13306dj123456awesome}\n复制代码保存配置

有时候,我们想要将程序中生成的配置,或者所做的修改保存下来。viper提供了接口!

下面我们通过程序生成一个config.toml配置:

packagemain\n\nimport(\n"log"\n\n"github.com/spf13/viper"\n)\n\nfuncmain(){\nviper.SetConfigName("config")\nviper.SetConfigType("toml")\nviper.AddConfigPath(".")\n\nviper.Set("app_name","awesomeweb")\nviper.Set("log_level","DEBUG")\nviper.Set("mysql.ip","127.0.0.1")\nviper.Set("mysql.port",3306)\nviper.Set("mysql.user","root")\nviper.Set("mysql.password","123456")\nviper.Set("mysql.database","awesome")\n\nviper.Set("redis.ip","127.0.0.1")\nviper.Set("redis.port",6381)\n\nerr:=viper.SafeWriteConfig()\niferr!=nil{\nlog.Fatal("writeconfigfailed:",err)\n}\n}\n复制代码

编译、运行程序,生成的文件如下:

app_name="awesomeweb"\nlog_level="DEBUG"\n\n[mysql]\ndatabase="awesome"\nip="127.0.0.1"\npassword="123456"\nport=3306\nuser="root"\n\n[redis]\nip="127.0.0.1"\nport=6381\n复制代码监听文件修改

viper可以监听文件修改,热加载配置。因此不需要重启服务器,就能让配置生效。

packagemain\n\nimport(\n"fmt"\n"log"\n"time"\n\n"github.com/spf13/viper"\n)\n\nfuncmain(){\nviper.SetConfigName("config")\nviper.SetConfigType("toml")\nviper.AddConfigPath(".")\nerr:=viper.ReadInConfig()\niferr!=nil{\nlog.Fatal("readconfigfailed:%v",err)\n}\n\nviper.WatchConfig()\n\nfmt.Println("redisportbeforesleep:",viper.Get("redis.port"))\ntime.Sleep(time.Second*10)\nfmt.Println("redisportaftersleep:",viper.Get("redis.port"))\n}\n复制代码

只需要调用viper.WatchConfig,viper会自动监听配置修改。如果有修改,重新加载的配置。

上面程序中,我们先打印redis.port的值,然后Sleep10s。在这期间修改配置中redis.port的值,Sleep结束后再次打印。发现打印出修改后的值:

redisportbeforesleep:7381\nredisportaftersleep:73810\n复制代码

另外,还可以为配置修改增加一个回调:

viper.OnConfigChange(func(efsnotify.Event){\nfmt.Printf("Configfile:%sOp:%s\\n",e.Name,e.Op)\n})\n复制代码

这样文件修改时会执行这个回调。

好了,文章到此结束,希望可以帮助到大家。

本站涵盖的内容、图片、视频等数据,部分未能与原作者取得联系。若涉及版权问题,请及时通知我们并提供相关证明材料,我们将及时予以删除!谢谢大家的理解与支持!

Copyright © 2023