goim源碼分析與二次開發-comet分析一


因為要完成一個聊天的項目,所以借鑒了goim,第一篇分析打算半原版,先摘抄http://www.jianshu.com/p/8bd96a9a473d他的一些理解,寫這些還是為了讓自己更好的理解這個項目,如果有什么不對的,請大家給我留言。

Comet 角色定位


http://img.blog.csdn.net/20151010171504047
GOIM整體架構

在整個架構中,系統被分成 Comet, Logic, Job, Router 四大模塊,各個模塊通過 RPC 通信,參考官方中文文檔,Comet 程序是連接層,暴露給公網,所有的業務處理推給 Logic 模塊,通過 RPC 通信。這樣設計的好處在於,長連接邏輯很少變動,穩定的保持公網連接,而后端 Logic, Router 模塊經常變動,重啟不會影響連接層。

幾個重要的結構體

做為典型代碼即注釋的開源項目,goim 基本無太多閱讀障礙,幾個邏輯點梳理下很快就會明白。

Bucket: 每個 Comet 程序擁有若干個 Bucket, 可以理解為 Session Management, 保存着當前 Comet 服務於哪些 Room 和 Channel. 長連接具體分布在哪個 Bucket 上呢?根據 SubKey 一致性 Hash 來選擇。

Room: 可以理解為房間,群組或是一個 Group. 這個房間內維護 N 個 Channel, 即長連接用戶。在該 Room 內廣播消息,會發送給房間內的所有 Channel.

Channel: 維護一個長連接用戶,只能對應一個 Room. 推送的消息可以在 Room 內廣播,也可以推送到指定的 Channel.

Proto: 消息結構體,存放版本號,操作類型,消息序號和消息體。

多協議支持

Goim 支持 Tcp, Http, WebSocket, TLS WebSocket. 非常強大,底層原理一樣,下面的分析都是基於 Tcp 協議。

Bucket

先來看看結構體的定義


Bucket結構體

定義很明了,維護當前消息通道和房間的信息,方法也很簡單,加減 Channel 和 Room. 一個 Comet Server 默認開啟 1024 Bucket, 這樣做的好處是減少鎖 ( Bucket.cLock ) 爭用,在大並發業務上尤其明顯。最新的版本增加了routineamout,routinesize這兩個選項主要是用來做progress推送消息的作用,第一個是做消息推送協程個數,第二個是管道長度。比舊版buket取消了以前的roomoptions直接使用room的參數

 

Room

 


Room結構體

Room 結構體稍顯復雜一些,不但要維護所屬的消息通道 Channel, 還要消息廣播的合並寫,即 Batch Write, 如果不合並寫,每來一個小的消息都通過長連接寫出去,系統 Syscall 調用的開銷會非常大,Pprof 的時候會看到網絡 Syscall 是大頭。新版本room改動很大,以前這么同學說辭不知道是否管用,但是從結構體定義來看,room增加了下個channel和一個在線狀態。

 

消息廣播

Logic Server 通過 RPC 調用,將廣播的消息發給 Room.Push,  數據會被暫存在proto這個結構體 里,每個 Room 在初始化時會開啟一個 groutine 用來處理暫存的消息,達到 Batch Num 數量或是延遲一定時間后,將消息批量 Push 到 Channel 消息通道。

Channel

 

Channel結構體

這是一個通道。Writer/Reader 就是對網絡 Conn 的封裝,cliProto 是一個 Ring Buffer,保存 Room 廣播或是直接發送過來的消息體。

消息流轉

這里只分析 Comet 代碼,所以消息生成暫時不提

1. Client 連接到 Comet Server,  握手認證

2. 新建當前長連接的 Channel, 由於 Comet 服務不處理業務邏輯,需要 RPC 去 Logic Server 獲取該 Channel 的訂閱信息。同時 Channel 開啟一個 dispatchTCP groutine, 阻塞等待 Ring Buffer 數據可用,發送到 Client。

3. Logic 服務通過 RPC, 將消息寫到 Room (廣播)或是直接寫到指定 Channel (單播)。注意這里,廣播是有寫合並 BatchWrite, 而單播沒有,消息生成后立刻發送。

4. Room 里的廣播消息到達一定數量 Batch Num, 或是延遲等待一定時間后,將消息寫到 Channel Ring Buffer。

小結

新版本跟以前那位同學的很不一樣,第一版本由於時間關系,基本大多數是摘抄這位同學的,感覺新版本很多不對,但是閱讀代碼還沒到那個程度,所以第一版就到這里,這位同學對goim大體架子分析的很清楚,細節在我后續章節會有,如果有不對的,請及時留言糾正

 

補充-來自毛劍

1. bucket按照key的cityhash求余命中的,沒有用一致性hash,因為這里不涉及遷移

2. 私信發送其實也有合並的,和room合並不同的是,在ringbuffer取消息飢餓時候才會真正flush

3. 還有一個優化可以改進,因為room有個特點大家消息可能都一樣,所以在room提前合並成字節buffer,然后廣播所有人,避免每個人都序列化一次,然后利用gc來處理這個buffer的釋放,這樣可以節省大量cpu,目前這個優化還沒做


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM