欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Go項目開發(fā)中如何讀取應(yīng)用配置詳解

 更新時間:2024年05月22日 09:22:29   作者:孔令飛  
本文主要介紹了Go項目開發(fā)中如何讀取應(yīng)用配置詳解,Go生態(tài)中有很多包可以加載并解析配置,最受歡迎的是Viper包,下面就來詳細的介紹一下

幾乎所有的后端服務(wù)都需要一些配置項來配置我們的服務(wù),一些小型的項目,配置不是很多,可以選擇只通過命令行參數(shù)來傳遞配置,但是大型項目配置很多,通過命令行參數(shù)傳遞就變的很麻煩,不好維護,標準的解決方案是將這些配置信息保存在配置文件中,由程序啟動時加載和解析。所以對于一個稍微大型點的系統(tǒng),配置文件加載和解析就是一個剛需。Go 生態(tài)中有很多包可以加載并解析配置,目前最受歡迎的是 Viper 包。

Viper 包介紹

Viper 是 Go 應(yīng)用程序現(xiàn)代化、完整的解決方案,能夠處理不同格式的配置文件,讓我們在構(gòu)建現(xiàn)代應(yīng)用程序時,不必擔心配置文件格式。Viper 也能夠滿足我們對應(yīng)用配置的各種需求。Viper有 很多特性,其中一些比較重要的特性如下:

  • 支持默認配置。
  • 支持從 json、tomlyaml、ymlpropertiesprops、prop、hcl、dotenv、env 格式的文件中讀取數(shù)據(jù)。
  • 實時監(jiān)控和重新讀取配置文件(可選)。
  • 支持從環(huán)境變量中讀取配置。
  • 支持從遠程配置系統(tǒng)(etcd 或 Consul)讀取并監(jiān)控配置變化。
  • 從命令行參數(shù)讀取配置。
  • 支持從 buffer 中讀取配置。
  • 可以顯式的給配置項設(shè)置值。

Viper 可以從不同的位置讀取配置,不同位置的配置具有不同的優(yōu)先級,高優(yōu)先的配置會覆蓋低優(yōu)先級相同的配置,按優(yōu)先級從高到低排列如下:

  • 通過 viper.Set 函數(shù)式設(shè)置的配置;
  • 命令行參數(shù);
  • 環(huán)境變量;
  • 配置文件;
  • key/value 存儲;
  • 默認值。

Viper 因為其強大的特性,越來越多的優(yōu)秀項目開始使用 Viper 作為其配置解析工具,例如:Hugo、Docker Notary、Clairctl、Mercure 等。

這里需要注意,Viper 配置鍵不區(qū)分大小寫。

Viper 使用方法

Viper 有很多功能,這里選擇一些常用的、重要的功能來講解。在使用 viper 的過程中,最重要的2類功能就是讀入配置和讀取配置,Viper 提供不同的方式來讀入配置和讀取配置。

讀入配置,將配置讀入到 viper 中,有如下讀入方式:

  • 可以設(shè)置默認的配置文件名。
  • 讀取配置文件。
  • 監(jiān)聽和重新讀取配置文件。
  • 從 io.Reader 讀取配置。
  • 從環(huán)境變量讀取。
  • 從命令行標志讀取。
  • 從遠程 Key/Value 存儲讀取。
    讀取配置,從 viper 中讀取配置到應(yīng)用程序中,viper 提供如下函數(shù),來讀取配置:
  • Get(key string) interface{}
  • Get<Type>(key string) <Type>
  • AllSettings() map[string]interface{}

讀入配置

設(shè)置默認值

一個好的配置系統(tǒng)應(yīng)該支持默認值。viper 支持對 key 設(shè)置默認值,當沒有通過配置文件,環(huán)境變量,遠程配置或命令行標志設(shè)置 key 時,設(shè)置默認值通常是很有用的,可以使程序即使在沒有明確指定配置時,也能夠正常運行。例如:

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

讀取配置文件

viper 可以讀取配置文件來解析配置,支持 jsontoml、yaml、yml、properties、props、prop、hcldotenv、env 格式的配置文件。Viper 可以搜索多個路徑,但目前單個 Viper 實例僅支持單個配置文件。Viper 不默認任何配置搜索路徑,將默認決策留給應(yīng)用程序。

以下是如何使用 Viper 搜索和讀取配置文件的示例:

package main

import (
	"fmt"

	"github.com/spf13/pflag"
	"github.com/spf13/viper"
)

var (
	cfg  = pflag.StringP("config", "c", "", "Configuration file.")
	help = pflag.BoolP("help", "h", false, "Show this help message.")
)

func main() {
	pflag.Parse()
	if *help {
		pflag.Usage()
		return
	}

  // 從配置文件中讀取配置
	if *cfg != "" {
		viper.SetConfigFile(*cfg)   // 指定配置文件名
		viper.SetConfigType("yaml") // 如果配置文件名中沒有文件擴展名,則需要指定配置文件的格式,告訴 viper 以何種格式解析文件
	} else {
		viper.AddConfigPath(".")          // 把當前目錄加入到配置文件的搜索路徑中
		viper.AddConfigPath("$HOME/.iam") // 配置文件搜索路徑,可以設(shè)置多個配置文件搜索路徑
		viper.SetConfigName("config")     // 配置文件名稱(沒有文件擴展名)
	}

	if err := viper.ReadInConfig(); err != nil { // 讀取配置文件。如果指定了配置文件名,則使用指定的配置文件,否則在注冊的搜索路徑中搜索
		panic(fmt.Errorf("Fatal error config file: %s \n", err))
	}

	fmt.Printf("Used configuration file is: %s\n", viper.ConfigFileUsed())
}

在加載配置文件出錯時,你可以像下面這樣處理找不到配置文件的特定情況:

if err := viper.ReadInConfig(); err != nil {
    if _, ok := err.(viper.ConfigFileNotFoundError); ok {
        // 配置文件未找到錯誤;如果需要可以忽略
    } else {
        // 配置文件被找到,但產(chǎn)生了另外的錯誤
    }
}

// 配置文件找到并成功解析

viper 支持設(shè)置多個配置文件搜索路徑,需要注意添加搜索路徑的順序,viper 會根據(jù)添加的路徑順序搜索配置文件,如果找到則停止搜索。如果調(diào)用 SetConfigFile 直接指定了配置文件名,并且配置文件名沒有文件擴展名時,需要顯試指定配置文件的格式,以使 viper 能夠正確解析配置文件。

如果通過搜索的方式查找配置文件,則需要注意 SetConfigName 設(shè)置的配置文件名是不帶擴展名的,在搜索時 viper 會在文件名之后追加文件擴展名,并嘗試搜索所有支持的擴展類型。比如,如果我們通過 SetConfigName 設(shè)置了配置文件名為 config,則 viper 會在注冊的搜索路徑中,依次搜索:config.json、config.toml、config.yaml、config.yml、config.propertiesconfig.props、config.prop、config.hcl、config.dotenvconfig.env。

寫入配置文件

讀取配置文件很有用,但有時候我們可能需要將程序中當前的配置保存起來,方便后續(xù)使用或者 debug,viper 提供了一系列的函數(shù),可以讓我們把當前的配置保存到文件中,viper 提供了如下函數(shù)來保存配置:

  • WriteConfig:保存當前的配置到 viper 當前使用的配置文件中,如果配置文件不存在會報錯,如果配置文件存在則覆蓋當前的配置文件。
  • SafeWriteConfig:保存當前的配置到 viper 當前使用的配置文件中,如果配置文件不存在會報錯,如果配置文件存在則返回 file exists 錯誤。
  • WriteConfigAs:保存當前的配置到指定的文件中,如果文件不存在則新建,如果文件存在則會覆蓋文件。
  • SafeWriteConfigAs:保存當前的配置到指定的文件中,如果文件不存在則新建,如果文件存在則返回 file exists 錯誤。

根據(jù)經(jīng)驗,標記為 Safe 的所有方法都不會覆蓋任何文件,而是直接創(chuàng)建(如果不存在),而默認行為是創(chuàng)建或截斷。

一個小示例:

viper.WriteConfig()
viper.SafeWriteConfig()
viper.WriteConfigAs("config.running.yaml")
viper.SafeWriteConfigAs("config.running.yaml")

監(jiān)聽和重新讀取配置文件

Viper 支持在運行時讓應(yīng)用程序?qū)崟r讀取配置文件,也就是熱加載配置??梢酝ㄟ^ WatchConfig 函數(shù)熱加載配置。在調(diào)用 WatchConfig 函數(shù)之前,請確保已經(jīng)添加了配置文件的搜索路徑。可選地,可以為 Viper 提供一個回調(diào)函數(shù),以便在每次發(fā)生更改時運行。

示例:

viper.WatchConfig()
viper.OnConfigChange(func(e fsnotify.Event) {
   // 配置文件發(fā)生變更之后會調(diào)用的回調(diào)函數(shù)
	fmt.Println("Config file changed:", e.Name)
})

設(shè)置配置值

我們可以通過 viper.Set() 函數(shù)來顯式設(shè)置配置:

viper.Set("user.username", "colin")

注冊和使用別名

別名允許多個鍵引用單個值。示例:

viper.RegisterAlias("loud", "Verbose")
viper.Set("verbose", true) // 效果等同于下面一行代碼
viper.Set("loud", true)   // 效果等同于上面一行代碼

viper.GetBool("loud") // true
viper.GetBool("verbose") // true

使用環(huán)境變量

viper 還支持環(huán)境變量,通過如下 5 個函數(shù)來支持環(huán)境變量:

  • AutomaticEnv()
  • BindEnv(input ...string) error
  • SetEnvPrefix(in string)
  • SetEnvKeyReplacer(r *strings.Replacer)
  • AllowEmptyEnv(allowEmptyEnv bool)

這里要注意:viper 讀取環(huán)境變量是區(qū)分大小寫的。viper 提供了一種機制來確保 ENV 變量是唯一的。通過使用 SetEnvPrefix,可以告訴 Viper 在讀取環(huán)境變量時使用前綴。BindEnv 和 AutomaticEnv 都將使用此前綴。比如,我們設(shè)置了 viper.SetEnvPrefix("VIPER"),當使用 viper.Get("apiversion") 時,實際讀取的環(huán)境變量是 VIPER_APIVERSION。

BindEnv 需要一個或兩個參數(shù)。第一個參數(shù)是鍵名,第二個是環(huán)境變量的名稱,環(huán)境變量的名稱區(qū)分大小寫。如果未提供 ENV 變量名,則viper將假定ENV變量名為: 環(huán)境變量前綴_鍵名全大寫 ,例如:前綴為VIPER,key為username,則ENV變量名為: VIPER_USERNAME 。當顯式提供 ENV 變量名(第二個參數(shù))時,它不會自動添加前綴。例如,如果第二個參數(shù)是 id,Viper 將查找環(huán)境變量 ID

在使用 ENV 變量時,需要注意的一件重要事情是,每次訪問該值時都將讀取它。Viper在調(diào)用 BindEnv 時不固定該值。

還有一個魔法函數(shù) SetEnvKeyReplacer,SetEnvKeyReplacer 允許你使用 strings.Replacer 對象來重寫 Env 鍵。如果你想在 Get() 調(diào)用中使用 - 或者 . ,但希望你的環(huán)境變量使用 _ 分隔符,可以通過 SetEnvKeyReplacer 來實現(xiàn)。比如,我們設(shè)置了環(huán)境變量 USER_SECRET_KEY=bVix2WBv0VPfrDrvlLWrhEdzjLpPCNYb,但我們想用 viper.Get("user.secret-key"),我們調(diào)用函數(shù):

viper.SetEnvKeyReplacer(strings.NewReplacer(".", "_", "-", "_"))

上面的代碼,在調(diào)用 viper.Get() 函數(shù)時,會用 _ 替換 . 和 - 。默認情況下,空環(huán)境變量被認為是未設(shè)置的,并將返回到下一個配置源。若要將空環(huán)境變量視為已設(shè)置,可以使用 AllowEmptyEnv 方法。使用環(huán)境變量示例如下:

// 使用環(huán)境變量
os.Setenv("VIPER_USER_SECRET_ID", "QLdywI2MrmDVjSSv6e95weNRvmteRjfKAuNV")
os.Setenv("VIPER_USER_SECRET_KEY", "bVix2WBv0VPfrDrvlLWrhEdzjLpPCNYb")

viper.AutomaticEnv() // 讀取環(huán)境變量
viper.SetEnvPrefix("VIPER") // 設(shè)置環(huán)境變量前綴:VIPER_,如果是 viper,將自動轉(zhuǎn)變?yōu)榇髮憽?
viper.SetEnvKeyReplacer(strings.NewReplacer(".", "_", "-", "_")) // 將 viper.Get(key) key 字符串中 '.' 和 '-' 替換為 '_'
viper.BindEnv("user.secret-key")
viper.BindEnv("user.secret-id", "USER_SECRET_ID") // 綁定環(huán)境變量名到 key

使用標志

viper 支持 pflag 包,能夠綁定 key 到 flag。與 BindEnv 類似,在調(diào)用綁定方法時,不會設(shè)置該值。但在訪問它時會設(shè)置。對于單個標志,可以調(diào)用 BindPFlag() 進行綁定:

viper.BindPFlag("token", pflag.Lookup("token")) // 綁定單個標志

還可以綁定一組現(xiàn)有的pflags(pflag.FlagSet):

viper.BindPFlags(pflag.CommandLine)             //綁定標志集

讀取配置

viper 提供了如下方法來讀取配置:

  • Get(key string) interface{} ;
  • Get<Type>(key string) <Type> ;
  • AllSettings() map[string]interface{} ;
  • IsSet(key string) bool 。

每一個 Get 方法在找不到值的時候都會返回零值。為了檢查給定的鍵是否存在,可以使用 IsSet() 方法。<Type> 可以是 viper 支持的類型首字母大寫:Bool、Float64、Int、IntSlice、String、StringMap、StringMapStringStringSliceTime、Duration。例如:GetInt()。

讀取配置具體使用方法如下:

訪問嵌套的鍵

例如:加載下面的 JSON 文件:

{
    "host": {
        "address": "localhost",
        "port": 5799
    },
    "datastore": {
        "metric": {
            "host": "127.0.0.1",
            "port": 3099
        },
        "warehouse": {
            "host": "198.0.0.1",
            "port": 2112
        }
    }
}

Viper可以通過傳入 . 分隔的路徑來訪問嵌套字段:

viper.GetString("datastore.metric.host") // (返回 "127.0.0.1")

如果 datastore.metric 被直接賦值覆蓋(被 flag,環(huán)境變量,set() 方法等等),那么 datastore.metric 的所有子鍵都將變?yōu)槲炊x狀態(tài),它們被高優(yōu)先級配置級別覆蓋了。

如果存在與分隔的鍵路徑匹配的鍵,則直接返回其值。例如:

{
    "datastore.metric.host": "0.0.0.0",
    "host": {
        "address": "localhost",
        "port": 5799
    },
    "datastore": {
        "metric": {
            "host": "127.0.0.1",
            "port": 3099
        },
        "warehouse": {
            "host": "198.0.0.1",
            "port": 2112
        }
    }
}

通過 viper.GetString 獲取值:

viper.GetString("datastore.metric.host") // 返回 "0.0.0.0"

提取子樹

例如:viper 加載了如下配置:

app:
  cache1:
    max-items: 100
    item-size: 64
  cache2:
    max-items: 200
    item-size: 80

可以通過 viper.Sub 提取子樹:

subv := viper.Sub("app.cache1")

subv 現(xiàn)在就代表:

max-items: 100
item-size: 64

反序列化

viper 可以支持將所有或特定的值解析到結(jié)構(gòu)體、map 等??梢酝ㄟ^ 2 個函數(shù)來實現(xiàn):

  • Unmarshal(rawVal interface{}) error
  • UnmarshalKey(key string, rawVal interface{}) error

一個示例:

type config struct {
	Port int
	Name string
	PathMap string `mapstructure:"path_map"`
}

var C config

err := viper.Unmarshal(&C)
if err != nil {
	t.Fatalf("unable to decode into struct, %v", err)
}

如果想要解析那些鍵本身就包含 . (默認的鍵分隔符)的配置,則需要修改分隔符:

v := viper.NewWithOptions(viper.KeyDelimiter("::"))

v.SetDefault("chart::values", map[string]interface{}{
    "ingress": map[string]interface{}{
        "annotations": map[string]interface{}{
            "traefik.frontend.rule.type":                 "PathPrefix",
            "traefik.ingress.kubernetes.io/ssl-redirect": "true",
        },
    },
})

type config struct {
	Chart struct{
        Values map[string]interface{}
    }
}

var C config

v.Unmarshal(&C)

Viper 在后臺使用 github.com/mitchellh/mapstructure 來解析值,其默認情況下使用 mapstructure tags。當我們需要將 viper 讀取的配置反序列到我們定義的結(jié)構(gòu)體變量中時,一定要使用 mapstructure tags。

序列化成字符串

有時候我們需要將 viper 中保存的所有設(shè)置序列化到一個字符串中,而不是將它們寫入到一個文件中,示例如下:

import (
    yaml "gopkg.in/yaml.v2"
    // ...
)

func yamlStringSettings() string {
    c := viper.AllSettings()
    bs, err := yaml.Marshal(c)
    if err != nil {
        log.Fatalf("unable to marshal config to YAML: %v", err)
    }
    return string(bs)
}

總結(jié)

本文討論了 Go 語言中用于加載和解析配置文件的 Viper 包。Viper 支持多種格式的配置文件,并提供了許多功能來幫助開發(fā)者管理配置。
關(guān)鍵要點包括:

  • 配置文件格式:Viper 支持 JSON、TOML、YAML、HCL 和 INI 格式的配置文件。
  • 默認值設(shè)置:開發(fā)者可以為配置鍵設(shè)置默認值,以便在配置文件中未找到該鍵時使用。
  • 讀取配置文件:Viper 提供了多種方法來讀取配置文件,包括從文件系統(tǒng)、環(huán)境變量或命令行參數(shù)中讀取。
  • 監(jiān)聽和重新讀取配置文件:Viper 可以監(jiān)聽配置文件的更改,并自動重新加載配置。
  • 示例代碼:文檔提供了一些示例代碼,幫助開發(fā)者更好地理解和使用 Viper 包。

 到此這篇關(guān)于Go項目開發(fā)中如何讀取應(yīng)用配置詳解的文章就介紹到這了,更多相關(guān)Go 讀取應(yīng)用配置內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • gorm 結(jié)構(gòu)體中 binding 和 msg 結(jié)構(gòu)體標簽示例詳解

    gorm 結(jié)構(gòu)體中 binding 和 msg 結(jié)構(gòu)體標簽示例詳解

    文章介紹了Gin框架中binding和msg結(jié)構(gòu)體標簽的使用,包括基本用法、常用驗證規(guī)則、自定義驗證器、錯誤信息自定義、控制器使用示例、組合驗證規(guī)則、跨字段驗證和初始化驗證器等,這些標簽主要用于數(shù)據(jù)驗證、自定義錯誤信息、參數(shù)綁定和表單驗證
    2024-11-11
  • 淺析Go語言如何避免數(shù)據(jù)競爭Data?Race和競態(tài)條件Race?Condition

    淺析Go語言如何避免數(shù)據(jù)競爭Data?Race和競態(tài)條件Race?Condition

    在并發(fā)編程中,數(shù)據(jù)競爭?(Data?Race)?和?競態(tài)條件?(Race?Condition)?是兩個常見的問題,本文將簡單介紹一下二者如何避免,有需要的可以了解下
    2025-01-01
  • Golang實現(xiàn)Json分級解析及數(shù)字解析實踐詳解

    Golang實現(xiàn)Json分級解析及數(shù)字解析實踐詳解

    你是否遇到過在無法準確確定json層級關(guān)系的情況下對json進行解析的需求呢?本文就來和大家介紹一次解析不確定的json對象的經(jīng)歷,以及遇到的問題和解決方法
    2023-02-02
  • Go中g(shù)routine通信與context控制實例詳解

    Go中g(shù)routine通信與context控制實例詳解

    隨著context包的引入,標準庫中很多接口因此加上了context參數(shù),下面這篇文章主要給大家介紹了關(guān)于Go中g(shù)routine通信與context控制的相關(guān)資料,需要的朋友可以參考下
    2022-02-02
  • golang時間處理工具箱now的使用詳解

    golang時間處理工具箱now的使用詳解

    這篇文章主要介紹了golang時間處理工具箱now的使用詳解,幫助大家更好的理解和學習使用golang,感興趣的朋友可以了解下
    2021-02-02
  • 使用Gin框架搭建一個Go Web應(yīng)用程序的方法詳解

    使用Gin框架搭建一個Go Web應(yīng)用程序的方法詳解

    在本文中,我們將要實現(xiàn)一個簡單的 Web 應(yīng)用程序,通過 Gin 框架來搭建,主要支持用戶注冊和登錄,用戶可以通過注冊賬戶的方式創(chuàng)建自己的賬號,并通過登錄功能進行身份驗證,感興趣的同學跟著小編一起來看看吧
    2023-08-08
  • Go語言調(diào)用SiliconFlow實現(xiàn)文本轉(zhuǎn)換為MP3格式

    Go語言調(diào)用SiliconFlow實現(xiàn)文本轉(zhuǎn)換為MP3格式

    這篇文章主要為大家詳細介紹了Go語言如何調(diào)用?SiliconFlow?語音生成?API?的腳本,用于將文本轉(zhuǎn)換為?MP3?格式的語音文件,感興趣的小伙伴可以了解下
    2025-02-02
  • Go 如何使用原始套接字捕獲網(wǎng)卡流量

    Go 如何使用原始套接字捕獲網(wǎng)卡流量

    為了減少對環(huán)境的依賴可以使用原始套接字捕獲網(wǎng)卡流量,然后使用?gopacket?的協(xié)議解析功能,這樣就省去了解析這部分的工作量,正確性也可以得到保證,同時 CGO 也可以關(guān)閉,這篇文章主要介紹了Go 使用原始套接字捕獲網(wǎng)卡流量,需要的朋友可以參考下
    2024-07-07
  • 細細探究Go 泛型generic設(shè)計

    細細探究Go 泛型generic設(shè)計

    這篇文章主要帶大家細細探究了Go 泛型generic設(shè)計及示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2023-04-04
  • golang實現(xiàn)枚舉的幾種方式

    golang實現(xiàn)枚舉的幾種方式

    在Go語言中,雖沒有內(nèi)置枚舉類型,但可通過常量、結(jié)構(gòu)體或自定義類型和方法實現(xiàn)枚舉功能,這些方法提高了代碼的可讀性和維護性,避免了魔法數(shù)字的使用,感興趣的可以了解一下
    2024-09-09

最新評論