goim 文章系列(共5篇):html
- goim 架構與定製
- 從goim定製, 淺談 golang 的 interface 解耦合與gRPC
- goim中的 bilibili/discovery (eureka)基本概念及應用
- goim 的 data flow 數據流
- goim的業務集成(分享會小結與QA)
有個 slack 頻道, 很多朋友在交流 goim , 歡迎加入slack #goimgit
goim 官網 goim.iogithub
goim 源碼 github.com/Terry-Mao/g…golang
goim 是 很是成功的 IM (Instance Message) 即時消息平臺, 依賴項爲 kafka ( 消息隊列) + zookeeper ( 擴展/均衡 ) + bilibili/discovery( 在 netflix/eureka上擴展的服務註冊與發現, golang 實現)redis
做爲一個曾經的架構師(2005~2014, Utstarcom IPTV/OTT 事業部) 與當前自由職業者(你懂的~~~~), 時常在 Golang 圈轉轉, 有朋友聊到IM 並提到goim, 我做了一些學習與研究json
中國 B站( BiliBili ) 的技術領軍 毛劍 是我神交以久的技術專家, goim 是一個很是成功的架構示例, 其模塊拆分, 接口設計, 技術選型 ,部署方式 以及持續改進演變, 都是一個互聯網商用項目典範.bash
同時, 另外一位技術專家 Xin.zh 的文章 一套高可用實時消息系統實現 給我很大啓發.cookie
在電信/廣電的幾年經歷, 這一次, 閒來無事, 算是滿懷着在巨人肩頭的感謝與敬意, 嘗試寫一些代碼來加深學習.session
感謝兩位技術專家, 感謝開源社區架構
我的在 Utstarcom 以業務平臺架構師/解決方案工程師/ IPTV播控產品線 release manager 角色折騰過比較長一些時間, 除了技術方案的原型代碼撰寫與現場應急幫忙修bug 之外, 甚少參撰寫商用項目中的代碼, 此次寫寫代碼也是有趣的練習 :P
歡迎指點/交流....
goim 是 bilibili, 簡稱B站 的彈幕業務解決方案的開源實現, 因此, 業務場景, 想一想彈幕
原圖在這裏
我重繪了這張圖, 把各網元, 及外部網元關係標示清晰一些
說明: 下圖右側 http client 是 goim push message 接口, 我標註了 backend 只是我的習慣, 事實上這只是個即時消息發送接口, 無所謂先後臺
comet / job / logic 支持多實例部署, 這是 goim 分佈式架構設計的精粹. 同時, push message 消息發佈接口從 comet 拆分也有必定的考量, 畢竟多數IM 尤爲是 bilibili 的業務場景上來講, 發送量少, 而閱讀量多, 想一想彈幕的業務場景就明白了.
goim 採用 bilibili/discovery 實現註冊/服務發現, 從而實現分佈式路由與動態調度, 相關細節參看 bilibili/discovery 文檔, 以及 Netflix/eureka 原始設計文檔
配置 discovery 時, 注意 region / zone / env 的相互匹配對應關係
測試部署請注意 redis-server 儘可能只要部署一個實例或一個集羣(至關於單實例), kafka / zookeeper 相對簡單, 部署多少都行, 配置對接上就行
goim 源碼很少, 閱讀簡單也算是 golang 語言的特色, 在 goim 尤爲如此. ( 推薦用 goland 閱讀代碼) 下圖中 goim 各網元的內部邏輯組件(邏輯單元), 以及各邏輯接口的相互關係, 能夠對照源碼自行閱讀, 擴展
請注意各網元的鏈接線, 箭頭標示了數據/信令的流向
在學習過程當中, 網上問到比較多的定製問題有幾個, 分別以下
下面畫出 goim 定製擴展, 或優化的一個可行方式
下面的源碼標記出處在 github.com/Terry-Mao/g…
與個人 repo github.com/tsingson/go… 並不相同!!!
我 fork 的代碼庫中, 消息隊列抽象成爲golang 的 interface , 而且 discovery 正在抽離處理中
源碼在文件 /internal/logic/dao/kafka.go 中
// PushMsg push a message to databus.
func (d *Dao) PushMsg(c context.Context, op int32, server string, keys []string, msg []byte) (err error) {
pushMsg := &pb.PushMsg{
Type: pb.PushMsg_PUSH,
Operation: op,
Server: server,
Keys: keys,
Msg: msg,
}
//
// 即時消息存儲擴展 HOOKS:
// 在這裏增長即時消息存儲擴展
// 若是須要只存儲離線消息, 能夠先檢查當前用戶是否在線, 依據用戶在線狀況處理存儲
//
b, err := proto.Marshal(pushMsg)
if err != nil {
return
}
m := &sarama.ProducerMessage{
Key: sarama.StringEncoder(keys[0]),
Topic: d.c.Kafka.Topic,
Value: sarama.ByteEncoder(b),
}
if _, _, err = d.kafkaPub.SendMessage(m); err != nil {
log.Errorf("PushMsg.send(push pushMsg:%v) error(%v)", pushMsg, err)
}
return
}
複製代碼
源碼在 /internal/logic/conn.go
// Connect connected a conn.
func (l *Logic) Connect(c context.Context, server, cookie string, token []byte) (mid int64, key, roomID string, accepts []int32, hb int64, err error) {
var params struct {
Mid int64 `json:"mid"`
Key string `json:"key"`
RoomID string `json:"room_id"`
Platform string `json:"platform"`
Accepts []int32 `json:"accepts"`
}
if err = json.Unmarshal(token, ¶ms); err != nil {
log.Errorf("json.Unmarshal(%s) error(%v)", token, err)
return
}
mid = params.Mid
roomID = params.RoomID
accepts = params.Accepts
hb = int64(l.c.Node.Heartbeat) * int64(l.c.Node.HeartbeatMax)
//
// 用戶管理 HOOKS
// 這裏增長用戶管理邏輯代碼, 好比:
// 1. 調用用戶管理模塊( 好比 UMS) 檢查 mid ( 會員ID / 用戶 ID ) 是否存在
// 2. 檢查用戶與 room 的權限關係
//
// 補充: 通常來講, goim 就做爲一個即時消息服務, 用戶註冊/用戶認證等業務應該由 goim 之外的網元或子系統完成
// 這裏的 HOOKS 只要提供一個與用戶管理子系統/會話管理子系統的相應接口調用就能夠了
//
// 會話管理 HOOKS
// key 是會話ID ( session ID) , 在這裏增長會話管理邏輯代碼, 好比:
// 1. 檢查會話 ID 是否合法
// 2. 若是不合法, 爲受權用戶建立會活ID
//
// 下面這個 if 代碼段, 是一個簡化掉的例子:
if key = params.Key; key == "" {
keyUuid, _ := uuid.NewV4()
key = keyUuid.String()
}
// 這裏是保存用戶會話
if err = l.dao.AddMapping(c, mid, key, server); err != nil {
log.Errorf("l.dao.AddMapping(%d,%s,%s) error(%v)", mid, key, server, err)
return
}
//
log.Infof("conn connected key:%s server:%s mid:%d token:%s", key, server, mid, token)
return
}
複製代碼
因爲學習目的, 及深刻閱讀源碼的需求, 簡化了 kafka / zk 的複雜部署參數配置與 jvm 依賴, 我 fork 了 goim 並修改成 nats + liftbridge, 由 nats 實現 簡化掉 kafka 隊列功能 + zookeeper , 由 liftbridge 實現 nats 消息的持久化
源碼見這裏 github.com/tsingson/go… 上做一些擴展學習
網名 tsingson (三明智, 江湖人稱3爺)
原 ustarcom IPTV/OTT 事業部播控產品線技術架構溼/解決方案工程溼角色(8年), 自由職業者,
喜歡音樂(口琴,是第三/四/五屆廣東國際口琴嘉年華的主策劃人之一), 攝影與越野,
喜歡 golang 語言 (商用項目中主要用 postgres + golang )
tsingson 寫於中國深圳 小羅號口琴音樂中心, 2019/04/21
_
_
_
_
相關文章被轉載在
studygolang.com/ 保留了個人我的署名/我的介紹, 以及 readhub.cn/tech 幾乎對個人幾篇文章都在次日進行了引用推薦, 並對文章引用署名做者爲 掘金 | tsingson
謝謝了
雖然只是一個小小的署名, 但體現了相互的尊重.
文章發表在互聯網, 發佈在我的博客與掘金, 就是爲了分享, 因此也沒有特別加版權著做權這些聲明瞭, 但轉載文章,保留原做者的署名, 是基本的相互尊重吧.............
爲 readhub.cn/tech / studygolang.com/ 點個贊!!
--------------- tsingson 於 2019/06/04 香港銅鑼灣
總之, 歡迎轉載個人原創文章.
能夠的話, 請保留個人原創文章署名, 固然, 不署名, 留空也行, 只要別把誰誰的名字替換掉原做者名稱, 或聲稱爲某某本身的原創文章, 也是挺好的...
就這樣, 祝愉快.