# Day14 Gin框架之jwt与viper
# 配置加载概述
- 主流的配置文件格式:toml、yaml、json、xml、ini、(conf、properties)
- 配置的来源:文本文件、环境变量、配置中心(携程开源的apollo配置中心、nacos)
- 支持配置热加载
# viper 配置工具
Viper完整的配置解决方案,可以处理所有的配置需求和格式
- 安装依赖
go get github.com/spf13/viper
- 基本使用 三步走
package main
import (
"fmt"
"github.com/spf13/viper"
)
func main() {
// 1.指定配置文件来源
viper.SetConfigFile("./config.yaml") // 指定配置文件路径, 可以用这个更明确些
viper.AddConfigPath("/etc/appname/") // 查找配置文件所在的路径
viper.AddConfigPath("$HOME/.appname") // 多次调用以添加多个搜索路径
viper.AddConfigPath(".") // 还可以在工作目录中查找配置
viper.SetConfigName("config") // 配置文件名称(无扩展名),同名时优先json格式
viper.SetConfigType("yaml") // 如果配置文件的名称中没有扩展名,则不需要配置此项
// 2.从配置文件读取配置信息
if err := viper.ReadInConfig(); err != nil {
fmt.Println("viper.ReadInConfig error:", err)
}
// 3. 使用配置
fmt.Println(viper.Get("port").(int)) // Get返回空接口类型,需要断言
fmt.Println(viper.GetInt("port")) // GetInt直接返回int类型,默认0
fmt.Println(viper.Get("version"))
fmt.Println(viper.GetString("version"))
}
- 监控并重新读取配置文件
确保在调用WatchConfig()
之前添加了所有的配置路径
viper.WatchConfig()
viper.OnConfigChange(func(e fsnotify.Event) {
// 配置文件发生变更之后会调用的回调函数
fmt.Println("Config file changed:", e.Name)
})
- 远程Key/Value存储支持
// 远程需要匿名导入这包
import _ "github.com/spf13/viper/remote"
// ETCD
viper.AddRemoteProvider("etcd", "http://127.0.0.1:4001","/config/config.json")
viper.SetConfigType("json") // 因为在字节流中没有文件扩展名,所以这里需要设置下类型。支持的扩展名有 "json", "toml", "yaml", "yml", "properties", "props", "prop", "env", "dotenv"
err := viper.ReadRemoteConfig()
// Consul 先设置一个KEY MY_CONSUL_KEY
viper.AddRemoteProvider("consul", "localhost:8500", "MY_CONSUL_KEY")
viper.SetConfigType("json") // 需要显示设置成json
err := viper.ReadRemoteConfig()
- 监控etcd中的更改-未加密
// 或者你可以创建一个新的viper实例
var runtime_viper = viper.New()
runtime_viper.AddRemoteProvider("etcd", "http://127.0.0.1:4001", "/config/hugo.yml")
runtime_viper.SetConfigType("yaml") // 因为在字节流中没有文件扩展名,所以这里需要设置下类型。支持的扩展名有 "json", "toml", "yaml", "yml", "properties", "props", "prop", "env", "dotenv"
// 第一次从远程读取配置
err := runtime_viper.ReadRemoteConfig()
// 反序列化
runtime_viper.Unmarshal(&runtime_conf)
// 开启一个单独的goroutine一直监控远端的变更
go func(){
for {
time.Sleep(time.Second * 5) // 每次请求后延迟一下
// 目前只测试了etcd支持
err := runtime_viper.WatchRemoteConfig()
if err != nil {
log.Errorf("unable to read remote config: %v", err)
continue
}
// 将新配置反序列化到我们运行时的配置结构体中。你还可以借助channel实现一个通知系统更改的信号
runtime_viper.Unmarshal(&runtime_conf)
}
}()
# viper 实时监控
package main
import (
"fmt"
"time"
"github.com/fsnotify/fsnotify"
"github.com/spf13/viper"
)
func main() {
viper.SetConfigFile("./config.yaml") // 指定配置文件路径, 可以用这个更明确些
if err := viper.ReadInConfig(); err != nil {
fmt.Println("viper.ReadInConfig error:", err)
}
// 思路1:协程循环监控
go func() {
for {
time.Sleep(2 * time.Second)
viper.WatchConfig()
}
}()
// 思路2:时间循环
viper.WatchConfig()
viper.OnConfigChange(func(e fsnotify.Event) {
// 配置文件发生变更之后会调用的回调函数
fmt.Println("Config file changed:", e.Name)
})
for {
time.Sleep(2 * time.Second)
fmt.Println(viper.Get("version"))
}
}
# viper 获取值
每一个Get方法在找不到值的时候都会返回零值,IsSet()
检查给定的键是否存在
Get(key string) : interface{}
GetBool(key string) : bool
GetFloat64(key string) : float64
GetInt(key string) : int
GetIntSlice(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
AllSettings() : map[string]interface{}
- 访问嵌套的键
- 提取子树
- 反序列化
# viper 实时加载示例
// ./conf/config.yaml
version: "v1.0.0"
port: 8090
package main
import (
"fmt"
"net/http"
"github.com/fsnotify/fsnotify"
"github.com/gin-gonic/gin"
"github.com/spf13/viper"
)
// 为了其他包也能访问,大写
type Config struct {
Verison string `mapstructure:"version"`
Port int `mapstructure:"port"`
}
var Conf = new(Config)
func main() {
viper.SetConfigFile("./conf/config.yaml") // 指定配置文件路径
err := viper.ReadInConfig() // 读取配置信息
if err != nil { // 读取配置信息失败
panic(fmt.Errorf("fatal error config file: %s", err))
}
// 将读取的配置信息保存至全局变量Conf
if err := viper.Unmarshal(Conf); err != nil {
panic(fmt.Errorf("unmarshal conf failed, err:%s", err))
}
// 监控配置文件变化
viper.WatchConfig()
// 注意!!!配置文件发生变化后要同步到全局变量Conf
viper.OnConfigChange(func(in fsnotify.Event) {
if err := viper.Unmarshal(Conf); err != nil {
panic(fmt.Errorf("unmarshal conf failed, err:%s", err))
}
})
r := gin.Default()
// 访问/version的返回值会随配置文件的变化而变化
r.GET("/version", func(c *gin.Context) {
c.JSON(http.StatusOK, gin.H{
"version": Conf.Verison,
})
})
if err := r.Run(fmt.Sprintf(":%d", Conf.Port)); err != nil {
panic(err)
}
}