Golang配置解析神器go viper使用详解

目录
  • 前言
  • viper简介
    • 功能
    • viper配置优先级
    • 安装viper
    • 支持哪些文件格式
    • key大小写问题
  • 使用指南
    • 如何访问viper的功能
    • 配置默认值
    • 读取配置文件
    • 写配置文件
    • WriteConfig
    • SafeWriteConfig
    • WriteConfigAs
    • SafeWriteConfigAs
    • 监听配置文件
    • 从io.Reader读取配置
    • 显示设置配置项
    • 注册和使用别名
    • 读取环境变量
    • 与命令行参数搭配使用
    • pflag
    • 扩展其他flag
    • 远程key/value存储支持
    • 访问配置
    • 直接访问
    • 序列化
    • 判断配置key是否存在
    • 打印所有配置
  • 小结

前言

对于现代应用程序,尤其大中型的项目来说,在程序启动和运行时,往往需要传入很多参数来控制程序的行为,这些参数可以通过以下几种方式传递给程序:

  • 命令行参数
  • 环境变量
  • 配置文件

显然,对于Go项目而言,单个去读取命令行、环境变量、配置文件并不难,但一个个读取却是很麻烦,有没有一个第三方库可以帮我们一次性读取上面几种数据源的配置呢?

有的,这里推荐使用viper库,viper支持读取不同数据源和不同格式的配置文件,是Go项目读取配置的神器,今天跟着这篇文章,一起来探究一下吧!~

viper简介

viper是一个很完善的Go项目配置解决方案,很多著名的开源项目都在使用,比如Hugo,Docker都使用了该库,使用viper可以让我们专注于自己的项目代码,而不用自己写那些配置解析代码。

功能

  • 支持配置key默认值设置
  • 支持读取JSON,TOML,YAML,HCL,envfile和java properties等多种不同类型配置文件
  • 可以监听配置文件的变化,并重新加载配置文件
  • 读取系统环境变量的值
  • 读取存储在远程配置中心的配置数据,如ectd,Consul,firestore等系统,并监听配置的变化
  • 从命令行读取配置
  • 从buffer读取配置
  • 可以显示设置配置的值

viper配置优先级

viper支持从多个数据源读取配置值,因此当同一个配置key在多个数据源有值时,viper读取的优先级如下:

  • 显示使用Set函数设置值
  • flag:命令行参数
  • env:环境变量
  • config:配置文件
  • key/value store:key/value存储系统,如(etcd)
  • default:默认值

优先级示意图

安装viper

viper的安装非常简单,如同其他Go第三方包一样,只需要go get命令即可安装,如:

安装

go get github.com/spf13/viper

使用

import "github.com/spf13/viper"

支持哪些文件格式

我们一直在说,viper支持多种不同格式的配置文件,到底是哪些格式呢?如下:

  • json
  • toml
  • yaml
  • yml
  • properties
  • props
  • prop
  • hcl
  • tfvars
  • dotenv
  • env
  • ini

key大小写问题

viper的配置的key值是不区分大小写,如:

# 小写的key
viper.set("test","this is a test value")
# 大写的key,也可以读到值
fmt.Println(viper.get("TEST"))//输出"this is a test value"

使用指南

在了解了viper是什么之后,下面我们来看看要怎么使用viper去帮我们读取配置。

如何访问viper的功能

使用包名viper,如:

viper.SetDefault("key1","value")//调用包级别下的函数

使用viper.New()函数创建一个Viper Struct,如:

viper := viper.New()
viper.SetDefault("key2","value2")

其实,这就是Go包的编程惯例,对实现功能对象再进行封装,并通过包名来调用。

因此,下面所有示例中调用函数使用viper,可以是指包名viper,或者通过viper.New()返回的对象。

配置默认值

viper.SetDefault("key1","value1")
viper.SetDefault("key2","value2")

读取配置文件

直接指定文件路径

viper.SetConfigFile("./config.yaml")
viper.ReadInConfig()
fmt.Println(viper.Get("test"))

多路径查找

viper.SetConfigName("config")     // 配置文件名,不需要后缀名
viper.SetConfigType("yml")            // 配置文件格式
viper.AddConfigPath("/etc/appname/")  // 查找配置文件的路径
viper.AddConfigPath("$HOME/.appname") // 查找配置文件的路径
viper.AddConfigPath(".")              // 查找配置文件的路径
err := viper.ReadInConfig()           // 查找并读取配置文件
if err != nil {                       // 处理错误
    panic(fmt.Errorf("Fatal error config file: %w \n", err))
}

读取配置文件时,可能会出现错误,如果我们想判断是否是因为找不到文件而报错的,可以判断err是否为ConfigFileNotFoundError

if err := viper.ReadInConfig(); err != nil {
	if _, ok := err.(viper.ConfigFileNotFoundError); ok {

	} else {

	}
}

写配置文件

除了读取配置文件外,viper也支持将配置值写入配置文件,viper提供了四个函数,用于将配置写回文件。

WriteConfig

WriteConfig函数会将配置写入预先设置好路径的配置文件中,如果配置文件存在,则覆盖,如果没有,则创建。

SafeWriteConfig

SafeWriterConfig与WriteConfig函数唯一的不同是如果配置文件存在,则会返回一个错误。

WriteConfigAs

WriteConfigAs与WriteConfig函数的不同是需要传入配置文件保存路径,viper会根据文件后缀判断写入格式。

SafeWriteConfigAs

SafeWriteConfigAs与WriteConfigAs的唯一不同是如果配置文件存在,则返回一个错误。

监听配置文件

viper支持监听配置文件,并会在配置文件发生变化,重新读取配置文件和回调函数,这样可以避免每次配置变化时,都需要重启启动应用的麻烦。

viper.OnConfigChange(func(e fsnotify.Event) {
	fmt.Println("Config file changed:", e.Name)
})
viper.WatchConfig()

从io.Reader读取配置

除了支持从配置文件读取配置外,viper也支持从实现了io.Reader接口的实例中读取配置(其实配置文件也实现了io.Reader),如:

viper.SetConfigType("json") //设置格式

var yamlExample = []byte(`
{
	"name":"小明"
}
`)
viper.ReadConfig(bytes.NewBuffer(yamlExample))
fmt.Println(viper.Get("name")) //输出“小明”

显示设置配置项

也可以使用Set函数显示为某个key设置值,这种方式的优先级最高,会覆盖该key在其他地方的值,如:

viper.SetConfigType("json") //设置格式
var yamlExample = []byte(`
{
	"name":"小明"
}
`)
viper.ReadConfig(bytes.NewBuffer(yamlExample))
fmt.Println(viper.Get("name")) //输出:小明
viper.Set("name","test")
fmt.Println(viper.Get("name"))//输出:test

注册和使用别名

为某个配置key设置别名,这样可以方便我们在不改变key的情况下,使用别的名称访问该配置。

viper.Set("name", "test")
//为name设置一个username的别名
viper.RegisterAlias("username", "name")
//通过username可以读取到name的值
fmt.Println(viper.Get("username"))
//修改name的配置值,username的值也发生改变
viper.Set("name", "小明")
fmt.Println(viper.Get("username"))
//修改username的值,name的值也发生改变
viper.Set("username", "测试")
fmt.Println(viper.Get("name"))

读取环境变量

对于读取操作系统环境变量,viper提供了下面五个函数:

  • AutomaticEnv()
  • BindEnv(string...) : error
  • SetEnvPrefix(string)
  • SetEnvKeyReplacer(string...) *strings.Replacer
  • AllowEmptyEnv(bool)

要让viper读取环境变量,有两种方式:

  • 调用AutomaticEnv函数,开启环境变量读取
fmt.Println(viper.Get("path"))
//开始读取环境变量,如果没有调用这个函数,则下面无法读取到path的值
viper.AutomaticEnv()
//会从环境变量读取到该值,注意不用区分大小写
fmt.Println(viper.Get("path"))
  • 使用BindEnv绑定某个环境变量
//将p绑定到环境变量PATH,注意这里第二个参数是环境变量,这里是区分大小写的
viper.BindEnv("p", "PATH")
//错误绑定方式,path为小写,无法读取到PATH的值
//viper.BindEnv("p","path")
fmt.Println(viper.Get("p"))//通过p可以读取PATH的值

使用函数SetEnvPrefix可以为所有环境变量设置一个前缀,这个前缀会影响AutomaticEnvBindEnv函数

os.Setenv("TEST_PATH","test")
viper.SetEnvPrefix("test")
viper.AutomaticEnv()
//无法读取path的值,因为此时加上前缀,viper会去读取TEST_PATH这个环境变量的值
fmt.Println(viper.Get("path"))//输出:nil
fmt.Println(viper.Get("test_path"))//输出:test

环境变量大多是使用下划号(_)作为分隔符的,如果想替换,可以使用SetEnvKeyReplacer函数,如:

//设置一个环境变量
os.Setenv("USER_NAME", "test")
//将下线号替换为-和.
viper.SetEnvKeyReplacer(strings.NewReplacer("-", "_", ".", "_"))
//读取环境变量
viper.AutomaticEnv()
fmt.Println(viper.Get("user.name"))//通过.访问
fmt.Println(viper.Get("user-name"))//通过-访问
fmt.Println(viper.Get("user_name"))//原来的下划线也可以访问

默认的情况下,如果读取到的环境变量值为空(注意,不是环境变量不存在,而是其值为空),会继续向优化级更低数据源去查找配置,如果想阻止这一行为,让空的环境变量值有效,则可以调用AllowEmptyEnv函数:

viper.SetDefault("username", "admin")
viper.SetDefault("password", "123456")
//默认是AllowEmptyEnv(false),这里设置为true
viper.AllowEmptyEnv(true)
viper.BindEnv("username")
os.Setenv("USERNAME", "")
fmt.Println(viper.Get("username"))//输出为空,因为环境变量USERNAME空
fmt.Println(viper.Get("password"))//输出:123456

与命令行参数搭配使用

viper可以和解析命令行库相关flag库一起工作,从命令行读取配置,其内置了对pflag库的支持,同时也留有接口让我们可以支持扩展其他的flag库

pflag

pflag.Int("port", 8080, "server http port")
pflag.Parse()
viper.BindPFlags(pflag.CommandLine)
fmt.Println(viper.GetInt("port"))//输出8080

扩展其他flag

如果我们没有使用pflag库,但又想让viper帮我们读取命令行参数呢?

package main
import (
	"flag"
	"fmt"
	"github.com/spf13/viper"
)
type myFlag struct {
	f *flag.Flag
}
func (m *myFlag) HasChanged() bool {
	return false
}
func (m *myFlag) Name() string {
	return m.f.Name
}
func (m *myFlag) ValueString() string {
	return m.f.Value.String()
}
func (m *myFlag) ValueType() string {
	return "string"
}
func NewMyFlag(f *flag.Flag) *myFlag {
	return &myFlag{f: f}
}
func main() {
	flag.String("username", "defaultValue", "usage")
	m := NewMyFlag(flag.CommandLine.Lookup("username"))
	viper.BindFlagValue("myFlagValue", m)
	flag.Parse()
	fmt.Println(viper.Get("myFlagValue"))
}

远程key/value存储支持

viper支持存储或者读取远程配置存储中心和NoSQL(目前支持etcd,Consul,firestore)的配置,并可以实时监听配置的变化,不过需要在代码中引入下面的包:

import _ "github.com/spf13/viper/remote"

现在远程配置中心存储着以下JSON的配置信息

{
	"hostname":"localhost",
	"port":"8080"
}

那么我们可以通过下面的方面连接到系统,并读取配置,也可以单独开启一个Goroutine实时监听配置的变化。

连接Consul

viper.AddRemoteProvider("consul", "localhost:8500", "MY_CONSUL_KEY")

连接etcd

viper.AddRemoteProvider("etcd", "http://127.0.0.1:4001","/config/hugo.json")

连接firestore

viper.AddRemoteProvider("firestore", "google-cloud-project-id", "collection/document")

连接上配置中心后,就可以像读取配置文件读取其中的配置了,如:

viper.SetConfigType("json")
err := viper.ReadRemoteConfig()
fmt.Println(viper.Get("port")) // 输出:8080
fmt.Println(viper.Get("hostname")) // 输出:localhost

监听配置系统,如:

go func(){
	for {
		time.Sleep(time.Second * 5)
		err := viper.WatchRemoteConfig()
		if err != nil {
			log.Errorf("unable to read remote config: %v", err)
			continue
		}
	}
}()

另外,viper连接etcd,Consul,firestore进行配置传输时,也支持加解密,这样可以更加安全,如果想要实现加密传输可以把AddRemoteProvider函数换为SecureRemoteProvider

viper.SecureRemoteProvider("etcd", "http://127.0.0.1:4001","/config/hugo.json","/etc/secrets/mykeyring.gpg")

访问配置

viper可以帮我们读取各个地方的配置,那读到配置之后,要怎么用呢?

直接访问

{
  "mysql":{
    "db":"test"
  },
  "host":{
	  "address":"localhost"
	  "ports":[
		  "8080",
		  "8081"
	  ]
  }
}

对于多层级配置key,可以用逗号隔号,如:

viper.Get("mysql.db")
viper.GetString("user.db")
viper.Get("host.address")//输出:localhost

数组,可以用序列号访问,如:

viper.Get("host.posts.1")//输出: 8081

也可以使用sub函数解析某个key的下级配置,如:

hostViper := viper.Sub("host")
fmt.Println(hostViper.Get("address"))
fmt.Println(hostViper.Get("posts.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

序列化

读取了配置之后,除了使用上面列举出来的函数访问配置,还可以将配置序列化到struct或map之中,这样可以更加方便访问配置。

示例代码

配置文件:config.yaml

host: localhost
username: test
password: test
port: 3306
charset: utf8
dbName: test

解析代码:

type MySQL struct {
	Host     string
	DbName   string
	Port     string
	Username string
	Password string
	Charset  string
}
func main() {

	viper.SetConfigName("config")
	viper.SetConfigType("yaml")
	viper.AddConfigPath(".")
	viper.ReadInConfig()
	var mysql MySQL
	viper.Unmarshal(&mysql)//序列化

	fmt.Println(mysql.Username)
	fmt.Println(mysql.Host)
}

对于多层级的配置,viper也支持序列化到一个复杂的struct中,如:

我们将config.yaml改为如下结构:

mysql:
  host: localhost
  username: test
  password: test
  port: 3306
  charset: utf8
  dbName: test
redis:
  host: localhost
  port: 6379

示例程序

type MySQL struct {
	Host     string
	DbName   string
	Username string
	Password string
	Charset  string
}
type Redis struct {
	Host string
	Port string
}
type Config struct {
	MySQL MySQL
	Redis Redis
}
func main() {
	viper.SetConfigName("config")
	viper.SetConfigType("yaml")
	viper.AddConfigPath(".")
	viper.ReadInConfig()
	var config Config
	viper.Unmarshal(&config)
	fmt.Println(config.MySQL.Username)
	fmt.Println(config.Redis.Host)
}

判断配置key是否存在

if viper.IsSet("user"){
	fmt.Println("key user is not exists")
}

打印所有配置

m := viper.AllSettings()
fmt.Println(m)

小结

到此这篇关于Golang配置解析神器go viper使用详解的文章就介绍到这了,更多相关go viper使用内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • golang 使用 viper 读取自定义配置文件

    viper 支持 Yaml.Json. TOML.HCL 等格式,读取非常的方便. viper 官网有案例:https://github.com/spf13/viper go get github.com/spf13/viper 创建 config.yaml 文件 database: driver: mysql host: 127.0.0.1 port: 3306 username: blog dbname: blog password: 123456 建一个 config.go 用于初始化配置

  • Golang使用第三方包viper读取yaml配置信息操作

    Golang有很多第三方包,其中的 viper 支持读取多种配置文件信息.本文只是做一个小小demo,用来学习入门用的. 1.安装 go get github.com/spf13/viper 2.编写一个yaml的配置文件,config.yaml database: host: 127.0.0.1 user: root dbname: test pwd: 123456 3.编写学习脚本main.go,读取config.yaml配置信息 package main import ( "fmt&quo

  • golang常用库之配置文件解析库-viper使用详解

    golang常用库:gorilla/mux-http路由库使用 golang常用库:配置文件解析库-viper使用 golang常用库:操作数据库的orm框架-gorm基本使用 golang常用库:字段参数验证库-validator使用 一.viper简介 viper 配置管理解析库,是由大神 Steve Francia 开发,他在google领导着 golang 的产品开发,他也是 gohugo.io 的创始人之一,命令行解析库 cobra 开发者.总之,他在golang领域是专家,很牛的一个

  • golang配置管理神器Viper使用教程

    目录 Viper 安装 什么是Viper? 为什么选择Viper? 把值存入Viper 建立默认值 读取配置文件 写入配置文件 监控并重新读取配置文件 从io.Reader读取配置 覆盖设置 注册和使用别名 使用环境变量 Env 示例: 使用Flags flag接口 远程Key/Value存储支持 远程Key/Value存储示例-未加密 etcd Consul Firestore 远程Key/Value存储示例-加密 监控etcd中的更改-未加密 从Viper获取值 访问嵌套的键 提取子树 反序

  • 聊聊Golang中很好用的viper配置模块

    前言 viper 支持Yaml.Json. TOML.HCL 等格式,读取非常的方便. 安装 go get github.com/spf13/viper 如果提示找不到golang.org/x/text/这个库,是因为golang.org/x/text/这个库在GitHub上托管的路径不一致. 解决办法: 可以从https://github.com/golang/text下载源码下来,然后到$GOPATH/src下面创建golang.org/x/文件夹(已存在的忽略),把压缩包的文件解压到gol

  • Golang配置管理库 Viper的教程详解

    目录 一.Viper 是什么? 二.安装 Viper 三.Viper 有什么作用 四.Viper demo 可供参考 注意 五.总结 一.Viper 是什么? Viper 是应用程序的完整配置的管理工具,用于在应用程序中工作,可以处理所有类型的配置需求和格式. 二.安装 Viper go get github.com/spf13/viper 三.Viper 有什么作用 设置默认值 读取 JSON.TOML.YAML(YML).HCL.envfile 和 Java properties 属性配置文

  • 详解go基于viper实现配置文件热更新及其源码分析

    go第三方库 github.com/spf13/viper实现了对配置文件的读取并注入到结构中,好用方便. 其中以 viperInstance := viper.New() // viper实例 viperInstance.WatchConfig() viperInstance.OnConfigChange(func(e fsnotify.Event) { log.Print("Config file updated.") viperLoadConf(viperInstance) //

  • Golang配置解析神器go viper使用详解

    目录 前言 viper简介 功能 viper配置优先级 安装viper 支持哪些文件格式 key大小写问题 使用指南 如何访问viper的功能 配置默认值 读取配置文件 写配置文件 WriteConfig SafeWriteConfig WriteConfigAs SafeWriteConfigAs 监听配置文件 从io.Reader读取配置 显示设置配置项 注册和使用别名 读取环境变量 与命令行参数搭配使用 pflag 扩展其他flag 远程key/value存储支持 访问配置 直接访问 序列

  • golang时间处理工具箱now的使用详解

    golang不像C#,Java这种高级语言,有丰富的语法糖供开发者很方便的调用.所以这便催生出很多的开源组件,通过使用这些第三方组件能够帮助我们在开发过程中少踩很多的坑. 时间处理是所有语言都要面对的一个问题,parse根据字符串转为date类型,tostring()将date类型转为定制化的字符串. 在实际使用过程中,parse的使用有一种不是很舒服的方法. 上源码 time1, _ := time.Parse("2006-01-02", "2020-02-22"

  • Golang 实现 RTP音视频传输示例详解

    目录 引言 RTP 数据包头部字段 Golang 的相关实现 结尾 引言 在 Coding 之前我们先来简单介绍一下 RTP(Real-time Transport Protocol), 正如它的名字所说,用于互联网的实时传输协议,通过 IP 网络传输音频和视频的网络协议. 由音视频传输工作小组开发,1996 年首次发布,并提出了以下使用设想. 简单的多播音频会议 使用 IP 的多播服务进行语音通信.通过某种分配机制,获取多播组地址和端口对.一个端口用于音频数据的,另一个用于控制(RTCP)包,

  • 对Python 两大环境管理神器 pyenv 和 virtualenv详解

    简介 pyenv 是一个开源的 Python 版本管理工具,可以轻松地给系统安装任意 Python 版本,想玩哪个版本,瞬间就可以切换.有了 pyenv,我们不需要再为系统多版本 Python 共存问题而发愁,也不用为手动编译安装其他 Python 版本而浪费时间,只需要执行一条简单的命令就可以切换并使用任何其他版本,该工具真正地做到了开箱即用,简单实用. virtualenv 是一个用来创建完全隔离的 Python 虚拟环境的工具,可以为每个项目工程创建一套独立的 Python 环境,从而可以

  • Python实现解析ini配置文件的示例详解

    目录 楔子 ini 文件 特殊格式 小结 楔子 在开发过程中,配置文件是少不了的,只不过我们有时会将 py 文件作为配置文件(config.py),然后在其它的模块中直接导入.这样做是一个好主意,不过配置文件是有专门的格式的,比如:ini, yaml, toml 等等. 而对于 Python 而言,也都有相应的库来解析相应格式的文件,下面我们来看看 ini 文件要如何解析. ini 文件 先来了解一下 ini 文件的格式: [satori] name = 古明地觉 age = 16 where 

  • Python命令行参数解析包argparse的使用详解

    目录 一.argparse简介 二.简单案例 三.ArgumentParser参数 四.add_argument指令参数解释 五.vars() 一.argparse简介 argparse 是 python 自带的命令行参数解析包,可以用来方便的服务命令行参数,使用之前需要先导入包 import argparse 二.简单案例 简单使用,创建一个名为test.py的文件 # 导入 argparse 模块 import argparse # 创建一个argparse 的对象 parser = arg

  • Golang 官方依赖注入工具wire示例详解

    目录 依赖注入是什么 开源选型 wire providers injectors 类型区分 总结 依赖注入是什么 Dependency Injection is the idea that your components (usually structs in go) should receive their dependencies when being created. 在 Golang 中,构造一个结构体常见的有两种方式: 在结构体初始化过程中,构建它的依赖: 将依赖作为构造器入参,传入进

  • Golang中的错误处理的示例详解

    目录 1.panic 2.包装错误 3.错误类型判断 4.错误值判断 1.panic 当我们执行panic的时候会结束下面的流程: package main import "fmt" func main() { fmt.Println("hello") panic("stop") fmt.Println("world") } 输出: go run 9.go hellopanic: stop 但是panic也是可以捕获的,我们可

  • Golang实现简单http服务器的示例详解

    目录 一.基本描述 二 .具体方法 2.1 连接的建立 2.2 http请求解析 2.3 http请求处理 2.4 http请求响应 三.完整示例 一.基本描述 完成一个http请求的处理和响应,主要有以下几个步骤: 监听端口 建立连接 解析http请求 处理请求 返回http响应 完成上面几个步骤,便能够实现一个简单的http服务器,完成对基本的http请求的处理 二 .具体方法 2.1 连接的建立 go中net包下有提供Listen和Accept两个方法,可以完成连接的建立,可以简单看下示例

随机推荐