移動端即時通訊系統(tǒng)實踐

在信息高度發(fā)達的今天,IM基本上已經(jīng)成為了一個社交應用的標配。本文將以一個移動開發(fā)者的視角,探討移動端即時通訊系統(tǒng)的技術選型和關鍵要點。

1 即時通訊系統(tǒng)的需求
任何技術系統(tǒng)都來源于真實業(yè)務的需求,做架構設計之前應該先設定好目標。作為一個即時通訊應用,可以參考微信的使用體驗,你需要保證以下特性:
1,實時。消息的接收端應該能夠及時收到并處理消息。
2,不丟。需要保證所有的消息都順利送達。
3,不重。重復的消息對用戶來說是一種糟糕的體驗。
4,保序。只要順序一亂,消息根本沒發(fā)看。
5,節(jié)能。流量可貴,電量可貴,能省則省。
6,安全。如果涉及敏感數(shù)據(jù),安全必須重視。
7,流暢。卡頓的應用是不會被用戶接受的。

2 關鍵技術點
為了保證消息的實時性,有兩種思路:
1,長輪詢方式,高頻率地從服務端拉取新消息。這種方式其實就是傳統(tǒng)的請求-響應模型,現(xiàn)在很多體育文字直播軟件也采取這種方式。這種方法雖然簡單,但有很多缺點。一是會產(chǎn)生很多請求,這對服務器的壓力和用戶的流量都是浪費。二是消息仍然不夠及時,不考慮傳輸時間,最長的延遲就是輪詢的間隔。
2,消息的生產(chǎn)者主動推送消息。這應該是更好的選擇,可以解決長輪詢的缺點。我們的即時通訊系統(tǒng)也會采用這種方式。使用長連接,而且連接必須是穩(wěn)定可靠的,才能確保消息的實時性。

2.1 數(shù)據(jù)通信協(xié)議
服務端與客戶端之間需要協(xié)商好數(shù)據(jù)格式,這是數(shù)據(jù)傳輸和數(shù)據(jù)處理的基礎。協(xié)議的設計需要著重考慮第一節(jié)提到幾點需求。
XMPP和MQTT是當前比較成熟的兩種消息協(xié)議。如果能較好地處理你的業(yè)務需求,就沒有必要重復造輪子。有很多企業(yè)的業(yè)務有特殊需求,可以考慮根據(jù)實際情況自定義協(xié)議,從頭開始顯然是不現(xiàn)實的,可以參考已經(jīng)成熟的協(xié)議再做設計和開發(fā)。具體協(xié)議內(nèi)容在此不詳細展開,只做優(yōu)劣的比較。

2.1.1 XMPP
XMPP是一種以XML為基礎的開放式即時通訊協(xié)議。
XMPP的優(yōu)點是安全,SASL及TL等技術的可靠安全性已內(nèi)置于核心XMPP技術規(guī)格中。
XMPP 協(xié)議的最主要的一點就是開放,不管是協(xié)議、客戶端,還是 Server 端,都有成熟的實現(xiàn)方案。
基于XML,它天生擁有很強的靈活性,可以在核心協(xié)議之上方便地進行定制化。Google Talk就是采用這種協(xié)議。
但是XMPP的缺點也很明顯。首先,XMPP協(xié)議的方式被編碼為一個單一的長的XML文件,因此無法提供修改二進制數(shù)據(jù)。其次,XML 有大量的標簽冗余信息,網(wǎng)絡流量的 70% 都消耗在 XMPP 協(xié)議層了,這在移動互聯(lián)網(wǎng)時代,流量和電量是一個不可忽視的消耗。

2.1.2 MQTT
MQTT協(xié)議是由IBM提出的基于發(fā)布/訂閱模型的消息傳輸協(xié)議,相比于XMPP,它顯得非常輕量小巧,協(xié)議內(nèi)容包括固定頭部+可變頭部+消息體,最下的情況下頭部只需要兩個字節(jié),在傳輸開銷上有著巨大的優(yōu)勢,可以節(jié)省流量和電量。
MQTT可以保證消息的可靠性,它包括三種不同的服務質(zhì)量(最多只傳一次、最少被傳一次、一次且只傳一次),如果客戶端意外掉線,可以使用“遺愿”發(fā)布一條消息,同時支持持久訂閱。
XMPP使用XML,是一個歷史的選擇,在現(xiàn)在移動應用的場景下,個人更加推薦MQTT。據(jù)了解,不少企業(yè),包括做IM SDK的廠商,也是在MQTT的基礎上進行自定義的擴展和修改。

2.2 連接的穩(wěn)定性
移動互聯(lián)網(wǎng)的場景下,網(wǎng)絡環(huán)境經(jīng)常變化,需要保證連接是穩(wěn)定的。

2.2.1 心跳
最經(jīng)典的做法就是使用心跳,實時地檢測連接狀態(tài)。通常是客戶端每隔一小段時間向服務器發(fā)送一個數(shù)據(jù)包,通知服務器自己仍然在線,并傳輸一些可能必要的數(shù)據(jù)。如果在一定時間內(nèi)服務器沒有響應,則認為連接可能已經(jīng)斷開,重新嘗試連接。
偽代碼如下:

while(true) {
    if (now - last_pong_msg > keep_alive) {
        socket_close();
        reconnect();
    }
    send_heartbeat_ping();
    // 只是為了表示每keep_alive時間段發(fā)一次心跳
    sleep(keep_alive);
}

上述代碼的心跳間隔是固定的。由于心跳包也是會消耗流量的,因此應該找到一個理想的心跳周期,在能敏銳地察覺連接變化的前提下,盡量大地增加周期間隔。因此可以做一個優(yōu)化,是使心跳間隔動態(tài)增加。

2.2.2 多連接嘗試
(1)多連接嘗試
考慮到不同地區(qū)不同網(wǎng)絡運營商的情況下,用戶可能因為網(wǎng)絡限制,連接不上我們的服務或者比較慢。我們在實踐中就發(fā)現(xiàn),某些網(wǎng)絡運營商將某些端口封禁了,導致部分用戶連接不上服務。為了解決這個問題,可以提供多個ip和多個端口,客戶端在連接某個ip比較慢的情況下,可以進行輪詢,切換到一個更快的ip。
(2)長連接與短連接結合
這只是一條退路,而不是常規(guī)武器。
在長連接實在連接不上的情況下,可以考慮做降級,使用短連接長輪詢的方式進行替代。

2.3 服務質(zhì)量
系統(tǒng)的設計往往存在著取舍和妥協(xié)。正如TCP比UDP更加可靠,但它的負載會更高。
在即時通訊系統(tǒng)中也存在著取舍的問題,是追求極速送達,還是在傳輸上做可靠性的保證,確保不丟不重?不同的業(yè)務類型可能需要不同的服務質(zhì)量,MQTT協(xié)議提供了三種服務質(zhì)量,可以作為參考:
QoS 0: 至多發(fā)送一次,發(fā)送即丟棄。沒有確認消息,也不知道對方是否收到。針對的消息不重要,丟失也無所謂。
QoS 1: 至少發(fā)送一次。發(fā)送之后,會等待接收方ack確認。在一定時間之內(nèi),如果沒有收到ack,則會再發(fā)一次,一直到接收方收到。重發(fā)的消息會在頭部有dup標示。這種QoS可以保證消息不丟,但接收方可能會有重復消息,需要做去重。如下圖所示:

QoS 2:有且僅有一次。可以保證不丟不重,但是通信壓力高,需要多次握手。如下圖所示:


3 客戶端實現(xiàn)

3.1 消息處理
發(fā)送消息比較簡單,只需要往某一個topic發(fā)布即可。
接收消息的流程如下:

收消息:客戶端需要保持一個長連接,并且確保連接穩(wěn)定,如上章節(jié)所示。
消息過濾:如果發(fā)送端不能確保消息不重(如mqtt中QoS為0或1),客戶端需要做去重,因此消息需要有一個唯一的id。
消息合并和分發(fā):在實際使用場景中,往往有各種各樣的消息類型(如聊天消息、系統(tǒng)通知等),對同一類型的消息可以做合并,以加快后續(xù)消息處理速度。而不同類型的消息則分發(fā)到各自的處理器當中,如存儲到本地數(shù)據(jù)庫,通知頁面更新等)
UI更新:客戶端一般是使用列表來展示消息(iOS中是UITableView,Android中是ListView),而列表的數(shù)據(jù)量可能很大,數(shù)據(jù)源更新頻率也可能很頻繁,因此需要對列表做性能優(yōu)化,以確保用戶體驗。以iOS為例,使用Instruments監(jiān)控性能瓶頸的地方,對內(nèi)存占用和CPU占用大戶進行優(yōu)化。確定問題后,常用的技巧有:對cell高度做緩存;簡化UI層次結構;避免大量的離屏渲染;減少混合圖層;等等。對癥下藥,各個擊破。

3.2 其他
IM應用中,還有很多常用的實現(xiàn)需求,例如表情鍵盤,圖片語音等多媒體的存儲和下載隊列等。但這不在系統(tǒng)實現(xiàn)的范疇中,將來后有文章進行詳細闡述。
參考:
https://en.wikipedia.org/wiki/XMPP
http://www.blogjava.net/yongboy/archive/2014/02/15/409893.html

最后編輯于
?著作權歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內(nèi)容

  • 前言 本文會用實例的方式,將iOS各種IM的方案都簡單的實現(xiàn)一遍。并且提供一些選型、實現(xiàn)細節(jié)以及優(yōu)化的建議。 注:...
    涂耀輝閱讀 94,711評論 219 1,750
  • Spring Cloud為開發(fā)人員提供了快速構建分布式系統(tǒng)中一些常見模式的工具(例如配置管理,服務發(fā)現(xiàn),斷路器,智...
    卡卡羅2017閱讀 134,991評論 19 139
  • 2016年,上班的第一天。一切都要全新開始。
    kind_lion閱讀 107評論 0 0
  • 變量和常量 注釋 Integers 類型別名 Bool //元組 可選型 Swift’s nil is not t...
    歲月蹉跎繁華落寞閱讀 494評論 0 0
  • -1- 一位朋友上傳到微信群一段本地視頻,看得我身臨其境,膽戰(zhàn)心驚,手心冒汗。 視頻顯示,一輛黑色越野與一輛白色轎...
    瑩光灼華閱讀 570評論 7 7