【Go API 开发实战 6】基础 2:配置文件读取
配置文件讀取
本節核心內容
介紹 apiserver 所采用的配置解決方案
介紹如何配置 apiserver 并讀取其配置,以及配置的高級用法
本小節源碼下載路徑:demo02
可先下載源碼到本地,結合源碼理解后續內容,邊學邊練。
本小節的代碼是基于 demo01 來開發的。
Viper 簡介
Viper 是國外大神 spf13 編寫的開源配置解決方案,具有如下特性:
設置默認值
可以讀取如下格式的配置文件:JSON、TOML、YAML、HCL
監控配置文件改動,并熱加載配置文件
從環境變量讀取配置
從遠程配置中心讀取配置(etcd/consul),并監控變動
從命令行 flag 讀取配置
從緩存中讀取配置
支持直接設置配置項的值
Viper 配置讀取順序:
viper.Set()所設置的值
命令行 flag
環境變量
配置文件
配置中心:etcd/consul
默認值
從上面這些特性來看,Viper 毫無疑問是非常強大的,而且 Viper 用起來也很方便,在初始化配置文件后,讀取配置只需要調用viper.GetString()、viper.GetInt() 和 viper.GetBool()等函數即可。
Viper 也可以非常方便地讀取多個層級的配置,比如這樣一個 YAML 格式的配置:
common:database:
name: test
host: 127.0.0.1
如果要讀取 host 配置,執行viper.GetString("common.database.host")即可。
apiserver 采用 YAML 格式的配置文件,采用 YAML 格式,是因為 YAML 表達的格式更豐富,可讀性更強。
初始化配置
主函數中增加配置初始化入口
package mainimport ( "errors"
"log"
"net/http"
"time"
"apiserver/config"
... "github.com/spf13/pflag")var (
cfg = pflag.StringP("config", "c", "", "apiserver config file path.")
)func main() {
pflag.Parse() // init config
if err := config.Init(*cfg); err != nil { panic(err)
} // Create the Gin engine.
g := gin.New()
...
}
在main函數中增加了config.Init(*cfg)調用,用來初始化配置,cfg變量值從命令行 flag 傳入,可以傳值,比如./apiserver -c config.yaml,也可以為空,如果為空會默認讀取conf/config.yaml。
解析配置
main函數通過?config.Init 函數來解析并 watch 配置文件(函數路徑:config/config.go),config.go 源碼為:
package configimport ( "log""strings"
"github.com/fsnotify/fsnotify"
"github.com/spf13/viper")type Config struct {
Name string}func Init(cfg string) error {
c := Config {
Name: cfg,
} // 初始化配置文件
if err := c.initConfig(); err != nil { return err
} // 監控配置文件變化并熱加載程序
c.watchConfig() return nil}func (c *Config) initConfig() error { if c.Name != "" {
viper.SetConfigFile(c.Name) // 如果指定了配置文件,則解析指定的配置文件
} else {
viper.AddConfigPath("conf") // 如果沒有指定配置文件,則解析默認的配置文件
viper.SetConfigName("config")
}
viper.SetConfigType("yaml") // 設置配置文件格式為YAML
viper.AutomaticEnv() // 讀取匹配的環境變量
viper.SetEnvPrefix("APISERVER") // 讀取環境變量的前綴為APISERVER
replacer := strings.NewReplacer(".", "_")
viper.SetEnvKeyReplacer(replacer) if err := viper.ReadInConfig(); err != nil { // viper解析配置文件
return err
} return nil}// 監控配置文件變化并熱加載程序func (c *Config) watchConfig() {
viper.WatchConfig()
viper.OnConfigChange(func(e fsnotify.Event) {
log.Printf("Config file changed: %s", e.Name)
})
}
config.Init() 通過 initConfig() 函數來解析配置文件,通過 watchConfig()函數來 watch 配置文件,兩個函數解析如下:
func (c *Config) initConfig() error
設置并解析配置文件。如果指定了配置文件 *cfg 不為空,則解析指定的配置文件,否則解析默認的配置文件 conf/config.yaml。通過指定配置文件可以很方便地連接不同的環境(開發環境、測試環境)并加載不同的配置,方便開發和測試。
通過如下設置
viper.AutomaticEnv()viper.SetEnvPrefix("APISERVER")
replacer := strings.NewReplacer(".", "_")
可以使程序讀取環境變量,具體效果稍后會演示。
config.Init 函數中的 viper.ReadInConfig() 函數最終會調用 Viper 解析配置文件。
func (c *Config) watchConfig()
通過該函數的 viper 設置,可以使 viper 監控配置文件變更,如有變更則熱更新程序。所謂熱更新是指:可以不重啟 API 進程,使 API 加載最新配置項的值。
配置并讀取配置
API 服務器端口號可能經常需要變更,API 服務器啟動時間可能會變長,自檢程序超時時間需要是可配的(通過設置次數),另外 API 需要根據不同的開發模式(開發、生產、測試)來匹配不同的行為。開發模式也需要是可配置的,這些都可以在配置文件中配置,新建配置文件conf/config.yaml(默認配置文件名字固定為 config.yaml),config.yaml 的內容為:
runmode: debug # 開發模式, debug, release, testaddr: :6663 # HTTP綁定端口
name: apiserver # API Server的名字
url: http://127.0.0.1:6663 # pingServer函數請求的API服務器的ip:port
max_ping_count: 10 # pingServer函數嘗試的次數
在main函數中將相應的配置改成從配置文件讀取,需要替換的配置見下圖中紅框部分。
替換后,代碼為:
另外根據配置文件的 runmode 調用 gin.SetMode 來設置 gin 的運行模式:
func main() {pflag.Parse() // init config
if err := config.Init(*cfg); err != nil { panic(err)
}
// Set gin mode.
gin.SetMode(viper.GetString("runmode"))
....
}
gin 有 3 種運行模式:debug、release 和 test,其中 debug 模式會打印很多 debug 信息。
編譯并運行
下載 apiserver_demos 源碼包(如前面已經下載過,請忽略此步驟)
$ git clone https://github.com/lexkong/apiserver_demos將 apiserver_demos/demo02 復制為 $GOPATH/src/apiserver
$ cp -a apiserver_demos/demo02/ $GOPATH/src/apiserver在 apiserver 目錄下編譯源碼
$ gofmt -w .
$ go tool vet .
$ go build -v .
修改conf/config.yaml將端口修改為8888,并啟動 apiserver修改后配置文件為:
addr: :8888 # HTTP綁定端口
name: apiserver # API Server的名字
url: http://127.0.0.1:8888 # pingServer函數請求的API服務器的ip:port
max_ping_count: 10 # pingServer函數try的次數
修改后啟動 apiserver:
可以看到,啟動 apiserver 后端口為配置文件中指定的端口。
Viper 高級用法
從環境變量讀取配置
在本節第一部分介紹過,Viper 可以從環境變量讀取配置,這是個非常有用的功能。現在越來越多的程序是運行在 Kubernetes 容器集群中的,在 API 服務器遷移到容器集群時,可以直接通過 Kubernetes 來設置環境變量,然后程序讀取設置的環境變量來配置 API 服務器。讀者不需要了解如何通過 Kubernetes 設置環境變量,只需要知道 Viper 可以直接讀取環境變量即可。
例如,通過環境變量來設置 API Server 端口:
$ export APISERVER_ADDR=:7777$ export APISERVER_URL=http://127.0.0.1:7777
$ ./apiserver
[GIN-debug] [WARNING] Running in "debug" mode. Switch to "release" mode in production.
- using env: export GIN_MODE=release
- using code: gin.SetMode(gin.ReleaseMode)
[GIN-debug] GET /sd/health --> apiserver/handler/sd.HealthCheck (5 handlers)
[GIN-debug] GET /sd/disk --> apiserver/handler/sd.DiskCheck (5 handlers)
[GIN-debug] GET /sd/cpu --> apiserver/handler/sd.CPUCheck (5 handlers)
[GIN-debug] GET /sd/ram --> apiserver/handler/sd.RAMCheck (5 handlers)
Start to listening the incoming requests on http address: :7777
The router has been deployed successfully.
從輸出可以看到,設置APISERVER_ADDR=:7777 和 APISERVER_URL=http://127.0.0.1:7777后,啟動 apiserver,API 服務器的端口變為7777。
環境變量名格式為config/config.go 文件中 viper.SetEnvPrefix("APISERVER")所設置的前綴和配置名稱大寫,
二者用 _連接,比如APISERVER_RUNMODE。如果配置項是嵌套的,情況可類推,比如
....max_ping_count: 10 # pingServer函數try的次數
db:
name: db_apiserver
對應的環境變量名為APISERVER_DB_NAME。
熱更新
在 main函數中添加如下測試代碼(for {}部分,循環打印runmode 的值):
import ( "fmt"....
)var (
cfg = pflag.StringP("config", "c", "", "apiserver config file path.")
)func main() {
pflag.Parse() // init config
if err := config.Init(*cfg); err != nil { panic(err)
} for {
fmt.Println(viper.GetString("runmode"))
time.Sleep(4*time.Second)
}
....
}
編譯并啟動apiserver后,修改配置文件中 runmode 為 test,可以看到runmode的值從debug變為test:
小結
本小節展示了如何用強大的配置管理工具 Viper 來解析配置文件并讀取配置,還演示了 Viper 的高級用法。
本系列文章轉載自公眾號:騰訊游戲存儲與計算技術 微信號: game_infra
總結
以上是生活随笔為你收集整理的【Go API 开发实战 6】基础 2:配置文件读取的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 【Go API 开发实战 5】基础1:启
- 下一篇: 【Go API 开发实战 1】该教程所实