不做大哥好多年 不做大哥好多年
首页
  • MySQL
  • Redis
  • Elasticsearch
  • Kafka
  • Etcd
  • MongoDB
  • TiDB
  • RabbitMQ
  • 01.Python
  • 02.GO
  • 03.Java
  • 04.业务问题
  • 05.关键技术
  • 06.项目常识
  • 10.计算机基础
  • Docker
  • K8S
  • 容器原理
  • Istio
  • 01.GO基础
  • 02.面向对象
  • 03.并发编程
  • 04.常用库
  • 05.数据库操作
  • 06.Beego框架
  • 07.Beego商城
  • 08.GIN框架
  • 09.GIN论坛
  • 10.微服务
  • 01.Python基础
  • 02.Python模块
  • 03.Django
  • 04.Flask
  • 05.SYL
  • 06.Celery
  • 10.微服务
  • 01.Java基础
  • 02.面向对象
  • 03.Java进阶
  • 04.Web基础
  • 05.Spring框架
  • 100.微服务
  • 数据结构
  • 算法基础
  • 算法题分类
  • 前置知识
  • PyTorch
  • Linux基础
  • Linux高级
  • Nginx
  • KeepAlive
  • ansible
  • zabbix
  • Shell
  • Linux内核

逍遥子

不做大哥好多年
首页
  • MySQL
  • Redis
  • Elasticsearch
  • Kafka
  • Etcd
  • MongoDB
  • TiDB
  • RabbitMQ
  • 01.Python
  • 02.GO
  • 03.Java
  • 04.业务问题
  • 05.关键技术
  • 06.项目常识
  • 10.计算机基础
  • Docker
  • K8S
  • 容器原理
  • Istio
  • 01.GO基础
  • 02.面向对象
  • 03.并发编程
  • 04.常用库
  • 05.数据库操作
  • 06.Beego框架
  • 07.Beego商城
  • 08.GIN框架
  • 09.GIN论坛
  • 10.微服务
  • 01.Python基础
  • 02.Python模块
  • 03.Django
  • 04.Flask
  • 05.SYL
  • 06.Celery
  • 10.微服务
  • 01.Java基础
  • 02.面向对象
  • 03.Java进阶
  • 04.Web基础
  • 05.Spring框架
  • 100.微服务
  • 数据结构
  • 算法基础
  • 算法题分类
  • 前置知识
  • PyTorch
  • Linux基础
  • Linux高级
  • Nginx
  • KeepAlive
  • ansible
  • zabbix
  • Shell
  • Linux内核
  • GO基础

  • 面向对象

  • 并发编程

  • 常用库

    • 01.fmt
    • 02.Time包
    • 03.encoding/json包
    • 04.Flag
    • 05.Log
    • 06.IO操作
    • 07.Strconv
    • 08.net/http
    • 09.Context
    • 10.反射
    • 11.zap日志包
    • 12.viper配置管理
      • 01.viper介绍
        • 1.1 viper是什么?
        • 1.2 为什么选择Viper?
        • 1.3 viper安装
      • 02.viper设置配置
        • 2.1 建立默认值
        • 2.2 读取配置文件
        • 2.3 写入配置文件
        • 2.4 监控并重新读取配置文件
        • 2.4 覆盖设置
      • 03.viper读取配置
        • 3.1 几种访问值的方法
        • 3.2 访问嵌套的键
        • 3.3 提取子树
        • 3.4 反序列化
      • 04.使用Viper示例
        • 4.1 ./conf/config.yaml
        • 4.2 gin中使用viper案例
        • 4.3 结构体变量保存配置
    • 13.gRPC
    • 14.jwt-go
    • 15.go-wrk压测
    • 16.Makefile
    • 17.cobor
    • 18.logrus
    • 19.cron定时
    • 20.machinery
    • 21.regexp2
    • 22.lo
    • 23.泛型
    • 24.pprof
  • 数据库操作

  • Beego框架

  • Beego商城

  • GIN框架

  • GIN论坛

  • 微服务

  • 设计模式

  • Go
  • 常用库
xiaonaiqiang
2021-06-08
目录

12.viper配置管理

# 01.viper介绍

  • 参考博客 (opens new window)

# 1.1 viper是什么?

  • Viper (opens new window)是适用于Go应用程序的完整配置解决方案。

  • 它被设计用于在应用程序中工作,并且可以处理所有类型的配置需求和格式

  • viper功能

    • 设置默认值
    • 从JSON、TOML、YAML、HCL、envfile和Java properties格式的配置文件读取配置信息
    • 实时监控和重新读取配置文件(可选)
    • 从环境变量中读取
    • 从远程配置系统(etcd或Consul)读取并监控配置变化
    • 从命令行参数读取配置
    • 从buffer读取配置
    • 显式配置值

# 1.2 为什么选择Viper?

  • 在构建现代应用程序时,你无需担心配置文件格式;

  • Viper能够为你执行下列操作:

    • 查找、加载和反序列化JSON、TOML、YAML、HCL、INI、envfile和Java properties格式的配置文件。
    • 提供一种机制为你的不同配置选项设置默认值。
    • 提供一种机制来通过命令行参数覆盖指定选项的值。
    • 提供别名系统,以便在不破坏现有代码的情况下轻松重命名参数。
    • 当用户提供了与默认值相同的命令行或配置文件时,可以很容易地分辨出它们之间的区别。
  • Viper会按照下面的优先,每个项目的优先级都高于它下面的项目

    • 显示调用Set设置值
    • 命令行参数(flag)
    • 环境变量
    • 配置文件
    • key/value存储
    • 默认值
  • 重要: 目前Viper配置的键(Key)是大小写不敏感的

# 1.3 viper安装

go get github.com/spf13/viper
1

# 02.viper设置配置

# 2.1 建立默认值

  • 一个好的配置系统应该支持默认值。

  • 键不需要默认值,但如果没有通过配置文件、环境变量、远程配置或命令行标志(flag)设置键,则默认值非常有用。

  • 例如:

viper.SetDefault("ContentDir", "content")
viper.SetDefault("LayoutDir", "layouts")
viper.SetDefault("Taxonomies", map[string]string{"tag": "tags", "category": "categories"})
1
2
3

# 2.2 读取配置文件

  • Viper需要最少知道在哪里查找配置文件的配置。
  • Viper支持JSON、TOML、YAML、HCL、envfile和Java properties格式的配置文件。
  • Viper可以搜索多个路径,但目前单个Viper实例只支持单个配置文件。
viper.SetConfigFile("./config.yaml") // 指定配置文件路径
viper.SetConfigName("config") // 配置文件名称(无扩展名)
viper.SetConfigType("yaml") // 如果配置文件的名称中没有扩展名,则需要配置此项
viper.AddConfigPath("/etc/appname/")   // 查找配置文件所在的路径
viper.AddConfigPath("$HOME/.appname")  // 多次调用以添加多个搜索路径
viper.AddConfigPath(".")               // 还可以在工作目录中查找配置
err := viper.ReadInConfig() // 查找并读取配置文件
if err != nil { // 处理读取配置文件的错误
	panic(fmt.Errorf("Fatal error config file: %s \n", err))
}
1
2
3
4
5
6
7
8
9
10

# 2.3 写入配置文件

  • 从配置文件中读取配置文件是有用的,但是有时你想要存储在运行时所做的所有修改。
  • 为此,可以使用下面一组命令,每个命令都有自己的用途
viper.WriteConfig() // 将当前配置写入“viper.AddConfigPath()”和“viper.SetConfigName”设置的预定义路径
viper.SafeWriteConfig()
viper.WriteConfigAs("/path/to/my/.config")
viper.SafeWriteConfigAs("/path/to/my/.config") // 因为该配置文件写入过,所以会报错
viper.SafeWriteConfigAs("/path/to/my/.other_config")
1
2
3
4
5

# 2.4 监控并重新读取配置文件

  • 确保在调用WatchConfig()之前添加了所有的配置路径。
viper.WatchConfig()
viper.OnConfigChange(func(e fsnotify.Event) {
  // 配置文件发生变更之后会调用的回调函数
	fmt.Println("Config file changed:", e.Name)
})
1
2
3
4
5

# 2.4 覆盖设置

  • 这些可能来自命令行标志,也可能来自你自己的应用程序逻辑。
viper.Set("Verbose", true)
viper.Set("LogFile", LogFile)
1
2

# 03.viper读取配置

# 3.1 几种访问值的方法

  • 在Viper中,有几种方法可以根据值的类型获取值

  • 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.GetString("logfile") // 不区分大小写的设置和获取
if viper.GetBool("verbose") {
    fmt.Println("verbose enabled")
}
1
2
3
4

# 3.2 访问嵌套的键

  • 问器方法也接受深度嵌套键的格式化路径
  • 例如,如果加载下面的JSON文件
{
    "host": {
        "address": "localhost",
        "port": 5799
    },
    "datastore": {
        "metric": {
            "host": "127.0.0.1",
            "port": 3099
        },
        "warehouse": {
            "host": "198.0.0.1",
            "port": 2112
        }
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
  • Viper可以通过传入.分隔的路径来访问嵌套字段:
GetString("datastore.metric.host") // (返回 "127.0.0.1")
1

# 3.3 提取子树

  • 例如,viper实例现在代表了以下配置:
app:
  cache1:
    max-items: 100
    item-size: 64
  cache2:
    max-items: 200
    item-size: 80
1
2
3
4
5
6
7
  • 执行后:
subv := viper.Sub("app.cache1")
1
  • subv现在就代表:
max-items: 100
item-size: 64
1
2
  • 假设我们现在有这么一个函数:
func NewCache(cfg *Viper) *Cache {...}
1
  • 它基于subv格式的配置信息创建缓存。现在,可以轻松地分别创建这两个缓存,如下所示:
cfg1 := viper.Sub("app.cache1")
cache1 := NewCache(cfg1)

cfg2 := viper.Sub("app.cache2")
cache2 := NewCache(cfg2)
1
2
3
4
5

# 3.4 反序列化

你还可以选择将所有或特定的值解析到结构体、map等。

有两种方法可以做到这一点:

  • Unmarshal(rawVal interface{}) : error
  • UnmarshalKey(key string, rawVal interface{}) : error

  • main.go
package main

import (
	"fmt"
	"github.com/spf13/viper"
)

type Config struct {
	Port        int    `mapstructure:"port"`
	Version     string `mapstructure:"version"`
	MySQLConfig `mapstructure:"mysql"`
}

type MySQLConfig struct {
	Host   string `mapstructure:"host"`
	DbName string `mapstructure:"dbname"`
	Port   int    `mapstructure:"port"`
}

func main() {
	// 读取配置文件
	viper.SetConfigFile("./config.yaml") // 指定配置文件路径

	err := viper.ReadInConfig() // 查找并读取配置文件
	if err != nil {             // 处理读取配置文件的错误
		panic(fmt.Errorf("Fatal error config file: %s \n", err))
	}

	var c Config

	if err := viper.Unmarshal(&c); err != nil {
		fmt.Printf("viper.Unmarshal failed, err:%v\n", err)
		return
	}
	fmt.Printf("c:%#v\n", c)
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
  • config.yaml
port: 8081
version: "v0.0.2"

mysql:
  host: "127.0.0.1"
  port: 13306
  dbname: "sql_demo"
1
2
3
4
5
6
7

# 04.使用Viper示例

  • 目录结构

# 4.1 ./conf/config.yaml

port: 8123
version: "v1.2.3"
1
2

# 4.2 gin中使用viper案例

  • 这里用一个demo演示如何在gin框架搭建的web项目中使用viper,使用viper加载配置文件中的信息
  • 并在代码中直接使用viper.GetXXX()方法获取对应的配置值。
package main
import (
	"fmt"
	"github.com/fsnotify/fsnotify"
	"github.com/gin-gonic/gin"
	"github.com/spf13/viper"
	"net/http"
)

func main() {
	// 第一:viper配置
	viper.AddConfigPath(".")       // 还可以在工作目录中查找配置
	viper.SetConfigName("config")  // 配置文件名称(无扩展名)
	viper.SetConfigType("yaml")    // 如果配置文件的名称中没有扩展名,则需要配置此项
	viper.AddConfigPath("./conf/") // 指定查找配置文件的路径

	err := viper.ReadInConfig() // 读取配置信息
	if err != nil {             // 读取配置信息失败
		panic(fmt.Errorf("Fatal error config file: %s \n", err))
	}

	// 第二:实时监控配置文件的变化
	viper.WatchConfig()
	viper.OnConfigChange(func(e fsnotify.Event) {
		// 配置文件发生变更之后会调用的回调函数
		fmt.Println("Config file changed:", e.Name)
	})

	// 第三:读取配置
	r := gin.Default()
	r.GET("/version", func(c *gin.Context) {
		c.String(http.StatusOK, viper.GetString("version"))
	})
	r.Run()
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35

# 4.3 结构体变量保存配置

package main

import (
	"fmt"
	"github.com/spf13/viper"
)

type Config struct {
	Port    int    `mapstructure:"port"`
	Version string `mapstructure:"version"`
}

var Conf = new(Config)

func main() {
	// 第一:viper配置
	viper.AddConfigPath(".")       // 还可以在工作目录中查找配置
	viper.SetConfigName("config")  // 配置文件名称(无扩展名)
	viper.SetConfigType("yaml")    // 如果配置文件的名称中没有扩展名,则需要配置此项
	viper.AddConfigPath("./conf/") // 指定查找配置文件的路径

	err := viper.ReadInConfig() // 读取配置信息
	if err != nil {             // 读取配置信息失败
		panic(fmt.Errorf("Fatal error config file: %s \n", err))
	}

	// 第二:将读取的配置信息保存至全局变量Conf
	if err := viper.Unmarshal(Conf); err != nil {
		panic(fmt.Errorf("unmarshal conf failed, err:%s \n", err))
	}
	fmt.Printf("Conf:%#v\n", Conf) // 打印
}
/*
Conf:&main.Config{Port:8123, Version:"v1.2.3"}
 */
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
上次更新: 2024/3/13 15:35:10
11.zap日志包
13.gRPC

← 11.zap日志包 13.gRPC→

最近更新
01
04.数组双指针排序_子数组
03-25
02
08.动态规划
03-25
03
06.回溯算法
03-25
更多文章>
Theme by Vdoing | Copyright © 2019-2025 逍遥子 技术博客 京ICP备2021005373号
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式