goim 文章系列(共5篇):node
- goim 架構與定製
- 從goim定製, 淺談 golang 的 interface 解耦合與gRPC
- goim中的 bilibili/discovery (eureka)基本概念及應用
- goim 的 data flow 數據流
- goim的業務集成(分享會小結與QA)
有個 slack 頻道, 很多朋友在交流 goim , 歡迎加入slack #goimgit
在學習 goim 過程當中, bilibili/discovery 是一個服務註冊/發現的依賴網元, golang 實現了 netflix/eureka 並做了一些擴展改進github
這裏順帶記錄了對 bilibili/discovery 學習過程當中的一些理解golang
上圖標示了 bilibili/discovery 在 goim 中的位置, 與做用(以 comet / job 爲例):緩存
若是 discovery 網元不存在, 那很簡單, job 在配置文件中寫死 comet 地址( 一到多個), job 的 comet-gRPC-client 直接向 comet 的 comet-gRPC-server 進行互通完成業務. 這樣就失去了分佈式的動態擴展能力bash
discovery 之間, 會同步註冊的服務實例信息服務器
注意 在 bilibili/discovery 中, discovery 自己被標記爲架構
_appid = "infra.discovery"
複製代碼
相互之間同樣進行相互註冊/更新, 同在相互之間同步 名爲"infra.discovery" 與其餘 app 的實例信息app
在 discovery 的配置文件中, discovery 實例被稱爲 node , 由 nodes 參數進行配置, 配置定義以下curl
// Config discovery configures.
type Config struct {
Nodes []string # ******************** 這是配置一到多個 discovery 實例的定義
Region string
Zone string
Env string
Host string
}
複製代碼
discovery / eureka 中的基本概念, 如上圖所示, 就是一個分區進行註冊/調度的簡單劃分
注: bilibili/discovery 是以 http 方式提供註冊/更新/發現/同步...等服務註冊與發現等業務功能
因此, 能夠看到 discorevy 獲取一個服務器節點, 是以下方式
curl 'http://127.0.0.1:7171/discovery/fetch?zone=gd&env=dev&appid=goim.comet&status=1'
複製代碼
上面 URL 中, zone 對應就是獲取 gd 廣東區域內, 環境定義爲 dev , appID 爲 goim.comet 的服務器實例, 固然, status 是附加約束, 這裏 status=1 表示過濾名稱爲 goim.comet 的服務器實例狀態要求爲 status = 1 ( 即接收服務請求的 goim.comet 實例列表)
上面的 curl 會返回如下結果
{
"code": 0,
"data": {
"instances": {
"gd": [
{
"zone": "gd", # *** ** 可用區域
"env": "dev", # ****** 運行環境
"appid": "goim.comet", # ****** appID 名稱
"hostname": "hostname000000",
"version": "111",
"metadata": {
"provider": "",
"weight": "10"
},
"addrs": [
"http://172.1.1.1:8080",
"gorpc://172.1.1.1:8089" # ****** 有效的 gRPC 地址
],
"status": 1,
"reg_timestamp": 1525948301833084700,
"up_timestamp": 1525948301833084700,
"renew_timestamp": 1525949202959821300,
"dirty_timestamp": 1525948301848680000,
"latest_timestamp": 1525948301833084700
}
]
},
"latest_timestamp": 1525948301833084700
}
}
複製代碼
discovery/ eureka 換成 DNS 域名 能夠在邏輯上表示爲 schema://appID.Env.Zone.Region , 相似於 grpc://goim.comet.dev.gd.china.xxxxx.com
換成 etcd 能夠表示爲 /Region/Zone/Env/appID, 例如 "/china/gd/dev/goim.comet"
由上小節可知, bilibili/discovery 或 netflix/eureka 的配置中, 如下4個關鍵參數, 須要一一對應
在 goim 中, appID 已經在代碼中標記爲常量, 以下
# github.com/Terry-Mao/goim/cmd/comet/main.go
const (
ver = "2.0.0"
appid = "goim.comet"
)
# github.com/Terry-Mao/goim/cmd/logic/main.go
const (
ver = "2.0.0"
appid = "goim.logic"
)
複製代碼
仍是以 logic / comet 之間的 gRPC 爲例
全部使用 bilibili/discovery 的配置是相似的, 在配置中, 包含如下定義
原始定義在 github.com/bilibili/di… 第 46行開始
// Config discovery configures.
type Config struct {
Nodes []string # ******************** 這是配置一到多個 discovery 實例的定義
Region string
Zone string
Env string
Host string
}
複製代碼
在 comet 配置中定義爲
在 comet 配置源文件中 github.com/Terry-Mao/g… 第 112 行
// Config is comet config.
type Config struct {
Debug bool
Env *Env # ******************** 這裏這裏這裏
Discovery *naming.Config # ******************** 這裏這裏這裏
TCP *TCP
Websocket *Websocket
Protocol *Protocol
Bucket *Bucket
RPCClient *RPCClient
RPCServer *RPCServer
Whitelist *Whitelist
}
複製代碼
在 job 配置源文件中 github.com/Terry-Mao/g… 第 59 行
// Config is job config.
type Config struct {
Env *Env # ******************** 這裏這裏這裏
Kafka *Kafka
Discovery *naming.Config # ******************** 這裏這裏這裏
Comet *Comet
Room *Room
}
複製代碼
就像第二節所說的, regoin / zone / env , 因此, 重點關注 Env / Discovery 兩個配置定義, 重點在 Discovery 配置naming.Config 便可
源代碼見 github.com/Terry-Mao/g… 第42/43 行
// register discovery
dis := naming.New(conf.Conf.Discovery)
resolver.Register(dis)
複製代碼
該 comet 的註冊信息更新代碼放在一個 goroutine 中, 每10秒更新一次
源代碼見 github.com/Terry-Mao/g… 第42/43 行
if err = dis.Set(ins); err != nil {
log.Errorf("dis.Set(%+v) error(%v)", ins, err)
time.Sleep(time.Second)
continue
}
time.Sleep(time.Second * 10)
複製代碼
在 job 代碼中, 含有服務註冊代碼, 其實是無用代碼, 緣由是, 只有服務端才須要進行服務註冊, 而 job 實際上只有兩個業務關聯邏輯
代碼在 github.com/Terry-Mao/g… 第 28行
// grpc register naming
dis := naming.New(conf.Conf.Discovery)
resolver.Register(dis)
複製代碼
代碼在 github.com/Terry-Mao/g… 第 85行
func (j *Job) watchComet(c *naming.Config) {
dis := naming.New(c) # **************************** 構造符合 gRPC 要求的服務發現實例
resolver := dis.Build("goim.comet")
event := resolver.Watch() # **************************** 監聽 服務發現, 這裏返回一個 channel
select { # **************************** 從 channel 中循環獲取返回
case _, ok := <-event:
if !ok {
panic("watchComet init failed")
}
if ins, ok := resolver.Fetch(); ok { # **************************** ins 便是返回的實例
if err := j.newAddress(ins.Instances); err != nil {
panic(err)
}
log.Infof("watchComet init newAddress:%+v", ins)
}
case <-time.After(10 * time.Second):
log.Error("watchComet init instances timeout")
}
go func() {
for {
if _, ok := <-event; !ok {
log.Info("watchComet exit")
return
}
ins, ok := resolver.Fetch() # **************************** ins 便是返回的實例
if ok {
if err := j.newAddress(ins.Instances); err != nil {
log.Errorf("watchComet newAddress(%+v) error(%+v)", ins, err)
continue
}
log.Infof("watchComet change newAddress:%+v", ins)
}
}
}()
}
複製代碼
.............稍後一一道來, 哈, 先去掙點錢先.............
.
.
歡迎交流與批評..... .
.
關於我
網名 tsingson (三明智, 江湖人稱3爺)
原 ustarcom IPTV/OTT 事業部播控產品線技術架構溼/解決方案工程溼角色(8年), 自由職業者,
喜歡音樂(口琴,是第三/四/五屆廣東國際口琴嘉年華的主策劃人之一), 攝影與越野,
喜歡 golang 語言 (商用項目中主要用 postgres + golang )