Go -- 漫談IM通訊架構

前先後後作的IM和推送系統已經有好幾個了,一直都想好好總結下,所以就有了這篇文章。在我剛學編程的那會兒,以爲網絡通訊是一個很牛逼和門檻很高的一門技術,可是隨着開源技術的發展和互聯網知識的共享,如今要寫出高質量的網絡通訊程序已經變得容易多了。sql

只要談通信確定繞不開協議,鑑於本人經驗下面只談本人擼過的三種協議:編程

轉自: 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 = 一、已存在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不斷丟包鏈接中斷。
相關文章
相關標籤/搜索