網(wǎng)站首頁 編程語言 正文
問題
- Go語言在編譯時不會將配置文件這類第三方文件打包進(jìn)二進(jìn)制文件中
- 它既受當(dāng)前路徑的影響,也會因所填寫的不同而改變,并非是絕對可靠的
解決
命令行參數(shù)
在Go語言中,可以直接通過flag標(biāo)準(zhǔn)庫來實(shí)現(xiàn)該功能。實(shí)現(xiàn)邏輯為,如果存在命令行參數(shù),則優(yōu)先使用命令行參數(shù),否則使用配置文件中的配置參數(shù)。
如下:
var ( port string runMode string config string ) func init() { // 獲取命令行參數(shù) err = setupFlag() if err != nil { log.Fatalf("init.setupFlag err: %v", err) } ...... } // 獲取命令行參數(shù) func setupFlag() error { flag.StringVar(&port, "port", "", "啟動端口") flag.StringVar(&runMode, "mode", "", "啟動模式") flag.StringVar(&config, "config", "config/", "指定要使用的配置文件路徑") flag.Parse() return nil }
通過上述代碼,我們可以通過標(biāo)準(zhǔn)庫flag讀取命令行參數(shù),然后根據(jù)其默認(rèn)值判斷配置文件是否存在。若存在,則對讀取配置的路徑進(jìn)行變更,代碼如下:
package setting import "github.com/spf13/viper" type Setting struct { vp *viper.Viper } // 初始化配置文件的基礎(chǔ)屬性 func NewSetting(configs ...string) (*Setting, error) { vp := viper.New() vp.SetConfigName("config") if len(configs) != 0 { for _, config := range configs { if config != "" { vp.AddConfigPath(config) } } } else { vp.AddConfigPath("configs/") } vp.SetConfigType("yaml") err := vp.ReadInConfig() if err != nil { return nil, err } return &Setting{vp}, nil }
接下來,對ServerSetting配置項進(jìn)行覆寫。如果存在,則覆蓋原有的文件配置,使其優(yōu)先級更高,代碼如下:
// 初始化配置文件 func setupSetting() error { setting, err := setting2.NewSetting(strings.Split(config, ",")...) if err != nil { return err } ...... if port != "" { global.ServerSetting.HttpPort = port } if runMode != "" { global.ServerSetting.RunMode = runMode } return nil }
然后在運(yùn)行的時候傳入?yún)?shù)即可:
go run main.go -port=8081 -mode=debug -config=configs/
系統(tǒng)環(huán)境變量
可以將配置文件存放在系統(tǒng)自帶的全局變量中,如$HOME/conf或/etc/conf中,這樣做的好處是不需要重新自定義一個新的系統(tǒng)環(huán)境變量。
一般來說,我們會在程序中內(nèi)置一些系統(tǒng)環(huán)境變量的讀取,其優(yōu)先級低于命令行參數(shù),但高于文件配置。
打包進(jìn)二進(jìn)制文件
可以將配置文件這種第三方文件打包進(jìn)二進(jìn)制文件中,這樣就不需要過度關(guān)注這些第三方文件了。但這樣做是有一定代價的,因此要注意使用的應(yīng)用場景,即并非所有的項目都能這樣操作。
首先安裝go-bindata庫,安裝命令如下:
go get -u github.com/go-bindata/go-bindata/...
通過go-bindata庫可以將數(shù)據(jù)文件轉(zhuǎn)換為Go代碼。例如,常見的配置文件、資源文件(如Swagger UI)等都可以打包進(jìn)Go代碼中,這樣就可以“擺脫”靜態(tài)資源文件了。接下來在項目根目錄下執(zhí)行生成命令:
go-bindata -o configs/config.go -pkg-configs configs/config.yaml
執(zhí)行這條命令后,會將 configs/config.yaml 文件打包,并通過-o 選項指定的路徑輸出到configs/config.go文件中,再通過設(shè)置的-pkg選項指定生成的packagename為configs,接下來只需執(zhí)行下述代碼,就可以讀取對應(yīng)的文件內(nèi)容了:
b,_:=configs.Asset("configs/config.yaml")
把第三方文件打包進(jìn)二進(jìn)制文件后,二進(jìn)制文件必然增大,而且在常規(guī)方法下無法做文件的熱更新和監(jiān)聽,必須要重啟并且重新打包才能使用最新的內(nèi)容,因此這種方式是有利有弊的。
配置熱更新
開源的fsnotify
既然要做配置熱更新,那么首先要知道配置是什么時候修改的,做了哪些事?因此我們需要對所配置的文件進(jìn)行監(jiān)聽,只有監(jiān)聽到了,才能知道它做了哪些變更。
開源庫 fsnotify 是用Go語言編寫的跨平臺文件系統(tǒng)監(jiān)聽事件庫,常用于文件監(jiān)聽,因此我們可以借助該庫來實(shí)現(xiàn)這個功能。
(1)安裝
go get -u golang.org/x/sys/... go get -u github.com/fsnotify/fsnotify
fsnotify是基于golang.org/x/sys實(shí)現(xiàn)的,并非syscall標(biāo)準(zhǔn)庫,因此在安裝的同時需要更新其版本,確保版本是最新的。
(2)案例
package main import ( "gopkg.in/fsnotify.v1" "log" ) func main() { watcher, _ := fsnotify.NewWatcher() defer watcher.Close() done := make(chan bool) go func() { for { select { case event, ok := <-watcher.Events: if !ok { return } log.Fatal("event: ", event) if event.Op&fsnotify.Write == fsnotify.Write { log.Println("modified file:", event.Name) } case err, ok := <-watcher.Errors: if !ok { return } log.Fatal("error:", err) } } }() path := "configs/config.yaml" _ = watcher.Add(path) <-done }
通過監(jiān)聽,我們可以很便捷地知道文件做了哪些變更。進(jìn)一步來說,我們可以通過對其進(jìn)行二次封裝,在它的上層實(shí)現(xiàn)一些變更動作來完成配置文件的熱更新。
使用viper開源庫實(shí)現(xiàn)熱更新
viper開源庫能夠很便捷地實(shí)現(xiàn)對文件的監(jiān)聽和熱更新。
打開pkg/setting/section.go文件,針對重載應(yīng)用配置項,新增如下處理方法:
var sections = make(map[string]interface{}) // 解析配置文件 func (s *Setting) ReadSection(k string, v interface{}) error { err := s.vp.UnmarshalKey(k, v) if err != nil { return err } if _,ok:=sections[k];!ok{ sections[k] = v } return nil }
首先修改ReadSection方法,增加讀取section的存儲記錄,以便在重新加載配置的方法中進(jìn)行二次處理。接下來新增ReloadAllSection方法,重新讀取配置,代碼如下:
// 讀取所有配置 func (s *Setting) ReadAllSections() error { for k, v := range sections { err := s.ReadSection(k, v) if err != nil { return err } } return nil } // 監(jiān)聽配置變化 func (s *Setting) WatchSettingChange() { go func() { s.vp.WatchConfig() s.vp.OnConfigChange(func(in fsnotify.Event) { _ = s.ReloadAllSections() }) }() }
最后在pkg/setting/setting.go文件中新增文件熱更新的監(jiān)聽和變更處理,代碼如下:
// 初始化配置文件的基礎(chǔ)屬性 func NewSetting(configs ...string) (*Setting, error) { vp := viper.New() vp.SetConfigName("config") for _, config := range configs { if config != "" { vp.AddConfigPath(config) } } vp.SetConfigType("yaml") err := vp.ReadInConfig() if err != nil { return nil, err } // 加入熱更新 s := &Setting{vp: vp} s.WatchSettingChange() return s, nil }
在上述代碼中,首先在NewSetting方法中起一個協(xié)程,再在里面通過WatchConfig方法對文件配置進(jìn)行監(jiān)聽,并在OnConfigChange方法中調(diào)用剛剛編寫的重載方法ReloadAllSection來處理熱更新的文件監(jiān)聽事件回調(diào),這樣就可以“悄無聲息”地實(shí)現(xiàn)一個文件配置熱更新了。
OnConfigChange方法的回調(diào)方法形參,其實(shí)就是fsnotify。
原文鏈接:https://juejin.cn/post/7133036743493681188
相關(guān)推薦
- 2022-08-25 Python??中的pass語句語法詳析_python
- 2022-10-04 python?Pillow圖像降噪處理顏色處理_python
- 2022-12-14 C++?Boost?shared_ptr共享指針詳細(xì)講解_C 語言
- 2022-07-28 Go語言反射reflect.Value實(shí)現(xiàn)方法的調(diào)用_Golang
- 2022-07-01 使用Python讀寫多個sheet文件_python
- 2022-03-26 Postman配置多環(huán)境請求地址的實(shí)現(xiàn)_相關(guān)技巧
- 2022-09-19 Pytorch實(shí)現(xiàn)LSTM案例總結(jié)學(xué)習(xí)_python
- 2022-04-27 一文教你如何封裝安全的go_Golang
- 最近更新
-
- window11 系統(tǒng)安裝 yarn
- 超詳細(xì)win安裝深度學(xué)習(xí)環(huán)境2025年最新版(
- Linux 中運(yùn)行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎(chǔ)操作-- 運(yùn)算符,流程控制 Flo
- 1. Int 和Integer 的區(qū)別,Jav
- spring @retryable不生效的一種
- Spring Security之認(rèn)證信息的處理
- Spring Security之認(rèn)證過濾器
- Spring Security概述快速入門
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權(quán)
- redisson分布式鎖中waittime的設(shè)
- maven:解決release錯誤:Artif
- restTemplate使用總結(jié)
- Spring Security之安全異常處理
- MybatisPlus優(yōu)雅實(shí)現(xiàn)加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務(wù)發(fā)現(xiàn)-Nac
- Spring Security之基于HttpR
- Redis 底層數(shù)據(jù)結(jié)構(gòu)-簡單動態(tài)字符串(SD
- arthas操作spring被代理目標(biāo)對象命令
- Spring中的單例模式應(yīng)用詳解
- 聊聊消息隊列,發(fā)送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠(yuǎn)程分支