golang常用庫:配置檔案解析庫-viper使用

九卷發表於2020-10-11

一、viper簡介

viper 配置解析庫,是由大神 Steve Francia 開發,他在google領導著 golang 的產品開發,他也是 gohugo.io 的創始人之一,命令列解析庫 cobra 也是他開發。總之,他在golang領域是專家,很牛的一個人。

他的github地址:https://github.com/spf13

viper是一個配置解析的解決方案,它能夠從 json,toml,ini,yaml,hcl,env 等多種格式檔案中,讀取配置內容,它還能從一些遠端配置中心讀取配置檔案,如consul,etcd等;它還能夠監聽檔案的內容變化。

viper的 logo:

二、viper功能介紹

  • 讀取 json,toml,ini,yaml,hcl,env 等格式的檔案內容
  • 讀取遠端配置檔案,如 consul,etcd 等和監控配置檔案變化
  • 讀取命令列 flag 的值
  • 從 buffer 中讀取值

配置檔案又可以分為不同的環境,比如dev,test,prod等。

viper 可以幫助你專注管理配置檔案。

viper 讀取配置檔案的優先順序,從高到低,如下:

  • 顯式設定的Set函式
  • 命令列引數
  • 環境變數
  • 配置檔案
  • 遠端k-v 儲存系統,如consul,etcd等
  • 預設值

Viper 配置key是不區分大小寫的。

其實,上面的每一種檔案格式,都有一些比較有名的解析庫,如:

但是為啥子要用viper,因為它是一個綜合檔案解析庫,包含了上面所有的檔案格式解析,是一個集合體,少了配置多個庫的煩惱。

三、viper使用

安裝viper命令:
go get github.com/spf13/viper

3.1 把值存入Viper裡

文件: https://github.com/spf13/viper/blob/master/README.md#putting-values-into-viper

設定預設值

https://github.com/spf13/viper/blob/master/README.md#establishing-defaults

viper 支援預設值的設定。如果配置檔案、環境變數、遠端配置中沒有設定鍵值,就可以通過viper設定一些預設值。

Examples:

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

讀取配置檔案

https://github.com/spf13/viper/blob/master/README.md#reading-config-files

讀取配置檔案說明

讀取配置檔案要求:最少要知道從哪個位置查詢配置檔案。使用者一定要設定這個路徑。

viper可以從多個路徑搜尋配置檔案,單個viper例項只支援單個配置檔案。
viper本身沒有設定預設的搜尋路徑,需要使用者自己設定預設路徑。

viper搜尋和讀取配置檔案例子片段

viper.SetConfigName("config") // 配置檔案的檔名,沒有副檔名,如 .yaml, .toml 這樣的副檔名
viper.SetConfigType("yaml")  // 設定副檔名。在這裡設定檔案的副檔名。另外,如果配置檔案的名稱沒有副檔名,則需要配置這個選項
viper.AddConfigPath("/etc/appname/") // 查詢配置檔案所在路徑
viper.AddConfigPath("$HOME/.appname") // 多次呼叫AddConfigPath,可以新增多個搜尋路徑
viper.AddConfigPath(".")             // 還可以在工作目錄中搜尋配置檔案
err := viper.ReadInConfig()       // 搜尋並讀取配置檔案
if err != nil { // 處理錯誤
  panic(fmt.Errorf("Fatal error config file: %s \n", err))
}

說明:
這裡執行viper.ReadInConfig()之後,viper才能確定到底用哪個檔案,viper按照上面的AddConfigPath() 進行搜尋,找到第一個名為 config.ext (這裡的ext代表副檔名: 如 json,toml,yaml,yml,ini,prop 等副檔名) 的檔案後即停止搜尋。

如果有多個名稱為config的配置檔案,viper怎麼搜尋呢?它會按照如下順序搜尋

  • config.json
  • config.toml
  • config.yaml
  • config.yml
  • config.properties (這種一般是java中的配置檔名)
  • config.props (這種一般是java中的配置檔名)

你還可以處理一些特殊情況:

if err := viper.ReadInConfig(); err != nil {
    if _, ok := err.(viper.ConfigFileNotFoundError); ok {        
        // 配置檔案沒有找到; 如果需要可以忽略
    } else {        
        // 查詢到了配置檔案但是產生了其它的錯誤
    }
}

// 查詢到配置檔案並解析成功

注意[自1.6起]:  你也可以有不帶副檔名的檔案,並以程式設計方式指定其格式。對於位於使用者$HOME目錄中的配置檔案沒有任何副檔名,如.bashrc。

完整例子examples
  • 1. 讀取配置檔案

config.toml 配置檔案:

# this is a toml 

title = "toml exaples"
redis = "127.0.0.1:3300"  # redis

[mysql]
host = "192.168.1.1"
ports = 3306
username = "root"
password = "root123456"

viper_toml.go:

package main

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

// 讀取配置檔案config
type Config struct {
    Redis string
    MySQL MySQLConfig
}

type MySQLConfig struct {
    Port int
    Host string
    Username string
    Password string
}

func main() {
    // 把配置檔案讀取到結構體上
    var config Config
    
    viper.SetConfigName("config")
    viper.AddConfigPath(".")
    err := viper.ReadInConfig()
    if err != nil {
        fmt.Println(err)
        return
    }
     
    viper.Unmarshal(&config) //將配置檔案繫結到config上
    fmt.Println("config: ", config, "redis: ", config.Redis)
}
  • 2. 讀取多個配置檔案

多增加一個json的配置檔案,config3.json 配置檔案:

{
  "redis": "127.0.0.1:33000",
  "mysql": {
    "port": 3306,
    "host": "127.0.0.1",
    "username": "root",
    "password": "123456"
  }
}

viper_multi.go

package main

import (
	"fmt"

	"github.com/spf13/viper"
)

type Config struct {
	Redis string
	MySQL MySQLConfig
}

type MySQLConfig struct {
	Port     int
	Host     string
	Username string
	Password string
}

func main() {
	// 讀取 toml 配置檔案
	var config1 Config

	vtoml := viper.New()
	vtoml.SetConfigName("config")
	vtoml.SetConfigType("toml")
	vtoml.AddConfigPath(".")

	if err := vtoml.ReadInConfig(); err != nil {
		fmt.Println(err)
		return
	}

	vtoml.Unmarshal(&config1)
	fmt.Println("read config.toml")
	fmt.Println("config: ", config1, "redis: ", config1.Redis)

	// 讀取 json 配置檔案
	var config2 Config
	vjson := viper.New()
	vjson.SetConfigName("config3")
	vjson.SetConfigType("json")
	vjson.AddConfigPath(".")

	if err := vjson.ReadInConfig(); err != nil {
		fmt.Println(err)
		return
	}

	vjson.Unmarshal(&config2)
	fmt.Println("read config3.json")
	fmt.Println("config: ", config1, "redis: ", config1.Redis)
}

執行:

$ go run viper_multi.go

read config.toml
config:  {127.0.0.1:33000 {0 192.168.1.1 root 123456}} redis:  127.0.0.1:33000
read config3.json
config:  {127.0.0.1:33000 {0 192.168.1.1 root 123456}} redis:  127.0.0.1:33000

四、參考

相關文章