試試這樣讀取Go專案中的配置檔案

語言: CN / TW / HK

theme: channing-cyan highlight: androidstudio


持續創作,加速成長!這是我參與「掘金日新計劃 · 6 月更文挑戰」的第18天,點選檢視活動詳情

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

  1. 實際開發中配置項在不同環境下,配置的值是不同的

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

  3. 有些配置項是必填的,有些配置項的值是一些特殊的值,比如,郵箱,手機號,IP資訊等

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

  1. 安裝gonfig ~~~cmd go get github.com/xiao-ren-wu/gonfig ~~~
  2. 專案中新建配置目錄,並編寫對應配置檔案,在配置目錄同級目錄新增讀取配置檔案的go檔案 conf.yaml檔案中存放的是通用的配置,conf-{{active}}.yaml中存放不同環境不同的配置資訊。 ~~~cmd conf |-conf.yaml |-conf-dev.yaml |-conf-prod.yaml |config.go ~~~
  3. 利用go:embed將配置檔案載入到記憶體,呼叫gonfig.Unmarshal讀取配置檔案 ~~~go 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的簡潔性的原因是因為背後做了很多約束,只有符合約束的配置才能被成功讀取,具體的約束條件如下:

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

  2. 通過環境變數profile作為環境名稱,如果沒有配置,預設dev。

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

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

根據專案定製化配置檔案

gonfig.Unmarshal的函式簽名func Unmarshal(confDir ReadFileDir, v interface{}, ops ...Option) error提供很多配置項,供使用者定製化需求,具體的配置資訊如下: 1. 更改配置檔名稱字首FilePrefix(prefix string)

  1. 更改讀取配置檔案型別UnmarshalWith(uType UnmarshalType)

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

  3. 自定義設定profileProfileFunc(f func() string)

原理篇

gonfig的實現也很簡單,核心的原始碼如下: ~~~go 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

如果對你的工作或者學習有幫助,可以給個star,