# Day14 Gin框架之jwt与viper

# 配置加载概述

  1. 主流的配置文件格式:toml、yaml、json、xml、ini、(conf、properties)
  2. 配置的来源:文本文件、环境变量、配置中心(携程开源的apollo配置中心、nacos)
  3. 支持配置热加载

# 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)
	}
}
上次更新: 12/12/2022, 8:55:46 PM