nsq

Viper

Posted by Liangjf on May 27, 2019

在 Go 中使用 Viper 加载配置文件

viper是国外大神spf13写的开源配置解决方案。 viper 的 Github 是:https://github.com/spf13/viper

优秀go项目使用viper

  • Hugo
  • EMC RexRay
  • Imgur’s Incus
  • Nanobox/Nanopack
  • Docker Notary
  • BloomApi
  • doctl
  • Clairctl

viper优势

  • 设置默认值
  • 可以读取如下格式的配置⽂件:JSON、TOML、YAML、HCL
  • 监控配置文件改动,并热加载配置文件
  • 从环境变量读取配置
  • 从远程配置中心读取配置(etcd/consul),并监控变动
  • 从命令行flag中读取配置
  • 从缓存中读取配置
  • 支持直接设置配置项的值

Viper 配置读取顺序

  • viper.Set() 所设置的值
  • 命令行 flag
  • 环境变量
  • 配置文件
  • 配置中心:etcd/consul
  • 默认值

Viper 非常强大,而且 Viper 用起来也很方便。

使用步骤

  • 初始化配置文件(设置配置文件的目录、文件名、后缀、环境变量等)
  • 调用viper.GetString()、viper.GetInt() 和viper.GetBool() 获取配置值

仅仅使用两步,就可以应对大多数的业务场景。

使用例子:

package config

import (
	"log"
	"strings"

	"github.com/fsnotify/fsnotify"
	"github.com/spf13/viper"
)

type Config struct {
	name string
}

func Init(cfg string) error {
	c := Config {
		name:cfg,
	}

	if err := c.configInit(); err != nil {
		return err
	}

	c.watchConfig()

	return nil
}

func (c *Config)configInit() error {
	if c.name != "" {
		viper.SetConfigFile(c.name)
	} else {
		viper.AddConfigPath("F:\\go_home\\src\\serverapi\\conf") // tm 被windows的坑了。一定要绝对路径?
		viper.SetConfigName("config")
	}

	viper.SetConfigType("yaml")
	viper.AutomaticEnv()
	viper.SetEnvPrefix("SERVERAPI")

	replacer := strings.NewReplacer(".", "_")
	viper.SetEnvKeyReplacer(replacer)
	if err := viper.ReadInConfig(); err != nil {
		return err
	}

	return nil
}

func (c *Config)watchConfig() {
	viper.WatchConfig()
	viper.OnConfigChange(func(in fsnotify.Event) {
		log.Printf("config file change :%s", in.Name)
	})
}

测试程序:

package config

import (
	"fmt"
	"testing"

	"github.com/spf13/pflag"
	"github.com/spf13/viper"
)

var (
	cfg = pflag.StringP("config", "c", "", "config for serverapi")
)

func TestConfig(test *testing.T) {
	// 配置文件
	pflag.Parse()

	if err := Init(*cfg); err != nil {
		panic(err)
	}

	fmt.Println("config: "+viper.GetString("runmode"))
}

结果:

=== RUN   TestConfig
config: debug
--- PASS: TestConfig (0.02s)
PASS

Viper 高级用法

  • 获取子级配置

    mysql: ip: localhost port: 3306

    GetString(“mysql.port”)

  • 通过环境变量来设置配置值

比如:export SERVERAPI_ADDR=:7777

  • 热更新

    viper.WatchConfig() //设置对配置文件监控更新 viper.OnConfigChange(func(in fsnotify.Event) { log.Printf(“config file change :%s”, in.Name) })

  • 解析配置(将配置绑定到某个结构体、map)
  • Unmarshal(rawVal interface{}) : error
  • UnmarshalKey(key string, rawVal interface{}) : error

var mysql MySQL err := UnmarshalKey(“mysql”, &mysql) // 将配置解析到 mysql 变量 if err != nil { panic(err) }

总结

从上面的介绍和demo来看,viper确实使用简单,但功能强大。可以在平时项目开发中用起来。