go語言中讀取配置文件的方法總結
使用viper管理配置
- 支持多種配置文件格式,包括 JSON,TOML,YAML,HECL,envfile,甚至還包括Java properties
- 支持為配置項設置默認值
- 可以通過命令行參數(shù)覆蓋指定的配置項
- 支持參數(shù)別名
viper按照這個優(yōu)先級(從高到低)獲取配置項的取值:
- explicit call to Set: 在代碼邏輯中通過viper.Set()直接設置配置項的值
- flag:命令行參數(shù)
- env:環(huán)境變量
- config:配置文件
- key/value store:etcd或者consul
- default:默認值
按照這個優(yōu)先級(從高到低)獲取配置項的取值:
- explicit call to Set: 在代碼邏輯中通過viper.Set()直接設置配置項的值
- flag:命令行參數(shù)
- env:環(huán)境變量
- config:配置文件
- key/value store:etcd或者consul
- default:默認值
優(yōu)先級
驗證一下 viper.Set() 的優(yōu)先級高于 配置文件
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)先級最高") if configVar != "" { // SetConfigFile 顯式定義配置文件的路徑、名稱和擴展名。 // Viper 將使用它而不檢查任何配置路徑。 viper.SetConfigFile(configVar) } else { // 如果沒有顯式指定配置文件,則 // 會去下面的路徑里找文件名`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("到底用的是哪個配置文件: '%s'\n", viper.ConfigFileUsed()) fmt.Printf("Global.Source這個字段的值為: '%s'\n", viper.GetString("global.source")) }
輸出:
到底用的是哪個配置文件: '/Users/fliter/config-demo/cui-config.yaml'
Global.Source這個字段的值為: '優(yōu)先級最高'
驗證一下 環(huán)境變量 的優(yōu)先級高于 配置文件
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)先級最高") viper.AutomaticEnv() if configVar != "" { // SetConfigFile 顯式定義配置文件的路徑、名稱和擴展名。 // Viper 將使用它而不檢查任何配置路徑。 viper.SetConfigFile(configVar) } else { // 如果沒有顯式指定配置文件,則 // 會去下面的路徑里找文件名`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("到底用的是哪個配置文件: '%s'\n", viper.ConfigFileUsed()) fmt.Printf("LANG這個字段的值為: '%s'\n", viper.GetString("LANG")) }
viper.AutomaticEnv()會綁定所有環(huán)境變量,
如果只希望綁定特定的,可以使用SetEnvPrefix("global.source", "MYAPP_GLOAL_SOURCE"),注意這個函數(shù)不會自動加上MYAPP的前綴.
驗證一下 命令行參數(shù)的優(yōu)先級高于 配置文件
viper可以配合pflag來使用,pflag可以理解為標準庫flag的一個增強版,viper可以綁定到pflag上
和cobra,viper一樣,pflag也是同一作者的作品
驗證一下 默認值的優(yōu)先級低于 配置文件
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)先級最高") viper.AutomaticEnv() viper.SetDefault("Global.Source", "優(yōu)先級最低") if configVar != "" { // SetConfigFile 顯式定義配置文件的路徑、名稱和擴展名。 // Viper 將使用它而不檢查任何配置路徑。 viper.SetConfigFile(configVar) } else { // 如果沒有顯式指定配置文件,則 // 會去下面的路徑里找文件名`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("到底用的是哪個配置文件: '%s'\n", viper.ConfigFileUsed()) fmt.Printf("Global.Source這個字段的值為: '%s'\n", viper.GetString("Global.Source")) }
Watch機制(配置更新后 熱加載)
該機制可以監(jiān)聽配置文件的修改, 這樣就實現(xiàn)了熱加載,修改配置后,無需重啟服務
- 對于本地文件,是通過
fsnotify
實現(xiàn)的,然后通過一個回調(diào)函數(shù)去通知應用來reload; - 對于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 顯式定義配置文件的路徑、名稱和擴展名。 // Viper 將使用它而不檢查任何配置路徑。 viper.SetConfigFile(configVar) } else { // 如果沒有顯式指定配置文件,則 // 會去下面的路徑里找文件名`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這個字段的值為 %s:", e.Name, viper.GetString("Global.Source")) }) err := viper.ReadInConfig() if err != nil { panic(fmt.Errorf("error reading config: %s", err)) } fmt.Printf("到底用的是哪個配置文件: '%s'\n", viper.ConfigFileUsed()) fmt.Printf("Global.Source這個字段的值為: '%s'\n", viper.GetString("Global.Source")) time.Sleep(10000e9) }
configor
Configor: 一個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") // 測試模式,也可以通過環(huán)境變量開啟測試模式(CONFIGOR_DEBUG_MODE=true go run main.go ),這樣就無需修改代碼 //err := configor.New(&configor.Config{Verbose: true}).Load(&conf, "config.yml") // 模式,也可以通過環(huán)境變量開啟詳細模式(CONFIGOR_VERBOSE_MODE=true go run main.go ),這樣就無需修改代碼 if err != nil { panic(err) } fmt.Printf("%v \n", conf) }
開啟 測試模式 or 詳細模式
既可以在代碼中顯式開啟,如 err := configor.New(&configor.Config{Debug: true}).Load(&conf, "config.yml")
也可以通過環(huán)境變量開啟,如 CONFIGOR_DEBUG_MODE=true go run main.go
加載多個配置文件
// application.yml 的優(yōu)先級 大于 database.json, 排在前面的配置文件優(yōu)先級大于排在后的的配置 configor.Load(&Config, "application.yml", "database.json")
根據(jù)環(huán)境變量加載配置文件 or 從shell加載配置項
詳細可參考 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模式,可實現(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") // 無reload模式 //err := configor.Load(&conf, "config.yml") // err := configor.New(&configor.Config{Debug: true}).Load(&conf, "config.yml") // 測試模式,也可以通過環(huán)境變量開啟測試模式(CONFIGOR_DEBUG_MODE=true go run main.go ),這樣就無需修改代碼 //err := configor.New(&configor.Config{Verbose: true}).Load(&conf, "config.yml") // 模式,也可以通過環(huán)境變量開啟詳細模式(CONFIGOR_VERBOSE_MODE=true go run main.go ),這樣就無需修改代碼 if err != nil { panic(err) } fmt.Printf("%v \n", conf) time.Sleep(100000e9) }
到此這篇關于go語言中讀取配置文件的方法總結的文章就介紹到這了,更多相關go讀取配置文件內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
利用GoLang?Fiber進行高性能Web開發(fā)實例詳解
這篇文章主要為大家介紹了利用GoLang?Fiber進行高性能Web開發(fā)實例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2024-01-01Golang回調(diào)函數(shù)與閉包和接口函數(shù)的定義及使用介紹
這篇文章主要介紹了Golang回調(diào)函數(shù)與閉包和接口函數(shù)的定義及使用,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習吧2023-05-05詳解Golang中創(chuàng)建error的方式總結與應用場景
Golang中創(chuàng)建error的方式包括errors.New、fmt.Errorf、自定義實現(xiàn)了error接口的類型等,本文主要為大家介紹了這些方式的具體應用場景,需要的可以參考一下2023-07-07GoLang?socket網(wǎng)絡編程傳輸數(shù)據(jù)包時進行長度校驗的方法
在GoLang?socket網(wǎng)絡編程中,為了確保數(shù)據(jù)交互的穩(wěn)定性和安全性,通常會通過傳輸數(shù)據(jù)的長度進行校驗,發(fā)送端首先發(fā)送數(shù)據(jù)長度,然后發(fā)送數(shù)據(jù)本體,接收端則根據(jù)接收到的數(shù)據(jù)長度和數(shù)據(jù)本體進行比較,以此來確認數(shù)據(jù)是否傳輸成功2024-11-11