1.簡單介紹
安裝:
go get github.com/spf13/viper
Viper 是 Go 應用程式的完整配置解決方案,它旨在在應用程式中工作,並且可以處理所有型別的配置需求和格式。
- 設定預設值
- 從 JSON、TOML、YAML、HCL、envfile 和 Java 屬性配置檔案中讀取
- 實時觀看和重新讀取配置檔案(可選)
- 從環境變數中讀取
- 從遠端配置系統(etcd 或 Consul)讀取,並觀察變化
- 從命令列標誌讀取
- 從緩衝區讀取
- 設定顯式值
Viper 可以被認為是滿足您所有應用程式配置需求的登錄檔。
2.建立預設值
一個好的配置系統將支援預設值。鍵不需要預設值,但在未通過配置檔案、環境變數、遠端配置或標誌設定鍵的情況下它很有用。
//如果沒有從配置檔案讀到對應的配置,但是被使用了,就會去使用預設值
viper.SetDefault("ContentDir", "content")
viper.SetDefault("LayoutDir", "layouts")
viper.SetDefault("Taxonomies", map[string]string{"tag": "tags", "category": "categories"})
3.讀取配置檔案
Viper需要最小的配置,所以它知道在哪裡尋找配置檔案。Viper支援JSON, TOML, YAML, HCL, INI, envfile和Java Properties檔案。Viper可以搜尋多個路徑,但目前一個Viper例項只支援一個配置檔案。Viper不預設任何配置搜尋路徑,而是將預設決定權留給一個應用程式。(他不會約定你的配置檔案應該放在哪裡,也不會規定你用什麼格式或者名稱來規定你的配置檔案,你可以自己決定,告訴它就好!)
viper.SetConfigName("config") // 配置檔案的名稱(不帶副檔名)
viper.SetConfigType("yaml") // 如果配置檔名稱中沒有副檔名,則需要配置這一項
viper.AddConfigPath("/etc/appname/") // 新增viper去查詢配置檔案的路徑(可以新增多個)
viper.AddConfigPath(".") // 可以選擇在工作目錄中尋找配置。
if err != nil { // 處理讀取配置檔案的錯誤
panic(fmt.Errorf("Fatal error config file: %w \n", err))
}
您可以處理找不到配置檔案的特定情況,如下所示:
if err := viper.ReadInConfig(); err != nil {
if _, ok := err.(viper.ConfigFileNotFoundError); ok {
// 未找到配置檔案;如果需要,忽略錯誤
} else {
// 找到了配置檔案,但產生了另一個錯誤
}
}
// 找到併成功解析了配置檔案
注意 [自 1.6 起]:您還可以擁有一個沒有副檔名的檔案並以程式設計方式指定格式。對於那些位於使用者家中沒有任何副檔名的配置檔案,如 .bashrc
4.編寫配置檔案
從配置檔案中讀取很有用,但有時您希望儲存在執行時所做的所有修改。為此,有一堆命令可用,每個命令都有自己的目的:
- WriteConfig - 將當前 viper 配置寫入預定義路徑(如果存在)將覆蓋當前的配置檔案;如果沒有預定義路徑,則會出錯。
- SafeWriteConfig - 將當前 viper 配置寫入預定義路徑。如果存在,則不會覆蓋當前的配置檔案。如果沒有預定義路徑,則會出錯。
- WriteConfigAs - 將當前 viper 配置寫入給定的檔案路徑。將覆蓋給定的檔案(如果存在)。
- SafeWriteConfigAs - 將當前 viper 配置寫入給定的檔案路徑。不會覆蓋給定的檔案(如果存在)。
5.檢視和重新讀取配置檔案
Viper 支援讓您的應用程式在執行時實時讀取配置檔案的能力。
需要重新啟動伺服器才能使配置生效的日子已經一去不復返了,viper 驅動的應用程式可以在執行時讀取配置檔案的更新,而不會錯過任何一個節拍。
只需告訴 viper 例項 watchConfig。或者,您可以為 Viper 提供一個函式,以便在每次發生更改時執行。
原配置檔案內容:
server:
port: 5201
程式碼示例:
package main
import (
"fmt"
"github.com/fsnotify/fsnotify"
"github.com/spf13/viper"
)
func main(){
viper.SetConfigName("application") //設定配置檔名稱
viper.SetConfigType("yaml") //設定配置副檔名
viper.AddConfigPath(".") //設定配置檔案path
err := viper.ReadInConfig() //讀配置檔案
beforeGet := viper.Get("server.port")
fmt.Println(beforeGet) // 5201
viper.OnConfigChange(func(fs fsnotify.Event) { //配置檔案變更鉤子函式
fmt.Println("Config file changed:", fs.Name)
})
viper.WatchConfig() //開啟監聽
if err != nil{
fmt.Printf("config file read err %d \n",err)
}
if writeErr:= viper.WriteConfig();writeErr!=nil{
fmt.Printf("write err %d \n",writeErr)
}
viper.Set("server.port",11112) //通過set去修改配置
afterGet := viper.Get("server.port")
fmt.Println(afterGet) // 11112
}
輸出結果:觸發了watchConfig
配置檔案修改的hooks鉤子函式
5201
Config file changed: /Users/codehope/remote-es-server-code/vipper-demo/application.yaml
11112
6.從io.Reader
讀取配置
package main
import (
"bytes"
"fmt"
"github.com/spf13/viper"
"os"
)
func main() {
viper.SetConfigType("yaml") // 這個也需要加上的,不然讀取的是nil
fileBuf, err := os.ReadFile("./application.yaml")
if err != nil{
fmt.Println(err)
return
}
fmt.Println(string(fileBuf))
//fmt.Printf("%T",fileBuf)
err1 := viper.ReadConfig(bytes.NewBuffer(fileBuf))
if err1 != nil {
return
}
getName := viper.Get("name")
getServer := viper.Get("server")
fmt.Println(getName) //app-demo
fmt.Println(getServer) //map[port:5201]
}
【注意(version:viper@v1.9.0):從io.Reader讀取 的話也需要設定配置檔案的型別SetConfigType
,不然有可能讀出的值為nil
】
7.配置覆蓋
這些可能來自一個命令列標誌,或來自你自己的應用邏輯。
package main
import (
"bytes"
"fmt"
"github.com/spf13/viper"
)
func main() {
viper.SetConfigType("yaml")
ioReadConfig := []byte(
`name: "app-demo"`)
err := viper.ReadConfig(bytes.NewBuffer(ioReadConfig))
if err != nil {
fmt.Println(err)
return
}
fmt.Println(viper.Get("name")) //app-demo
viper.Set("name","app-demo2")
fmt.Println(viper.Get("name")) //app-demo2
}
8.註冊和使用別名
別名允許一個值被多個鍵所引用
package main
import (
"bytes"
"fmt"
"github.com/spf13/viper"
)
func main() {
viper.SetConfigType("yaml")
ioReadConfig := []byte(
`name: "app-demo"`)
err := viper.ReadConfig(bytes.NewBuffer(ioReadConfig))
if err != nil {
fmt.Println(err)
return
}
viper.RegisterAlias("t_name","name") //name配置起個別名叫t_name
fmt.Println(viper.Get("t_name")) //獲取t_name的配置值,實際去拿的 name的值 -> app-demo
viper.Set("t_name","app_new_name") //設定t_name的新值,實際去設定 name的值
fmt.Println(viper.Get("name")) //驗證是否通過別名修改成功 -> app_new_name
}
本作品採用《CC 協議》,轉載必須註明作者和本文連結