go kratos源碼及配置解析
相關(guān)類圖
項目啟動時一般前置條件為解析配置文件, 我們看下這塊是怎么設(shè)計的.
流程解釋
- config 實現(xiàn) config Interface 接口, 初始化reader對象
- 調(diào)用Load方法, 通過入口注入的不同文件源, 調(diào)用實現(xiàn)了source interface 對應(yīng)的Load方法(以file舉例子)
- file 通過 Load 入口, 判斷是目錄還是文件, 執(zhí)行對應(yīng)的方法, 拿到文件內(nèi)容返回
- config 拿到file返回的內(nèi)容, 交給reader去合并
- reader 根據(jù)文件格式(yml) 轉(zhuǎn)為 map[string]interface{} 結(jié)構(gòu), 并進行覆蓋合并, 將結(jié)果存到對象變量上
- reader 處理完成后, config調(diào)用 file watcher 進行文件變更監(jiān)聽, 啟動協(xié)程監(jiān)聽
由此可見, config對象其實作為入口, 將數(shù)據(jù)生產(chǎn)交給file、env, 將數(shù)據(jù)加工解析交給 reader
代碼案例
init 接收外部參數(shù)定義解析配置文件地址
func init() { flag.StringVar(&commonconf, "common", "./configs/local", "common config path, eg: -conf config.yaml") flag.StringVar(&flagconf, "conf", "./configs/local/api", "config path, eg: -conf config.yaml") flag.Parse() }
使用config.New初始化文件配置
//初始化配置 //新增兩個配置源, 文件格式 common and flagconf 路徑 c := config.New( config.WithSource( file.NewSource(commonconf), file.NewSource(flagconf), ), ) // 關(guān)閉watch相關(guān)的監(jiān)聽器 defer c.Close() // 加載配置文件 if err := c.Load(); err != nil { panic(err) } //解析配置到bc結(jié)構(gòu)上 var bc conf.Bootstrap if err := c.Scan(&bc); err != nil { panic(err) }
我們看下config.New的實現(xiàn)
// 初始化解析器, 關(guān)聯(lián)reader對象, 數(shù)據(jù)交給由reader加工和存儲 func New(opts ...Option) Config { o := options{ decoder: defaultDecoder, resolver: defaultResolver, } for _, opt := range opts { opt(&o) } return &config{ opts: o, reader: newReader(o), } }
Options 有下面幾個屬性
type options struct { sources []Source // 配置源, 由初始化負責(zé)傳入的source配置源 decoder Decoder // 解析器 resolver Resolver // 變量解析替換 }
執(zhí)行 Load 配置加載
func (c *config) Load() error { // 因為我們在入口傳入的是file對象, 所以執(zhí)行src load的時候也是file對象的 load 方法 for _, src := range c.opts.sources { // 獲取文件內(nèi)容(可能是一個目錄, 會存在多個文件) kvs, err := src.Load() if err != nil { return err } for _, v := range kvs { log.Debugf("config loaded: %s format: %s", v.Key, v.Format) } // 合并配置key if err = c.reader.Merge(kvs...); err != nil { log.Errorf("failed to merge config source: %v", err) return err } // 調(diào)用file watch, 監(jiān)聽文件變化 w, err := src.Watch() if err != nil { log.Errorf("failed to watch config source: %v", err) return err } c.watchers = append(c.watchers, w) // 異步監(jiān)聽文件變化(調(diào)用對應(yīng)的watch對象) go c.watch(w) } // 解析內(nèi)容中是否包含 ${APPID:default} 變量 // 如果在配置文件中存在 APPID: xx 配置, 則進行替換 // 否則使用default默認值 if err := c.reader.Resolve(); err != nil { log.Errorf("failed to resolve config source: %v", err) return err } return nil }
異步watch
func (c *config) watch(w Watcher) { for { kvs, err := w.Next() if err != nil { if errors.Is(err, context.Canceled) { log.Infof("watcher's ctx cancel : %v", err) return } time.Sleep(time.Second) log.Errorf("failed to watch next config: %v", err) continue } // 處理邏輯忽略 ..... c.cached.Range(func(key, value interface{}) bool { k := key.(string) v := value.(Value) if n, ok := c.reader.Value(k); ok && reflect.TypeOf(n.Load()) == reflect.TypeOf(v.Load()) && !reflect.DeepEqual(n.Load(), v.Load()) { v.Store(n.Load()) if o, ok := c.observers.Load(k); ok { o.(Observer)(k, v) } } return true }) } }
Scan 將配置轉(zhuǎn)換成結(jié)構(gòu)體
var bc conf.Bootstrap if err := c.Scan(&bc); err != nil { panic(err) }
以上就是go kratos源碼及配置解析的詳細內(nèi)容,更多關(guān)于kratos源碼配置的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
基于Go?goroutine實現(xiàn)一個簡單的聊天服務(wù)
對于聊天服務(wù),想必大家都不會陌生,因為在我們的生活中經(jīng)常會用到,本文我們用?Go?并發(fā)來實現(xiàn)一個聊天服務(wù)器,這個程序可以讓一些用戶通過服務(wù)器向其它所有用戶廣播文本消息,文中通過代碼示例介紹的非常詳細,需要的朋友可以參考下2023-06-06一文帶你了解Go語言標(biāo)準(zhǔn)庫strings的常用函數(shù)和方法
strings?庫包含了許多高效的字符串常用操作的函數(shù)和方法,巧用這些函數(shù)與方法,能極大的提高我們程序的性能。本文就來和大家分享一下Go標(biāo)準(zhǔn)庫strings的常用函數(shù)和方法,希望對大家有所幫助2022-11-11Go?Web開發(fā)之Gin多服務(wù)配置及優(yōu)雅關(guān)閉平滑重啟實現(xiàn)方法
這篇文章主要為大家介紹了Go?Web開發(fā)之Gin多服務(wù)配置及優(yōu)雅關(guān)閉平滑重啟實現(xiàn)方法詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2024-01-01go local history本地歷史恢復(fù)代碼神器
這篇文章主要為大家介紹了go local history本地歷史恢復(fù)代碼神器的使用功能詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2024-01-01