比拟有名的计划有
应用 viper 治理配置
- 反对多种配置文件格式,包含 JSON,TOML,YAML,HECL,envfile,甚至还包含 Java properties
- 反对为配置项设置默认值
- 能够通过命令行参数笼罩指定的配置项
- 反对参数别名
viper 依照这个优先级(从高到低)获取配置项的取值:
- explicit call to Set: 在代码逻辑中通过 viper.Set()间接设置配置项的值
- flag:命令行参数
- env:环境变量
- config:配置文件
- key/value store:etcd 或者 consul
- default:默认值
依照这个优先级(从高到低)获取配置项的取值:
- explicit call to Set: 在代码逻辑中通过 viper.Set()间接设置配置项的值
- flag:命令行参数
- env:环境变量
- config:配置文件
- key/value store:etcd 或者 consul
- default:默认值
优先级
验证一下 viper.Set() 的优先级高于 配置文件
package main
import (
"fmt"
"github.com/spf13/viper"
)
func main() {loadConfig()
}
func loadConfig() {
configVar := "shuang-config.yaml"
configVar = "" // 这行如果正文掉,则从指定的 configVar 读取配置文件;否则就各种条件去找了
viper.Set("Global.Source", "优先级最高")
if configVar != "" {
// SetConfigFile 显式定义配置文件的门路、名称和扩展名。// Viper 将应用它而不查看任何配置门路。viper.SetConfigFile(configVar)
} else {
// 如果没有显式指定配置文件,则
// 会去上面的门路里找文件名 `cui-config` 的文件 name of config file (without extension)
// 依照 []string{"json", "toml", "yaml", "yml", "properties", "props", "prop", "hcl", "tfvars", "dotenv", "env", "ini"}的程序(竟然还反对 Java 用的 properties)
viper.SetConfigName("cui-config")
viper.AddConfigPath("/etc/myapp") // 找寻的门路
viper.AddConfigPath("$HOME/.myapp/")
viper.AddConfigPath(".")
}
err := viper.ReadInConfig()
if err != nil {panic(fmt.Errorf("error reading config: %s", err))
}
fmt.Printf("到底用的是哪个配置文件:'%s'\n", viper.ConfigFileUsed())
fmt.Printf("Global.Source 这个字段的值为:'%s'\n", viper.GetString("global.source"))
}
输入:
到底用的是哪个配置文件: '/Users/fliter/config-demo/cui-config.yaml'
Global.Source 这个字段的值为: '优先级最高'
验证一下 环境变量 的优先级高于 配置文件
package main
import (
"fmt"
"github.com/spf13/viper"
)
func main() {loadConfig()
}
func loadConfig() {
configVar := "shuang-config.yaml"
configVar = "" // 这行如果正文掉,则从指定的 configVar 读取配置文件;否则就各种条件去找了
viper.Set("Global.Source", "优先级最高")
viper.AutomaticEnv()
if configVar != "" {
// SetConfigFile 显式定义配置文件的门路、名称和扩展名。// Viper 将应用它而不查看任何配置门路。viper.SetConfigFile(configVar)
} else {
// 如果没有显式指定配置文件,则
// 会去上面的门路里找文件名 `cui-config` 的文件 name of config file (without extension)
// 依照 []string{"json", "toml", "yaml", "yml", "properties", "props", "prop", "hcl", "tfvars", "dotenv", "env", "ini"}的程序(竟然还反对 Java 用的 properties)
viper.SetConfigName("cui-config")
viper.AddConfigPath("/etc/myapp") // 找寻的门路
viper.AddConfigPath("$HOME/.myapp/")
viper.AddConfigPath(".")
}
err := viper.ReadInConfig()
if err != nil {panic(fmt.Errorf("error reading config: %s", err))
}
fmt.Printf("到底用的是哪个配置文件:'%s'\n", viper.ConfigFileUsed())
fmt.Printf("LANG 这个字段的值为:'%s'\n", viper.GetString("LANG"))
}
viper.AutomaticEnv()会绑定所有环境变量,
如果只心愿绑定特定的,能够应用 SetEnvPrefix(“global.source”, “MYAPP_GLOAL_SOURCE”),留神这个函数不会主动加上 MYAPP 的前缀.
验证一下 命令行参数的优先级高于 配置文件
viper 能够配合 pflag 来应用,pflag 能够了解为规范库 flag 的一个增强版,viper 能够绑定到 pflag 上
和 cobra,viper 一样,pflag 也是同一作者的作品
验证一下 默认值的优先级低于 配置文件
package main
import (
"fmt"
"github.com/spf13/viper"
)
func main() {loadConfig()
}
func loadConfig() {
configVar := "shuang-config.yaml"
configVar = "" // 这行如果正文掉,则从指定的 configVar 读取配置文件;否则就各种条件去找了
//viper.Set("Global.Source", "优先级最高")
viper.AutomaticEnv()
viper.SetDefault("Global.Source", "优先级最低")
if configVar != "" {
// SetConfigFile 显式定义配置文件的门路、名称和扩展名。// Viper 将应用它而不查看任何配置门路。viper.SetConfigFile(configVar)
} else {
// 如果没有显式指定配置文件,则
// 会去上面的门路里找文件名 `cui-config` 的文件 name of config file (without extension)
// 依照 []string{"json", "toml", "yaml", "yml", "properties", "props", "prop", "hcl", "tfvars", "dotenv", "env", "ini"}的程序(竟然还反对 Java 用的 properties)
viper.SetConfigName("cui-config")
viper.AddConfigPath("/etc/myapp") // 找寻的门路
viper.AddConfigPath("$HOME/.myapp/")
viper.AddConfigPath(".")
}
err := viper.ReadInConfig()
if err != nil {panic(fmt.Errorf("error reading config: %s", err))
}
fmt.Printf("到底用的是哪个配置文件:'%s'\n", viper.ConfigFileUsed())
fmt.Printf("Global.Source 这个字段的值为:'%s'\n", viper.GetString("Global.Source"))
}
Watch 机制(配置更新后 热加载)
该机制能够监听配置文件的批改, 这样就实现了热加载,批改配置后,无需重启服务
- 对于本地文件,是通过
fsnotify
实现的,而后通过一个回调函数去告诉利用来 reload; - 对于 Remote KV Store,目前只反对 etcd,做法比拟 ugly,(5 秒钟)轮询一次 而不是 watch api
package main
import (
"fmt"
"time"
"github.com/fsnotify/fsnotify"
"github.com/spf13/viper"
)
func main() {loadConfig()
}
func loadConfig() {
configVar := "shuang-config.yaml"
configVar = "" // 这行如果正文掉,则从指定的 configVar 读取配置文件;否则就各种条件去找了
if configVar != "" {
// SetConfigFile 显式定义配置文件的门路、名称和扩展名。// Viper 将应用它而不查看任何配置门路。viper.SetConfigFile(configVar)
} else {
// 如果没有显式指定配置文件,则
// 会去上面的门路里找文件名 `cui-config` 的文件 name of config file (without extension)
// 依照 []string{"json", "toml", "yaml", "yml", "properties", "props", "prop", "hcl", "tfvars", "dotenv", "env", "ini"}的程序(竟然还反对 Java 用的 properties)
viper.SetConfigName("cui-config")
viper.AddConfigPath("/etc/myapp") // 找寻的门路
viper.AddConfigPath("$HOME/.myapp/")
viper.AddConfigPath(".")
}
viper.WatchConfig()
viper.OnConfigChange(func(e fsnotify.Event) {fmt.Printf("配置文件 %s 产生了更改!!! 最新的 Global.Source 这个字段的值为 %s:", e.Name, viper.GetString("Global.Source"))
})
err := viper.ReadInConfig()
if err != nil {panic(fmt.Errorf("error reading config: %s", err))
}
fmt.Printf("到底用的是哪个配置文件:'%s'\n", viper.ConfigFileUsed())
fmt.Printf("Global.Source 这个字段的值为:'%s'\n", viper.GetString("Global.Source"))
time.Sleep(10000e9)
}
Go viper 配置文件读取工具
动静获取配置文件(viper)
configor
Configor: 一个 Golang 配置工具,反对 YAML,JSON,TOML,Shell 环境,反对热加载
出自 jinzhu 大佬
package main
import (
"fmt"
"github.com/jinzhu/configor"
)
type Config struct {
APPName string `default:"app name"`
DB struct {
Name string
User string `default:"root"`
Password string `required:"true" env:"DBPassword"`
Port uint `default:"3306"`
}
Contacts []struct {
Name string
Email string `required:"true"`
}
}
func main() {var conf = Config{}
err := configor.Load(&conf, "config.yml")
// err := configor.New(&configor.Config{Debug: true}).Load(&conf, "config.yml") // 测试模式,也能够通过环境变量开启测试模式(CONFIGOR_DEBUG_MODE=true go run main.go),这样就无需批改代码
//err := configor.New(&configor.Config{Verbose: true}).Load(&conf, "config.yml") // 模式,也能够通过环境变量开启具体模式(CONFIGOR_VERBOSE_MODE=true go run main.go),这样就无需批改代码
if err != nil {panic(err)
}
fmt.Printf("%v \n", conf)
}
开启 测试模式 or 具体模式
既能够在代码中显式开启,如 err := configor.New(&configor.Config{Debug: true}).Load(&conf, "config.yml")
也能够通过环境变量开启,如 CONFIGOR_DEBUG_MODE=true go run main.go
加载多个配置文件
// application.yml 的优先级 大于 database.json, 排在后面的配置文件优先级大于排在后的的配置
configor.Load(&Config, "application.yml", "database.json")
依据环境变量加载配置文件 or 从 shell 加载配置项
具体可参考 Golang Configor 配置文件工具
热更新
package main
import (
"fmt"
"time"
"github.com/jinzhu/configor"
)
type Config struct {
APPName string `default:"app name"`
DB struct {
Name string
User string `default:"root"`
Password string `required:"true" env:"DBPassword"`
Port uint `default:"3306"`
}
Contacts []struct {
Name string
Email string `required:"true"`
}
}
func main() {var conf = Config{}
// reload 模式, 可实现热加载
err := configor.New(&configor.Config{
AutoReload: true,
AutoReloadInterval: time.Second,
AutoReloadCallback: func(config interface{}) {
// config 发生变化后登程什么操作
fmt.Printf("配置文件产生了变更 %#v\n", config)
},
}).Load(&conf, "config.yml")
// 无 reload 模式
//err := configor.Load(&conf, "config.yml")
// err := configor.New(&configor.Config{Debug: true}).Load(&conf, "config.yml") // 测试模式,也能够通过环境变量开启测试模式(CONFIGOR_DEBUG_MODE=true go run main.go),这样就无需批改代码
//err := configor.New(&configor.Config{Verbose: true}).Load(&conf, "config.yml") // 模式,也能够通过环境变量开启具体模式(CONFIGOR_VERBOSE_MODE=true go run main.go),这样就无需批改代码
if err != nil {panic(err)
}
fmt.Printf("%v \n", conf)
time.Sleep(100000e9)
}
残缺代码
本文由 mdnice 多平台公布