一、需求
1. 本地开发读取yaml中的配置,并序列化成对象
2. 支持线上环境配置env
3. 本地环境和线上环境无缝衔接不需要修改代码
二、viper实践
1. viper文档
2. 配置本地yaml
settings:
application:
# dev开发环境 test测试环境 prod线上环境
mode: dev
# 服务器ip,默认使用 0.0.0.0
host: 0.0.0.0
# 服务名称
name: DemoApp
# 服务端口号
port: 8000
# 读超时(s)
readtimeout: 10
# 写超时(s)
writertimeout: 2
# 数据权限功能开关
enabledp: false
3. 封装config对象,并加载配置
代码如下(示例):
package config
import (
"fmt"
"github.com/spf13/viper"
"strings"
)
var ApplicationConfig = new(Application)
type Application struct {
ReadTimeout int
WriterTimeout int
Host string
Port int64
Name string
Mode string
DemoMsg string
}
// Config 配置集合
type Config struct {
Application *Application `yaml:"application"`
}
type Settings struct {
Settings Config `yaml:"settings"`
}
var _cfg *Settings
// Setup 载入配置文件
func Setup(configFile string, fs ...func()) {
_cfg = &Settings{
Settings: Config{
Application: ApplicationConfig,
},
}
v := viper.New()
//自动获取全部的env加入到viper中。(如果环境变量多就全部加进来)默认别名和环境变量名一致
v.AutomaticEnv()
//将加入的环境变量*_*_格式替换成 *.*格式
//(因为从环境变量读是按"a.b.c.d"的格式读取,所以要给在viper维护一个别名对象,给环境变量一个别名)
v.SetEnvKeyReplacer(strings.NewReplacer(".", "_"))
// 配置文件位置
v.SetConfigFile(configFile)
//支持 "yaml", "yml", "json", "toml", "hcl", "tfvars", "ini", "properties", "props", "prop", "dotenv", "env":
v.SetConfigType("yaml")
//读文件到viper配置中
err := v.ReadInConfig()
if err != nil {
panic(fmt.Errorf("Fatal error config file: %s \n", err))
}
// 系列化成config对象
if err := v.Unmarshal(&_cfg); err != nil {
fmt.Println(err)
}
fmt.Println("所有的key: ")
for _,k := range v.AllKeys(){
fmt.Println(k)
}
fmt.Println("所有的config: \n",v.AllSettings())
}
4. 单元测试
代码如下(示例):
import (
"encoding/json"
"fmt"
"github.com/spf13/viper"
"os"
"strings"
"testing"
)
func init() {
os.Setenv("SETTINGS_APPLICATION_MODE","test")
os.Setenv("SETTINGS_APPLICATION_HOST","127.0.0.10")
os.Setenv("SETTINGS_APPLICATION_NAME","APP")
os.Setenv("SETTINGS_APPLICATION_PORT","9001")
os.Setenv("SETTINGS_APPLICATION_READTIMEOUT","1")
os.Setenv("SETTINGS_APPLICATION_ENABLEDP","true")
}
func TestSetup(t *testing.T) {
Setup("app_settings.yaml")
marshal, err := json.Marshal(_cfg)
if err != nil {
fmt.Println(err)
}
fmt.Println("序列化config: \n",string(marshal))
}
执行结果:
=== RUN TestSetup
所有的key:
settings.application.enabledp
settings.application.mode
settings.application.host
settings.application.name
settings.application.port
settings.application.readtimeout
settings.application.writertimeout
所有的config:
map[settings:map[application:map[enabledp:true host:127.0.0.10 mode:test name:APP port:9001 readtimeout:1 writertimeout:2]]]
序列化config:
{"Settings":{"Application":{"ReadTimeout":1,"WriterTimeout":2,"Host":"127.0.0.10","Port":9001,"Name":"APP","Mode":"test","DemoMsg":""}}}
--- PASS: TestViperSettings (0.00s)
PASS
总结
通过实践,viper能够满足本地环境和线上环境的无缝切换,自动识别环境变量。
1. yaml + env 思路
- 从yaml中读取配置,主要是为了序列化出要用到的参数。
- 使用v.AutomaticEnv(),自动加载出环境变量到viper。
- 将环境变量替换成掉yaml中读取的值。(注意:LoopEnv 环境变量如果存在就返回,如果不存在则用下一个优先级的值。)
2. viper配置读取的优先级
- Viper将首先检查别名是否存在。
- 然后按默认读取优先级别检查:flag > env > config file, key/value store.
- 最后,如果没有找到值并且flagDefault为true,并且键对应于一个标志,则返回标志的默认值。