Go -- 漫談IM通信架構


前前后后做的IM和推送系統已經有好幾個了,一直都想好好總結下,因此就有了這篇文章。在我剛學編程的那會兒,覺得網絡通信是一個很牛逼和門檻很高的一門技術,但是隨着開源技術的發展和互聯網知識的共享,現在要寫出高質量的網絡通信程序已經變得容易多了。

只要談通訊肯定繞不開協議,鑒於本人經驗下面只談本人擼過的三種協議:

轉自: http://www.yangguo.info/2015/08/17/%E6%BC%AB%E8%B0%88%E9%80%9A%E8%AE%AF%E6%9E%B6%E6%9E%84/

  1. XMPP
  2. MQTT
  3. 私有協議

XMPP

XMPP(Extensible Messaging and Presence Protocol),也叫Jabber,它是基於穩定長連接網絡環境所設計的,對於不夠穩定和帶寬小的移動網絡不是非常合適。由於XMPP基於XML,所以流量大,流量問題對於移動網絡來說非常敏感,然后就是消息不可靠、CMWAP兼容、開源項目對協議實現不完善等問題,也是XMPP面臨的問題。當然XML可以通過精簡壓縮來實現流量可控,目前這也是XMPP優化的可行方案,消息的不可靠可以通過擴展XMPP來實現ACK,隨着3/4G的發展,CMWAP網關畢竟是末日黃花,但是開源項目對協議只是部分實現等問題,也是使用XMPP繞不過去的坎。Openfire是XMPP領域最知名的開源項目,它簡單易用,是很多團隊的首選方案,這是國內使用最多的開源方案。Openfire雖然優點很多,但是缺點也不少,最致命的就是它的分布式擴展能力很弱,當用戶量很大的時候,水平擴展就成為它的瓶頸所在。還有一個不得不提的項目就是Tigase,這是筆者接觸的第一個XMPP開源項目,它在分布式擴展能力上和架構設計上比Openfire強了不少。由於該項目開始是一個私人項目,現在好像在商業化,所以使用者並不是很多,雖然國外有成熟案例,但是國內目前並不多,所以當時趟了Tigase的很多坑,目前平安好醫生的聊天系統就是基於此搭建的。如果對Tigase感興趣,可以閱讀我之前寫的一篇文章《Tigase集群方案及配置說明文檔》。不論使用哪個開源項目,雖然看起來開箱即用,但是要成為穩定成熟的產品,還需要深度的二次開發才行。

雖然XMPP有很多弊端,但是它的生態目前是最完善的,如果從成本角度來考量,XMPP是前期投入最小產出最快的。但是如果是搭建一個SAAS平台或者千萬量級的IM,XMPP就不是最優的選擇了。當然這是一家之言,國內外目前商業化的IM SAAS平台有好幾家都是基於XMPP實現的,這個大家可以自行Google。


MQTT

MQTT是輕量級基於代理的發布/訂閱的消息傳輸協議,它的最大特點就是協議開銷非常小,伴隨着的就是協議簡單(40多頁)、網絡帶寬要求極低和移動設備省電。有幸接觸到該協議是筆者在開發Android推送系統時,對它進行了較細致的研究,雖然最終方案中沒有使用該協議,但是自己定制的私有協議也參考了很多MQTT的設計。MQTT整個協議的組成,可以分為三個部分:

  1. 固定頭部:通用消息數據包格式
  2. 可變頭部:特定消息數據包格式
  3. 消息體:有效載荷

固定頭部

每個MQTT命令消息的消息頭部都包含一個固定頭部,固定頭部的格式如下:

fixed header

Byte 1
消息類型和標志字段

Byte 2
剩余長度字段(至少1個字節,最多4個字節),采用big-endian模式存儲

Message Type

mqtt-fixed-header-message1

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
0:保留
1:客戶端請求連接服務器
2:連接確認
3:發布消息
4:發布確認
5:發布接收(有保證的交付第1部分)
6:發布釋放(有保證的交付第2部分)
7:發布完成(有保證的交付第3部分)
8:客戶端訂閱請求
9:訂閱確認
10:客戶端取消訂閱請求
11:取消訂閱確認
12:PING請求
13:PING回復
14:客戶端斷開連接
15:保留

DUP(Duplicate delivery)

保證消息可靠傳輸,默認為0,只占用一個bit,表示是否第一次發送,它不能用於檢測消息重復發送。只適用於客戶端或服務器端嘗試重發PUBLISH, PUBREL, SUBSCRIBE 或 UNSUBSCRIBE消息,注意需要滿足以下條件:

1
2
QoS > 0
即消息需要回復確認

此時,在可變頭部需要包含消息ID。當值為1時,表示當前消息先前已經被傳送過。

Qos(Quality of Service)

該標志位標明 PUBLISH 消息的交付質量級別:

mqtt-fixed-header-message1

RETAIN

僅針對PUBLISH消息。不同值,不同含義:

1:表示發送的消息需要一直持久保存(不受服務器重啟影響),不但要發送給當前的訂閱者,並且以后新來的訂閱了此Topic name的訂閱者會馬上得到推送。

備注:新來乍到的訂閱者,只會取出最新的一個RETAIN flag = 1的消息推送。

0:僅僅為當前訂閱者推送此消息。

假如服務器收到一個空消息體(zero-length payload)、RETAIN = 1、已存在Topic name的PUBLISH消息,服務器可以刪除掉對應的已被持久化的PUBLISH消息。

Remaining Length

這個字節包含當前消息的剩余部分,包括變量頭部和負載的數據。

可變長度的編碼方式使用一個單獨的字節使消息可以達到127字節的長度上限。協議限制最多4個字節,這樣程序可以發送最大256M的消息。

上面便是最核心的固定頭部的內容,至於可變頭部和消息體可以自己查詢資料,目前有很多公司在使用MQTT實現Android的推送,但是目前筆者暫時不知道誰家的IM在使用它。

私有協議

一萬人眼中就有一萬個哈姆雷特,同樣的一萬人眼中就有一萬個私有協議。應用場景、設計風格,都會導致協議的設計千奇百怪。例如:數據量傳輸大的場景,壓縮方案可能也被設計到協議中,因為不同的環境可能用到不同的壓縮方式;傳輸質量,我們可能就默認某一個級別,可能就從協議中移除,具體的設計得靠經驗和應用場景來設計。


架構

做了好幾個系統,我將我喜歡使用的一套架構拋出來供大家探討。
mqtt-fixed-header-message1

  • CM-*:Connection Manager,可以分為WebSocket和Tcp兩種承載方式。
  • SM:Session Manager。
  • Web:Rest接口,HTTP承載。歷史消息,好友關系,個人信息管理等。

一套很簡單的架構,CM只負責鏈路的管理,鏈路和用戶ID的關系維護在Redis中,SM負責業務邏輯和消息路由。CMSM內部通過RPC調用,CMSM內部全部采用事件驅動的方式,全部采用異步的方式。任何一個模塊都可以水平擴展,並且SM如果達到非常復雜的地步,還可以拆分。最終的壓力基本就到了Redis和Mysql,這些高可用和高並發的方案,已經非常成熟,就不用多說了。

下圖是登錄流程和消息發送流程

login

message

 

鑒於筆者經驗,開發的IM最多承載用戶數也就百萬級別,所以架構上或者設計方案不一定完美,僅供參考!

注意事項

    1. CM一定要采用多隊列網卡,否者會出現服務器的一個CPU 100%,而別的CPU卻很空閑,從而導致系統吞吐量上不去。因為單隊列網卡的I/O中斷都被分配到了一個CPU核上,大量數據包到來時,單個CPU核無法全部處理,導致LVS不斷丟包連接中斷。


免責聲明!

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



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