Go語言項(xiàng)目中使用Viper獲取配置信息詳解
Viper是Go應(yīng)用的完整配置解決方案,它能處理所有類型的配置需求和配置格式,支持:
- 設(shè)置默認(rèn)值。
- 讀取JSON、TOML、YAML、HCL、envfile和Java屬性配置文件。
- 實(shí)時觀察和重新讀取配置(可選)。
- 讀取環(huán)境變量。
- 從遠(yuǎn)程配置系統(tǒng)(etcd或Consul)讀取配置,并觀察其變化。
- 從命令行標(biāo)志讀取配置。
- 從緩沖區(qū)讀取配置。
- 設(shè)置顯式值。
Viper的優(yōu)先級順序是:顯式調(diào)用Set > 命令行標(biāo)志 > 環(huán)境變量 > 配置文件 > 鍵/值 存儲(etcd或Consul) > 默認(rèn)值。
本文主要記錄從yaml文件、環(huán)境變量中獲取配置。
使用的go的版本和viper的版本如下:
go 1.20 github.com/spf13/viper v1.18.2
從配置文件中獲取配置
執(zhí)行以下指令初始化項(xiàng)目:
go mod init github.com/rengmo/practicego
執(zhí)行以下語句安裝viper包:
go get github.com/spf13/viper
創(chuàng)建yaml文件config/dev.yaml,內(nèi)容如下:
redis: host: localhost port: 6379 user: # 本地redis的用戶名為空 password: abc123 db: 0
創(chuàng)建Go文件infrastructure/config.go,內(nèi)容如下:
package config
import (
"fmt"
"github.com/spf13/viper"
)
func init() {
// 設(shè)置配置文件的名字
viper.SetConfigName("dev")
// 設(shè)置文件的格式
viper.SetConfigType("yaml")
// 設(shè)置查找配置文件的路徑為當(dāng)前路徑 . 表示項(xiàng)目的工作目錄,也就是main.go同級的那個目錄
viper.AddConfigPath("./config")
// 讀取配置文件中的數(shù)據(jù)到viper中
err := viper.ReadInConfig()
if err != nil {
panic(err)
}
// 從viper中獲取配置數(shù)據(jù)
redisPort := viper.Get("redis.port")
fmt.Printf("redisPort: %v \n", redisPort) // 打印結(jié)果:redisPort: 6379
}
還可以使用GetInt()、GetString()等方法,獲取指定類型的數(shù)據(jù),viper.GetString("redis.port")就是獲取字符串類型的數(shù)據(jù)。
創(chuàng)建main.go文件,內(nèi)容如下:
package main
import (
_ "github.com/rengmo/practicego/infrastructure"
)
func main() {
}
運(yùn)行代碼,會打印出獲取到的端口號。
把
// 設(shè)置配置文件的名字
viper.SetConfigName("dev")
// 設(shè)置文件的格式
viper.SetConfigType("yaml")
// 設(shè)置查找配置文件的路徑為當(dāng)前路徑 . 表示項(xiàng)目的工作目錄,也就是main.go同級的那個目錄
viper.AddConfigPath("./config")
換成:
// 設(shè)置配置文件的路徑
viper.SetConfigFile("./config/dev.yaml")
同樣能取到配置文件中的數(shù)據(jù)。
除了使用Get方法viper.Get("redis.port")獲取配置數(shù)據(jù),還可以將配置數(shù)據(jù)反序列化成Go對象。
func init() {
...
var config *Config
err = viper.Unmarshal(&config)
if err != nil {
panic(err)
}
redisConfig := config.Redis
fmt.Printf("redisConfig %+v\n", redisConfig)
// 打印的結(jié)果:redisConfig {Host:localhost Port:6379 User: Password:abc123 DB:0}
}
type Config struct {
Redis RedisConfig
}
type RedisConfig struct {
Host string
Port int
User string
Password string
DB int
}
Viper對于配置項(xiàng)的鍵是不區(qū)分大小寫的,比如把yaml文件中的鍵改成首字母大寫:
Redis: Host: localhost Port: 6379
使用viper.Get("redis.port")一樣能獲取到配置的值,使用viper.Get("REDIS.PORT")同樣能獲取配置的值。
從環(huán)境變量中獲取配置
AutomaticEnv會檢查環(huán)境變量中是否有和已經(jīng)存在的鍵匹配的環(huán)境變量,如果有,就會把環(huán)境變量加載到viper中。
// AutomaticEnv makes Viper check if environment variables match any of the existing keys
// (config, default or flags). If matching env vars are found, they are loaded into Viper.
func AutomaticEnv() { v.AutomaticEnv() }
先在終端執(zhí)行指令添加環(huán)境變量: export env=PROD,然后在終端執(zhí)行go run .。
光看這個函數(shù)名稱,是自動將環(huán)境變量添加到viper中的意思,那么執(zhí)行下面的語句應(yīng)該就能獲取環(huán)境變量,但是無效:
viper.AutomaticEnv()
env := viper.Get("env")
fmt.Println("env: ", env) // env: <nil>
根據(jù)函數(shù)注釋,需要確認(rèn)viper中是否已經(jīng)有對應(yīng)的鍵,那么執(zhí)行下面的語句應(yīng)該就能獲取環(huán)境變量,但是依然無效:
viper.SetDefault("env", "DEV")
viper.AutomaticEnv()
env := viper.Get("env")
fmt.Println("env: ", env) // env: DEV
找了一下源碼,判斷鍵是否存在的地方使用了envkeys, exists := v.env[lcaseKey],而BindEnv方法中有設(shè)置v.env:
func (v *Viper) BindEnv(input ...string) error {
if len(input) == 0 {
return fmt.Errorf("missing key to bind to")
}
key := strings.ToLower(input[0])
if len(input) == 1 {
v.env[key] = append(v.env[key], v.mergeWithEnvPrefix(key))
} else {
v.env[key] = append(v.env[key], input[1:]...)
}
return nil
}
所以需要這樣使用:
viper.BindEnv("env", "DEV")
viper.AutomaticEnv()
env := viper.Get("env")
fmt.Println("env: ", env) // env: PROD
要注意在比較過程中,Viper會把key轉(zhuǎn)換成大寫字母與環(huán)境變量進(jìn)行比較,所以環(huán)境變量的名稱必須為大寫,不能這樣設(shè)置環(huán)境變量:export env=PROD,必須這樣設(shè)置環(huán)境變量:export ENV=PROD。
其他
如果是使用編輯器來運(yùn)行代碼,需要在編輯器中設(shè)置環(huán)境變量,我用的是VSCode編輯器,所以是在launch.json中添加了環(huán)境變量:
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"name": "Launch Package",
"type": "go",
"request": "launch",
"mode": "auto",
"program": "${workspaceFolder}",
"env": {
"env": "PROD"
}
}
]
}
序列化和反序列化時,可以使用 mapstructure指定鍵的名稱。
比如yaml文件內(nèi)容:
mysql: host: localhost port: 3306 user: root password: 666666Aa_ db_name: practicego
go文件內(nèi)容:
type Config struct {
Redis RedisConfig
MySQL MySQLConfig
Priority string
}
type MySQLConfig struct {
Host string
Port int
User string
Password string
DBName string `mapstructure:"db_name"`
}
func init() {
viper.SetConfigFile("./config/dev.yaml")
err := viper.ReadInConfig()
if err != nil {
panic(err)
}
var config *Config
err = viper.Unmarshal(&config)
if err != nil {
panic(err)
}
mysqlConfig := config.MySQL
fmt.Printf("mysqlConfig %+v\n", mysqlConfig)
}
以上就是Go語言項(xiàng)目中使用Viper獲取配置信息詳解的詳細(xì)內(nèi)容,更多關(guān)于Go Viper獲取配置信息的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
golang實(shí)現(xiàn)并發(fā)控制的方法和技巧
golang 是一門支持并發(fā)的編程語言,它提供了 goroutine 和 channel 等強(qiáng)大的特性,讓我們可以輕松地創(chuàng)建和管理多個執(zhí)行單元,實(shí)現(xiàn)高效的任務(wù)處理,在本文中,我們將介紹一些 golang 的并發(fā)控制的方法和技巧,希望對你有所幫助2024-03-03
golang連接mysql數(shù)據(jù)庫操作使用示例
這篇文章主要為大家介紹了golang連接mysql數(shù)據(jù)庫操作使用示例,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步早日升職加薪2022-04-04
golang socket斷點(diǎn)續(xù)傳大文件的實(shí)現(xiàn)方法
今天小編就為大家分享一篇golang socket斷點(diǎn)續(xù)傳大文件的實(shí)現(xiàn)方法,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2019-07-07
golang 如何用反射reflect操作結(jié)構(gòu)體
這篇文章主要介紹了golang 用反射reflect操作結(jié)構(gòu)體的操作,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2021-04-04

