目录
配置模块化
Viper
Viper的功能
Viper配置读取顺序
配置文件解析
新建配置文件
解析配置
主文件读取配置
主函数中增加配置初始化入口
目录
配置模块化
Viper
Viper的功能
Viper配置读取顺序
配置文件解析
新建配置文件
解析配置
主文件读取配置
配置模块化
为什么需要配置文件
- 代码中对于一些可能会变化的内容,采用硬编码不便于维护
- 如:监听的IP和端口
- 如:数据库服务器地址和端口
- 如:当前运行的模式(开发/测试)
- ...
Viper
Viper的功能
Viper是国外大神spf13编写的开源配置解决方案,具有如下特性
- 设置默认值
- 可以读取如下格式的配置文件:JSON、TOML、YAML、HCL
- 监控配置文件改动,并热加载配置文件
- 支持直接设置配置项的值
- 从环境变量读取配置
- 从远程配置中心读取配置(etc/consul),并监控变动
- etcd是一个分布式一致性的key-value存储技术,被用来做配置共享和服务发现
- 从命令行flag读取配置
- 从缓存中读取配置
Viper配置读取顺序
Viper可以同时从不同的位置读取配置,不同位置的配置具有不同的优先级,高优先级的配置会覆盖低优先级相同的配置,按优先级从高到低排列如下
- 通过viper.Set函数显示设置的配置
- 命令行参数
- 环境变量
- 配置文件
- Key/Value存储
- 默认值
配置文件解析
apiserver采用YAML格式的配置文件,采用YAML格式是因为YAML表达的格式更丰富,可读性更强
在初始化配置文件后,Viper也可以非常方便地读取多个层级的配置
读取配置只需要调用viper.GetString()、viper.GetInt()和viper.GetBool()等函数即可
新建配置文件
在apiserver/conf/config.yaml(默认配置文件名字固定为config.yaml)
runmode: debug # 开发模式, debug, release, test addr: :8000 # HTTP绑定端⼝ name: apiserver # API Server的名字 url: http://127.0.0.1:8000 # pingServer函数请求的API服务器的ip:port max_ping_count: 10 # pingServer函数try的次数
解析配置
在apiserver/config/config.go
main 函数通过 config.Init 函数来解析并 watch 配置⽂件
package config
import (
"log"
"strings"
"github.com/fsnotify/fsnotify"
"github.com/spf13/viper"
)
// 定义Config结构体,Name为配置⽂件名
type Config struct {
Name string
}
// 为结构体添加initConfig⽅法 => 初始化配置⽂件
// 函数的返回值当出错时,返回err;当没有出错时,返回nil(err的零值)
func (c *Config) initConfig() error {
if c.Name != "" {
// 如果指定了配置⽂件,则解析指定的配置⽂件
viper.SetConfigFile(c.Name)
} else {
// 如果没有指定配置⽂件,则解析默认的配置⽂件(conf/config)
viper.AddConfigPath("conf")
viper.SetConfigName("config")
}
// 设置配置⽂件格式为YAML
viper.SetConfigType("yaml")
// 读取匹配的环境变量
viper.AutomaticEnv()
// 读取环境变量的前缀为APISERVER
viper.SetEnvPrefix("APISERVER")
// 设置环境变量的读取规则,把点号('.')映射成下划线('_')
replacer := strings.NewReplacer(".", "_")
viper.SetEnvKeyReplacer(replacer)
// viper解析配置⽂件
if err := viper.ReadInConfig(); err != nil {
return err
}
return nil
}
// 监控配置⽂件变化并热加载程序
// 通过该函数的 viper 设置,可以使 viper 监控配置⽂件变更,如有变更则热更新程序
// 所谓热更新是指:可以不重启 API 进程,使 API 加载最新配置项的值
func (c *Config) watchConfig() {
viper.WatchConfig()
viper.OnConfigChange(func(e fsnotify.Event) {
log.Printf("Config file changed: %s", e.Name)
})
}
func Init(cfg string) error {
c := Config{
Name: cfg,
}
// 初始化配置⽂件
if err := c.initConfig(); err != nil {
return err
}
// 监控配置⽂件变化并热加载程序
c.watchConfig()
return nil
}
主文件读取配置
主函数中增加配置初始化入口
- 在main.go文件中导入模块
- "github.com/spf13/pflag"
- "github.com/spf13/viper"
- "apiserver/config"
- 在main.go文件中导入模块
var (
// pflag⽤于处理命令⾏参数
// 定义命令⾏参数对应的变量
// 参数1:配置名,参数2:配置选项,参数3:值,参数4:⽤法说明
// ex: ./apiserver -c config.yaml
cfg = pflag.StringP("config", "c", "", "apiserver config file path.")
)
func main() {
// 解析命令⾏传过来的参数(如果不解析或没有指定就相当于使⽤默认值conf/config.yaml)
pflag.Parse()
// init config => 初始化配置
if err := config.Init(*cfg); err != nil {
panic(err)
}
// Set gin mode.=> 设置项⽬模式
gin.SetMode(viper.GetString("runmode"))
....
}
替换原本硬编码的内容
- 在main.go文件中导入模块
- "github.com/spf13/pflag"
- "github.com/spf13/viper"
- "apiserver/config"
- 在main.go文件中导入模块
var (
// pflag⽤于处理命令⾏参数
// 定义命令⾏参数对应的变量
// 参数1:配置名,参数2:配置选项,参数3:值,参数4:⽤法说明
// ex: ./apiserver -c config.yaml
cfg = pflag.StringP("config", "c", "", "apiserver config file path.")
)
func main() {
// 解析命令⾏传过来的参数(如果不解析或没有指定就相当于使⽤默认值conf/config.yaml)
pflag.Parse()
// init config => 初始化配置
if err := config.Init(*cfg); err != nil {
panic(err)
}
// Set gin mode.=> 设置项⽬模式
gin.SetMode(viper.GetString("runmode"))
....
}
替换原本硬编码的内容
在main函数中将相应的配置改成从配置文件读取,需要替换的配置见下图中红框部分
// apiserver/main.go
// 调用viper.GetString() viper.GetInt() viper.GetBool()
替换后代码为
运行启动apiserver- 解决依赖 go mod tidy
- 运行代码,启动apiserver后端口为配置文件中指定的端口
修改:apiserver/router/router.go
加载:import "github.com/spf13/viper"
添加



