go語(yǔ)言中讀取配置文件的方法總結(jié)
使用viper管理配置
- 支持多種配置文件格式,包括 JSON,TOML,YAML,HECL,envfile,甚至還包括Java properties
- 支持為配置項(xiàng)設(shè)置默認(rèn)值
- 可以通過(guò)命令行參數(shù)覆蓋指定的配置項(xiàng)
- 支持參數(shù)別名
viper按照這個(gè)優(yōu)先級(jí)(從高到低)獲取配置項(xiàng)的取值:
- explicit call to Set: 在代碼邏輯中通過(guò)viper.Set()直接設(shè)置配置項(xiàng)的值
- flag:命令行參數(shù)
- env:環(huán)境變量
- config:配置文件
- key/value store:etcd或者consul
- default:默認(rèn)值
按照這個(gè)優(yōu)先級(jí)(從高到低)獲取配置項(xiàng)的取值:
- explicit call to Set: 在代碼邏輯中通過(guò)viper.Set()直接設(shè)置配置項(xiàng)的值
- flag:命令行參數(shù)
- env:環(huán)境變量
- config:配置文件
- key/value store:etcd或者consul
- default:默認(rèn)值
優(yōu)先級(jí)
驗(yàn)證一下 viper.Set() 的優(yōu)先級(jí)高于 配置文件
package main
import (
"fmt"
"github.com/spf13/viper"
)
func main() {
loadConfig()
}
func loadConfig() {
configVar := "shuang-config.yaml"
configVar = "" // 這行如果注釋掉,則從指定的configVar讀取配置文件;否則就各種條件去找了
viper.Set("Global.Source", "優(yōu)先級(jí)最高")
if configVar != "" {
// SetConfigFile 顯式定義配置文件的路徑、名稱和擴(kuò)展名。
// Viper 將使用它而不檢查任何配置路徑。
viper.SetConfigFile(configVar)
} else {
// 如果沒(méi)有顯式指定配置文件,則
// 會(huì)去下面的路徑里找文件名`cui-config`的文件 name of config file (without extension)
// 按照 []string{"json", "toml", "yaml", "yml", "properties", "props", "prop", "hcl", "tfvars", "dotenv", "env", "ini"}的順序(居然還支持Java用的properties)
viper.SetConfigName("cui-config")
viper.AddConfigPath("/etc/myapp") // 找尋的路徑
viper.AddConfigPath("$HOME/.myapp/")
viper.AddConfigPath(".")
}
err := viper.ReadInConfig()
if err != nil {
panic(fmt.Errorf("error reading config: %s", err))
}
fmt.Printf("到底用的是哪個(gè)配置文件: '%s'\n", viper.ConfigFileUsed())
fmt.Printf("Global.Source這個(gè)字段的值為: '%s'\n", viper.GetString("global.source"))
}輸出:
到底用的是哪個(gè)配置文件: '/Users/fliter/config-demo/cui-config.yaml'
Global.Source這個(gè)字段的值為: '優(yōu)先級(jí)最高'
驗(yàn)證一下 環(huán)境變量 的優(yōu)先級(jí)高于 配置文件
package main
import (
"fmt"
"github.com/spf13/viper"
)
func main() {
loadConfig()
}
func loadConfig() {
configVar := "shuang-config.yaml"
configVar = "" // 這行如果注釋掉,則從指定的configVar讀取配置文件;否則就各種條件去找了
viper.Set("Global.Source", "優(yōu)先級(jí)最高")
viper.AutomaticEnv()
if configVar != "" {
// SetConfigFile 顯式定義配置文件的路徑、名稱和擴(kuò)展名。
// Viper 將使用它而不檢查任何配置路徑。
viper.SetConfigFile(configVar)
} else {
// 如果沒(méi)有顯式指定配置文件,則
// 會(huì)去下面的路徑里找文件名`cui-config`的文件 name of config file (without extension)
// 按照 []string{"json", "toml", "yaml", "yml", "properties", "props", "prop", "hcl", "tfvars", "dotenv", "env", "ini"}的順序(居然還支持Java用的properties)
viper.SetConfigName("cui-config")
viper.AddConfigPath("/etc/myapp") // 找尋的路徑
viper.AddConfigPath("$HOME/.myapp/")
viper.AddConfigPath(".")
}
err := viper.ReadInConfig()
if err != nil {
panic(fmt.Errorf("error reading config: %s", err))
}
fmt.Printf("到底用的是哪個(gè)配置文件: '%s'\n", viper.ConfigFileUsed())
fmt.Printf("LANG這個(gè)字段的值為: '%s'\n", viper.GetString("LANG"))
}
viper.AutomaticEnv()會(huì)綁定所有環(huán)境變量,
如果只希望綁定特定的,可以使用SetEnvPrefix("global.source", "MYAPP_GLOAL_SOURCE"),注意這個(gè)函數(shù)不會(huì)自動(dòng)加上MYAPP的前綴.
驗(yàn)證一下 命令行參數(shù)的優(yōu)先級(jí)高于 配置文件
viper可以配合pflag來(lái)使用,pflag可以理解為標(biāo)準(zhǔn)庫(kù)flag的一個(gè)增強(qiáng)版,viper可以綁定到pflag上
和cobra,viper一樣,pflag也是同一作者的作品

驗(yàn)證一下 默認(rèn)值的優(yōu)先級(jí)低于 配置文件
package main
import (
"fmt"
"github.com/spf13/viper"
)
func main() {
loadConfig()
}
func loadConfig() {
configVar := "shuang-config.yaml"
configVar = "" // 這行如果注釋掉,則從指定的configVar讀取配置文件;否則就各種條件去找了
//viper.Set("Global.Source", "優(yōu)先級(jí)最高")
viper.AutomaticEnv()
viper.SetDefault("Global.Source", "優(yōu)先級(jí)最低")
if configVar != "" {
// SetConfigFile 顯式定義配置文件的路徑、名稱和擴(kuò)展名。
// Viper 將使用它而不檢查任何配置路徑。
viper.SetConfigFile(configVar)
} else {
// 如果沒(méi)有顯式指定配置文件,則
// 會(huì)去下面的路徑里找文件名`cui-config`的文件 name of config file (without extension)
// 按照 []string{"json", "toml", "yaml", "yml", "properties", "props", "prop", "hcl", "tfvars", "dotenv", "env", "ini"}的順序(居然還支持Java用的properties)
viper.SetConfigName("cui-config")
viper.AddConfigPath("/etc/myapp") // 找尋的路徑
viper.AddConfigPath("$HOME/.myapp/")
viper.AddConfigPath(".")
}
err := viper.ReadInConfig()
if err != nil {
panic(fmt.Errorf("error reading config: %s", err))
}
fmt.Printf("到底用的是哪個(gè)配置文件: '%s'\n", viper.ConfigFileUsed())
fmt.Printf("Global.Source這個(gè)字段的值為: '%s'\n", viper.GetString("Global.Source"))
}
Watch機(jī)制(配置更新后 熱加載)
該機(jī)制可以監(jiān)聽(tīng)配置文件的修改, 這樣就實(shí)現(xiàn)了熱加載,修改配置后,無(wú)需重啟服務(wù)
- 對(duì)于本地文件,是通過(guò)
fsnotify實(shí)現(xiàn)的,然后通過(guò)一個(gè)回調(diào)函數(shù)去通知應(yīng)用來(lái)reload; - 對(duì)于Remote KV Store,目前只支持etcd,做法比較ugly,(5秒鐘)輪詢一次 而不是watch api
package main
import (
"fmt"
"time"
"github.com/fsnotify/fsnotify"
"github.com/spf13/viper"
)
func main() {
loadConfig()
}
func loadConfig() {
configVar := "shuang-config.yaml"
configVar = "" // 這行如果注釋掉,則從指定的configVar讀取配置文件;否則就各種條件去找了
if configVar != "" {
// SetConfigFile 顯式定義配置文件的路徑、名稱和擴(kuò)展名。
// Viper 將使用它而不檢查任何配置路徑。
viper.SetConfigFile(configVar)
} else {
// 如果沒(méi)有顯式指定配置文件,則
// 會(huì)去下面的路徑里找文件名`cui-config`的文件 name of config file (without extension)
// 按照 []string{"json", "toml", "yaml", "yml", "properties", "props", "prop", "hcl", "tfvars", "dotenv", "env", "ini"}的順序(居然還支持Java用的properties)
viper.SetConfigName("cui-config")
viper.AddConfigPath("/etc/myapp") // 找尋的路徑
viper.AddConfigPath("$HOME/.myapp/")
viper.AddConfigPath(".")
}
viper.WatchConfig()
viper.OnConfigChange(func(e fsnotify.Event) {
fmt.Printf("配置文件 %s 發(fā)生了更改!!! 最新的Global.Source這個(gè)字段的值為 %s:", e.Name, viper.GetString("Global.Source"))
})
err := viper.ReadInConfig()
if err != nil {
panic(fmt.Errorf("error reading config: %s", err))
}
fmt.Printf("到底用的是哪個(gè)配置文件: '%s'\n", viper.ConfigFileUsed())
fmt.Printf("Global.Source這個(gè)字段的值為: '%s'\n", viper.GetString("Global.Source"))
time.Sleep(10000e9)
}
configor
Configor: 一個(gè)Golang配置工具,支持YAML,JSON,TOML,Shell環(huán)境,支持熱加載
出自jinzhu大佬

package main
import (
"fmt"
"github.com/jinzhu/configor"
)
type Config struct {
APPName string `default:"app name"`
DB struct {
Name string
User string `default:"root"`
Password string `required:"true" env:"DBPassword"`
Port uint `default:"3306"`
}
Contacts []struct {
Name string
Email string `required:"true"`
}
}
func main() {
var conf = Config{}
err := configor.Load(&conf, "config.yml")
// err := configor.New(&configor.Config{Debug: true}).Load(&conf, "config.yml") // 測(cè)試模式,也可以通過(guò)環(huán)境變量開(kāi)啟測(cè)試模式(CONFIGOR_DEBUG_MODE=true go run main.go ),這樣就無(wú)需修改代碼
//err := configor.New(&configor.Config{Verbose: true}).Load(&conf, "config.yml") // 模式,也可以通過(guò)環(huán)境變量開(kāi)啟詳細(xì)模式(CONFIGOR_VERBOSE_MODE=true go run main.go ),這樣就無(wú)需修改代碼
if err != nil {
panic(err)
}
fmt.Printf("%v \n", conf)
}開(kāi)啟 測(cè)試模式 or 詳細(xì)模式
既可以在代碼中顯式開(kāi)啟,如 err := configor.New(&configor.Config{Debug: true}).Load(&conf, "config.yml")
也可以通過(guò)環(huán)境變量開(kāi)啟,如 CONFIGOR_DEBUG_MODE=true go run main.go
加載多個(gè)配置文件
// application.yml 的優(yōu)先級(jí) 大于 database.json, 排在前面的配置文件優(yōu)先級(jí)大于排在后的的配置 configor.Load(&Config, "application.yml", "database.json")
根據(jù)環(huán)境變量加載配置文件 or 從shell加載配置項(xiàng)
詳細(xì)可參考 Golang Configor 配置文件工具
熱更新
package main
import (
"fmt"
"time"
"github.com/jinzhu/configor"
)
type Config struct {
APPName string `default:"app name"`
DB struct {
Name string
User string `default:"root"`
Password string `required:"true" env:"DBPassword"`
Port uint `default:"3306"`
}
Contacts []struct {
Name string
Email string `required:"true"`
}
}
func main() {
var conf = Config{}
// reload模式,可實(shí)現(xiàn)熱加載
err := configor.New(&configor.Config{
AutoReload: true,
AutoReloadInterval: time.Second,
AutoReloadCallback: func(config interface{}) {
// config發(fā)生變化后出發(fā)什么操作
fmt.Printf("配置文件發(fā)生了變更%#v\n", config)
},
}).Load(&conf, "config.yml")
// 無(wú)reload模式
//err := configor.Load(&conf, "config.yml")
// err := configor.New(&configor.Config{Debug: true}).Load(&conf, "config.yml") // 測(cè)試模式,也可以通過(guò)環(huán)境變量開(kāi)啟測(cè)試模式(CONFIGOR_DEBUG_MODE=true go run main.go ),這樣就無(wú)需修改代碼
//err := configor.New(&configor.Config{Verbose: true}).Load(&conf, "config.yml") // 模式,也可以通過(guò)環(huán)境變量開(kāi)啟詳細(xì)模式(CONFIGOR_VERBOSE_MODE=true go run main.go ),這樣就無(wú)需修改代碼
if err != nil {
panic(err)
}
fmt.Printf("%v \n", conf)
time.Sleep(100000e9)
}
到此這篇關(guān)于go語(yǔ)言中讀取配置文件的方法總結(jié)的文章就介紹到這了,更多相關(guān)go讀取配置文件內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Go語(yǔ)言sync包與鎖實(shí)現(xiàn)限制線程對(duì)變量的訪問(wèn)
本文主要介紹了Go語(yǔ)言sync包與鎖實(shí)現(xiàn)限制線程對(duì)變量的訪問(wèn),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2023-04-04
Go中的 panic / recover 簡(jiǎn)介與實(shí)踐記錄
這篇文章主要介紹了Go中的 panic / recover 簡(jiǎn)介與實(shí)踐,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2023-04-04
Go與Redis實(shí)現(xiàn)分布式互斥鎖和紅鎖
這篇文章主要介紹了Go與Redis實(shí)現(xiàn)分布式互斥鎖和紅鎖,文章圍繞主題展開(kāi)詳細(xì)的內(nèi)容介紹,具有一定的參考價(jià)值,需要的小伙伴可以參考一下2022-09-09
利用GoLang?Fiber進(jìn)行高性能Web開(kāi)發(fā)實(shí)例詳解
這篇文章主要為大家介紹了利用GoLang?Fiber進(jìn)行高性能Web開(kāi)發(fā)實(shí)例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2024-01-01
Golang回調(diào)函數(shù)與閉包和接口函數(shù)的定義及使用介紹
這篇文章主要介紹了Golang回調(diào)函數(shù)與閉包和接口函數(shù)的定義及使用,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)吧2023-05-05
詳解Golang中創(chuàng)建error的方式總結(jié)與應(yīng)用場(chǎng)景
Golang中創(chuàng)建error的方式包括errors.New、fmt.Errorf、自定義實(shí)現(xiàn)了error接口的類(lèi)型等,本文主要為大家介紹了這些方式的具體應(yīng)用場(chǎng)景,需要的可以參考一下2023-07-07
Golang設(shè)計(jì)模式中抽象工廠模式詳細(xì)講解
抽象工廠模式用于生成產(chǎn)品族的工廠,所生成的對(duì)象是有關(guān)聯(lián)的。如果抽象工廠退化成生成的對(duì)象無(wú)關(guān)聯(lián)則成為工廠函數(shù)模式。比如本例子中使用RDB和XML存儲(chǔ)訂單信息,抽象工廠分別能生成相關(guān)的主訂單信息和訂單詳情信息2023-01-01
一文詳解Golang?定時(shí)任務(wù)庫(kù)?gron?設(shè)計(jì)和原理
這篇文章主要介紹了一文詳解Golang?定時(shí)任務(wù)庫(kù)?gron?設(shè)計(jì)和原理,gron是一個(gè)比較小巧、靈活的定時(shí)任務(wù)庫(kù),可以執(zhí)行定時(shí)的、周期性的任務(wù)。gron提供簡(jiǎn)潔的、并發(fā)安全的接口2022-08-08
GoLang?socket網(wǎng)絡(luò)編程傳輸數(shù)據(jù)包時(shí)進(jìn)行長(zhǎng)度校驗(yàn)的方法
在GoLang?socket網(wǎng)絡(luò)編程中,為了確保數(shù)據(jù)交互的穩(wěn)定性和安全性,通常會(huì)通過(guò)傳輸數(shù)據(jù)的長(zhǎng)度進(jìn)行校驗(yàn),發(fā)送端首先發(fā)送數(shù)據(jù)長(zhǎng)度,然后發(fā)送數(shù)據(jù)本體,接收端則根據(jù)接收到的數(shù)據(jù)長(zhǎng)度和數(shù)據(jù)本體進(jìn)行比較,以此來(lái)確認(rèn)數(shù)據(jù)是否傳輸成功2024-11-11

