为了更好的集中管理项目的配置文件,引入了viper。git地址 https://github.com/spf13/viper
什么是viper,官方解释:
Viper is a complete configuration solution for Go applications including 12-Factor apps. It is designed to work within an application, and can handle all types of configuration needs and formats. It supports:
- setting defaults
- reading from JSON, TOML, YAML, HCL, and Java properties config files
- live watching and re-reading of config files (optional)
- reading from environment variables
- reading from remote config systems (etcd or Consul), and watching changes
- reading from command line flags
- reading from buffer
- setting explicit values
Viper can be thought of as a registry for all of your applications configuration needs.
主要api:
Get(key string) : interface{}
GetBool(key string) : bool
GetFloat64(key string) : float64
GetInt(key string) : int
GetString(key string) : string
GetStringMap(key string) : map[string]interface{}
GetStringMapString(key string) : map[string]string
GetStringSlice(key string) : []string
GetTime(key string) : time.Time
GetDuration(key string) : time.Duration
IsSet(key string) : bool
viper是国外大牛写的一个牛逼的配置管理库,支持的语言的也比较多,JSON, TOML, YAML, HCL, Java properties都可以,而且还能远程读取配置,这个做微服务配置中心挺合适。
这里我还是用yaml来写配置参数。
安装
go get github.com/spf13/viper
主要步骤
- 写好配置文件
- 设置viper查找的文件目录
- 设置配置文件名称
- 读取参数
实现步骤
首先我们创建目录放配置文件和程序。
yaml文件夹里面是配置文件,param里面是参数模块。
我添加了两个参数模块,分别是应用参数和数据源参数,我们看一下application.yaml里面的参数:
application:
mode: debug #gin的启动模式, debug or release or test
server:
port: 8080
datasource:
#多数据源使用数据源名称做主键保存到参数列表
user: #key
dialect: mysql
url: jdbc:mysql://localhost:3306/base_user?allowMultiQueries=true&useUnicode=true&characterEncoding=UTF-8&useSSL=true
username: root
password: root
maxIdle: 200
maxOpen: 50
admin: #key
dialect: mysql
url: jdbc:mysql://localhost:3306/base_admin?allowMultiQueries=true&useUnicode=true&characterEncoding=UTF-8&useSSL=true
username: root
password: root
maxIdle: 200
maxOpen: 50
InitConfig.go是参数配置的入口,其init在main导入config包后调用,InitConfig.go内容如下
type ConfigParam struct {
App AppParam //应用参数
Dbs DbParam //数据源参数
}
var Config ConfigParam
func init(){
fmt.Println("[init config params]......")
viper.SetConfigType("yaml")
viper.SetConfigName("application") // name of config file (without extension)
viper.AddConfigPath("./config/yaml") // 配置文件路径,多次使用可以查找多个目录
err := viper.ReadInConfig() // Find and read the config file
if err != nil { // Handle errors reading the config file
panic(fmt.Errorf("Fatal error config file: %s \n", err))
return
}
//加载配置
Config.App.ReadConfig()
Config.Dbs.ReadConfig()
fmt.Println("[end init config params]......")
}
里面创建了个全局参数Config供应用调用。Config里面是两个模块的参数。
init里面首先设置了配置文件名称和路径,然后调用参数模块的ReadConfig方法加载参数。
先看AppParam的加载
type server struct {
Mode string
Port string
}
type AppParam struct {
server
}
func (p *AppParam)ReadConfig(){
//default config
viper.SetDefault("application.mode", "debug")
viper.SetDefault("application.server.port","8080")
//read properties
p.Mode = viper.GetString("application.mode")
//注意server的port数字前面有个冒号
p.Port = ":" + viper.GetString("application.server.port")
fmt.Println("[Init AppParam Over]...", p)
}
这个比较简单,直接通过viper的方法读取配置文件里面的字符串。这里我们先给两个参数设置了默认值,当配置文件里面没有找到参数时viper会自动加载默认值。
然后是DbParam的加载
type datasource struct {
Dialect string
Url string
Username string
Password string
MaxIdle int
MaxOpen int
}
//多数据源支持,map[name]= db
type DbParam struct {
//数据源数量
Cnt int
//数据源map
Source map[string]datasource
}
func (p *DbParam) ReadConfig() {
//default config
//viper.SetDefault("ContentDir", "content")
//viper.SetDefault("ContentDir", "content")
p.Source = make(map[string]datasource)
err := viper.UnmarshalKey("datasource", &p.Source)
if err != nil {
panic(err)
return
}
p.Cnt = len(p.Source)
fmt.Println("[Init DbParam Over]...", p)
}
viper内置了类似反序列化的工具,实际使用的是mapstructure这个库,主要功能就是把map转struct。
Unmarshal(rawVal interface{}) : error
UnmarshalKey(key string, rawVal interface{}) : error
type config struct { Port int Name string PathMap string `mapstructure:"path_map"` } var C config err := Unmarshal(&C) if err != nil { t.Fatalf("unable to decode into struct, %v", err) }
这里把map转struct后保存到DbParam.source里面,使用“name”做数据源的主键, 然后就可以在其他地方使用参数了。
main函数里面使用参数
func main() {
fmt.Println("[Server Starting]...")
gin.SetMode(Config.App.Mode)
//注册路由
router := routers.InitRouter()
//启动服务器
router.Run(Config.App.Port)
log.Println("Server stopped")
//defer models.DbClose()
os.Exit(0)
}
启动程序,查看打印的参数
[Init AppParam Over]... &{{debug :8080}}
[Init DbParam Over]... &{2 map[user:{mysql jdbc:mysql://localhost:3306/base_user?allowMultiQueries=true&useUnicode=true&characterEncoding=UTF-8&useSSL=true root root 200 50} admin:{mysql jdbc:mysql://localhost:3306/base_admin?allowMultiQueries=true&useUnicode=true&characterEncoding=UTF-8&useSSL=true root root 200 50}]}
[end init config params]......