首頁 > 軟體

讀取Go專案中的組態檔的方法

2022-06-17 18:03:23

Go語言提供了很簡便的讀取json和yaml檔案的api,我們可以很輕鬆將一個json或者yaml檔案轉換成Go的結構體,但是如果直接在專案中讀取組態檔,這種方式並不夠好。缺點如下:

實際開發中設定項在不同環境下,設定的值是不同的

上面的問題可以通過不同的組態檔去決定在那個環境讀取那個組態檔,但是還有一個問題就是實際開發中,設定項有些是相同的,有些是不同的,如果組態檔有一個主組態檔,裡面存放的是不同環境相同的設定項,還有一個跟隨環境的組態檔,裡面存放的是不同環境的設定項,然後讀取兩個組態檔後,做一個merge,這樣得到的結果就是總共的設定項資訊。

有些設定項是必填的,有些設定項的值是一些特殊的值,比如,郵箱,手機號,IP資訊等

來看看gonfig是怎麼解決這個問題的

  • 安裝gonfig
go get github.com/xiao-ren-wu/gonfig
  • 專案中新建設定目錄,並編寫對應組態檔,在設定目錄同級目錄新增讀取組態檔的go檔案

conf.yaml檔案中存放的是通用的設定,conf-{{active}}.yaml中存放不同環境不同的設定資訊。

conf
   |-conf.yaml
   |-conf-dev.yaml
   |-conf-prod.yaml
   |config.go
  • 利用go:embed將組態檔載入到記憶體,呼叫gonfig.Unmarshal讀取組態檔
package config

import (
   "model"
   "github.com/xiao-ren-wu/gonfig"
   "embed"
)

//go:embed *.yaml
var confDir embed.FS

// 我們組態檔的設定struct
type AppConf struct {
	AppName string `yaml:"app-name" json:"app-name"`
	DB      DBConf `yaml:"db" json:"db"`
}

type DBConf struct {
	Username string `yaml:"username" json:"username"`
	Password string `yaml:"password" json:"password"`
}

var conf Conf

func Init() {
   if err := gonfig.Unmarshal(confDir, &conf); err != nil {
      panic(err)
   }
}

func GetConf() *Conf {
   return &conf
}

這樣就完成了專案中組態檔的讀取,是不是很簡單? 此時讀到的設定形式是conf-{{profile}}.yamlconf.yaml的總和,如果conf-{{profile}}.yaml中定義的屬性和conf.yaml相同,那麼會以conf-{{profile}}.yaml為準

約定

gonfigAPI的簡潔性的原因是因為背後做了很多約束,只有符合約束的設定才能被成功讀取,具體的約束條件如下:

  • gonfig.Unmarshal會預設讀取檔名稱有字首conf的檔案

  • 通過環境變數profile作為環境名稱,如果沒有設定,預設dev。

  • 程式會尋找conf.yaml作為主組態檔,conf-{{profile}}.yaml作為環境特有組態檔,然後對檔案內容進行合併

  • 如果conf-{{profile}}.yaml中的屬性和conf.yaml中屬性都有定義,那麼會以conf-{{profile}}.yaml為準。

根據專案客製化化組態檔

gonfig.Unmarshal的函數簽名func Unmarshal(confDir ReadFileDir, v interface{}, ops ...Option) error 提供很多設定項,供使用者客製化化需求,具體的設定資訊如下:

  • 更改組態檔名稱字首FilePrefix(prefix string)

  • 更改讀取組態檔型別UnmarshalWith(uType UnmarshalType)

  • 更改讀取的環境變數名稱ProfileUseEnv(envName, defaultProfile string)

  • 自定義設定profileProfileFunc(f func() string)

原理篇

gonfig的實現也很簡單,核心的原始碼如下:

func Unmarshal(confDir ReadFileDir, v interface{}, ops ...Option) error {
	if v != nil && reflect.ValueOf(v).Kind() != reflect.Ptr {
		return gonfig_error.ErrNonPointerArgument
	}

	var cs = &confStruct{
		confPrefix:      "conf",
		envName:         "profile",
		defaultEnvValue: "dev",
		unmarshalType:   Yaml,
	}
	cs.activeProfileFunc = func() string {
		return getActiveProfile(cs.envName, cs.defaultEnvValue)
	}

	for _, op := range ops {
		op(cs)
	}

	cs.profileActive = cs.activeProfileFunc()

	if err := loadConf(confDir, cs); err != nil {
		return err
	}

	// copy val
	v1 := reflect.New(reflect.TypeOf(v).Elem()).Interface()

	if err := fileUnmarshal(cs.activeConfRaw, v1, cs.unmarshalType); err != nil {
		return err
	}

	if len(cs.masterConfRaw) == 0 {
		return gonfig_error.MasterProfileConfNotSetError
	}

	if err := fileUnmarshal(cs.masterConfRaw, v, cs.unmarshalType); err != nil {
		return err
	}

	return mergo.Merge(v, v1, mergo.WithOverride)
}

大概的原理就是複製了一份使用者傳給函數的結構體v1,結構體v1和v分別用於接收conf-{{profile}}.yaml中的屬性和conf.yaml的設定資訊,然後通過呼叫三方開源庫mergo對兩個結構體的屬性做一個merge。

這就是關於gonfig的全部內容啦~~~

github地址是:https://github.com/xiao-ren-wu/gonfig

到此這篇關於讀取Go專案中的組態檔的方法的文章就介紹到這了,更多相關Go讀取組態檔內容請搜尋it145.com以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援it145.com!


IT145.com E-mail:sddin#qq.com