golang 每日一库 Viper (2)
本文主要介绍一个轻便好用的Golang配置库viper
正文
viper 的功能
viper 支持以下功能:
- 支持Yaml、Json 等格式的配置
- 可以从文件、io、环境变量、command line中提取配置
- 支持自动转换的类型解析
- 实时观看和重新读取配置文件(可选)
- 可以远程从etcd中读取配置
示例
定义一个config类型:
type config struct { v *viper.Viper; }
读取yaml:
注意:如果不用AddConfigPath去指定路径,它会在程序执行的目录去寻找app.yaml结尾文件
yaml配置app.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 Data: Name: "lisi" Alias: - "l" - "a" Favorite: Sport: - "swimming" - "football" Music: - "zui xuan min zu feng" LuckyNumber: 99
代码示例:
func LoadConfigFromYaml(c *config) error { c.v = viper.New() //设置配置文件的名字 c.v.SetConfigName("app") //添加配置文件所在的路径 c.v.AddConfigPath("./etc") c.v.AddConfigPath("./") //设置配置文件类型 c.v.SetConfigType("yaml") if err := c.v.ReadInConfig(); err != nil { return err } log.Println(c.v.AllKeys()) log.Printf("age: %s, name: %s \n", c.v.Get("information.age"), c.v.Get("information.name")) return nil }
从文件中读取配置:
func LoadConfigReadFile(c *config, fileType, filePath string) error { c.v = viper.New() switch fileType { case "yaml", "yml": //设置配置文件类型 c.v.SetConfigType("yaml") case "json": c.v.SetConfigType("json")case "env": c.v.SetConfigType("env") default: c.v.SetConfigType("yaml") log.Println("default config file type to yaml") } log.Println("current config file type is", fileType) if f, err := os.Open("app.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)) 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 }
从json中读取:
配置文件app.json:
{ "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, "Data": { "Name": "lisi", "Alias": [ "l", "a" ] } }, "Favorite": { "Sport": [ "swimming", "football" ], "Music": [ "zui xuan min zu feng" ], "LuckyNumber": 99 } }
代码示例:
func LoadConfigFromJson(c *config) error { c.v = viper.New() //设置配置文件的名字 c.v.SetConfigName("app") //添加配置文件所在的路径,注意在Linux环境下%GOPATH要替换为$GOPATH c.v.AddConfigPath("./etc") c.v.AddConfigPath("./") //设置配置文件类型 c.v.SetConfigType("json") if err := c.v.ReadInConfig(); err != nil { return err } log.Println(c.v.AllKeys()) log.Printf("file type json age: %s, name: %s \n", c.v.Get("information.age"), c.v.Get("information.name")) return nil }
文件修改自动reload
Viper支持让您的应用程序在运行时实时读取配置文件。
需要重新启动服务器才能使配置生效的日子已经一去不复返了,viper驱动的应用程序可以在运行时读取配置文件的更新,不会错过任何一个节拍。
只需告诉viper实例watchConfig即可。您可以为Viper提供一个函数,以便在每次发生更改时运行。
请确保在调用WatchConfig()之前添加了所有的configPaths
viper.OnConfigChange(func(e fsnotify.Event) { fmt.Println("Config file changed:", e.Name) }) viper.WatchConfig()
代码示例:
package main import ( "bytes" "fmt" "log" "net/http" "os" "github.com/fsnotify/fsnotify" "github.com/gin-gonic/gin" "github.com/spf13/viper" ) func main() {// 设置默认值 viper.SetDefault("fileDir", "./") // 读取配置文件 viper.SetConfigFile("./app.yaml") // 指定配置文件路径 viper.SetConfigName("app") // 配置文件名称(无扩展名) viper.SetConfigType("yaml") // 如果配置文件的名称中没有扩展名,则需要配置此项 viper.AddConfigPath("/etc/appname/") // 查找配置文件所在的路径 viper.AddConfigPath("$HOME/.appname") // 多次调用以添加多个搜索路径 viper.AddConfigPath(".") // 还可以在工作目录中查找配置 err := viper.ReadInConfig() // 查找并读取配置文件 if err != nil { // 处理读取配置文件的错误 panic(fmt.Errorf("Fatal error config file: %s \n", err)) } // 实时监控配置文件的变化 WatchConfig 开始监视配置文件的更改。 viper.WatchConfig() // OnConfigChange设置配置文件更改时调用的事件处理程序。 // 当配置文件变化之后调用的一个回调函数 viper.OnConfigChange(func(e fsnotify.Event) { fmt.Println("Config file changed:", e.Name) }) r := gin.Default() r.GET("/version", func(c *gin.Context) { // GetString以字符串的形式返回与键相关的值。 c.String(http.StatusOK, viper.GetString("version")) }) r.Run() }
代码运行输出结果:
===================viper read from yaml=================== 2024/01/08 16:15:33 [favorite.sport favorite.luckynumber information.alise information.data.alias information.image information.data.name passwd information.name favorite.music version information.age information.public timestamp author] 2024/01/08 16:15:33 file type yaml age: 37sss, name: Harry1 ===================viper read yaml from file=================== 2024/01/08 16:15:33 current config file type is yaml 2024/01/08 16:15:33 TimeStamp: "2018-07-16 10:23:19" Author: "WZP" PassWd: "Hello" version: "v1.011111" Information: Name: "Harry" Age: "37" Alise: - "Lion" - "NK" - "KaQS" Image: "/path/header.rpg" Public: false Data: Name: "lisi" Alias: - "l" - "a" Favorite: Sport: - "swimming" - "football" Music: - "zui xuan min zu feng" LuckyNumber: 99 2024/01/08 16:15:33 age: 37, name: Harry ===================viper read from json=================== [GIN-debug] [WARNING] Creating an Engine instance with the Logger and Recovery middleware already attached. [GIN-debug] [WARNING] Running in "debug" mode. Switch to "release" mode in production. - using env: export GIN_MODE=release - using code: gin.SetMode(gin.ReleaseMode) [GIN-debug] GET /version --> main.LoadConfigFromJson.func2 (3 handlers) [GIN-debug] [WARNING] You trusted all proxies, this is NOT safe. We recommend you to set a value. Please check https://pkg.go.dev/github.com/gin-gonic/gin#readme-don-t-trust-all-proxies for details. [GIN-debug] Environment variable PORT is undefined. Using port :8080 by default [GIN-debug] Listening and serving HTTP on :8080
完整代码:
package main import ( "bytes" "fmt" "log" "net/http" "os" "github.com/fsnotify/fsnotify" "github.com/gin-gonic/gin" "github.com/spf13/viper" ) type config struct { v *viper.Viper } func LoadConfigFromYaml(c *config) error { c.v = viper.New() //设置配置文件的名字 c.v.SetConfigName("app") //添加配置文件所在的路径 c.v.AddConfigPath("./etc") c.v.AddConfigPath("./") //设置配置文件类型 c.v.SetConfigType("yaml") if err := c.v.ReadInConfig(); err != nil { return err } log.Println(c.v.AllKeys()) log.Printf("file type yaml age: %s, name: %s \n", c.v.Get("information.age"), c.v.Get("information.name")) return nil } func LoadConfigFromJson(c *config) error { c.v = viper.New() //设置配置文件的名字 c.v.SetConfigName("app") //添加配置文件所在的路径 c.v.AddConfigPath("./etc") c.v.AddConfigPath("./") //设置配置文件类型 // because there is no file extension in a stream of bytes, // supported extensions are "json", "toml", "yaml", "yml", // "properties", "props", "prop", "env", "dotenv" c.v.SetConfigType("json") if err := c.v.ReadInConfig(); err != nil { return err } log.Println(c.v.AllKeys()) log.Printf("file type json age: %s, name: %s \n", c.v.Get("information.age"), c.v.Get("information.name")) return nil } func LoadConfigReadFile(c *config, fileType, filePath string) error { c.v = viper.New() switch fileType { case "yaml", "yml": //设置配置文件类型 c.v.SetConfigType("yaml") case "json": c.v.SetConfigType("json") case "toml": c.v.SetConfigType("toml") case "hcl": c.v.SetConfigType("hcl") case "env": c.v.SetConfigType("env") default: c.v.SetConfigType("yaml") log.Println("default config file type to yaml") } log.Println("current config file type is", fileType) if f, err := os.Open("app.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)) 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 } func main() { fmt.Println("===================viper read from yaml===================") if err := LoadConfigFromYaml(&config{}); err != nil { panic(err) } fmt.Println("===================viper read yaml from file===================") if err := LoadConfigReadFile(&config{}, "yaml", "./app.yaml"); err != nil { panic(err) } fmt.Println("===================viper read from json===================") if err := LoadConfigFromJson(&config{}); err != nil { panic(err) } fmt.Println("===================viper listen file change ===================") // 设置默认值 viper.SetDefault("fileDir", "./") // 读取配置文件 viper.SetConfigFile("./app.yaml") // 指定配置文件路径 viper.SetConfigName("app") // 配置文件名称(无扩展名) viper.SetConfigType("yaml") // 如果配置文件的名称中没有扩展名,则需要配置此项 viper.AddConfigPath("/etc/appname/") // 查找配置文件所在的路径 viper.AddConfigPath("$HOME/.appname") // 多次调用以添加多个搜索路径 viper.AddConfigPath(".") // 还可以在工作目录中查找配置 err := viper.ReadInConfig() // 查找并读取配置文件 if err != nil { // 处理读取配置文件的错误 panic(fmt.Errorf("Fatal error config file: %s \n", err)) } // 实时监控配置文件的变化 WatchConfig 开始监视配置文件的更改。 viper.WatchConfig() // OnConfigChange设置配置文件更改时调用的事件处理程序。 // 当配置文件变化之后调用的一个回调函数 viper.OnConfigChange(func(e fsnotify.Event) { fmt.Println("Config file changed:", e.Name) }) r := gin.Default() r.GET("/version", func(c *gin.Context) { // GetString以字符串的形式返回与键相关的值。 c.String(http.StatusOK, viper.GetString("version")) }) r.Run() }
本文来自博客园,作者:haonan071,转载请注明原文链接:https://www.cnblogs.com/zhanghn8/p/17952359
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 25岁的心里话
· 闲置电脑爆改个人服务器(超详细) #公网映射 #Vmware虚拟网络编辑器
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· 零经验选手,Compose 一天开发一款小游戏!
· 一起来玩mcp_server_sqlite,让AI帮你做增删改查!!