本文來自轉轉技術李帥的原創分享,已進行修訂和排版優化。
1、引言
在當今互聯網時代,高效的用户服務是提升用户體驗的關鍵。轉轉自研的客服IM聊天系統作為用户與客服溝通的橋樑,承擔着傳遞信息、解決問題的關鍵角色。然而,消息數據的流轉並非一帆風順,本文將深入探討IM系統在消息傳遞過程中遇到的問題和挑戰,以及相應的技術解決方案。如圖是IM系統中一條消息的流轉鏈路:
相較於普通web系統,IM系統的消息數據流轉鏈路更長、更復雜。
從客户端到服務端,再從服務端到另一個客户端,任何一個環節的故障都可能導致消息延遲、丟失、亂序或重複,從而影響用户體驗。網絡波動和客户端設備性能的不穩定性是影響IM系統性能的主要因素,這些因素可能導致消息的實時性、可靠性和完整性受到威脅。
技術交流:
- 移動端IM開發入門文章:《新手入門一篇就夠:從零開發移動端IM》
- 開源IM框架源碼:https://github.com/JackJiang2011/MobileIMSDK(備用地址點此)
(本文已同步發佈於:http://www.52im.net/thread-4874-1-1.html)
2、關於作者
李帥:轉轉履約中台研發工程師,主要負責客服工單、IM等系統研發。
3、如何保證聊天消息的實時性
首當其衝的就是消息延遲問題:
當一條消息發出後,我們的系統需要確保這條消息最快被接收人感知並獲取到,並且保證資源消耗較少。
這裏關鍵的點是:
1)最快觸達;
2)耗費資源少。
方案1:長短輪詢在PC
web早期,大部分應用都是採用“一問一答”的請求響應模式來獲取數據,IM系統採用客户端輪詢的方式,定期、高頻輪詢獲取服務端的新消息。這種方式開發成本較低、容易實現,但是高頻輪詢很多請求是無用請求,客户端浪費流量和電量,服務端資源壓力很大。後來基於短輪詢進化出長輪詢模式,相較於前者,後者在請求時獲取到新數據時不會立即返回,而是在服務端保持連接等待一段時間,如果等待期間有新消息就立即返回響應,長輪詢僅僅解決了客户端的無用消耗,但是服務端資源高負載情況依然未能解決。方案2:WebSocket隨着HTML5的出現,全雙工的WebSocket成為解決這一問題的關鍵。基於WebSocket實現的IM服務,客户端和服務端只需要完成一次握手,就可以創建持久的長連接,並進行隨時的雙向數據傳輸。
經過比較轉轉客服IM系統採用了WebSocket協議,具體使用方式見《轉轉客服IM系統的WebSocket集羣架構設計和部署方案》。
當服務端接收到新消息時,可以通過建立的WebSocket連接,直接進行推送,保證了消息到達的實時性。
關於傳統Web和現代Web的實時通信技術,可詳讀以下幾篇:
Web端即時通訊技術盤點:短輪詢、Comet、Websocket、SSE
詳解Web端通信方式的演進:從Ajax、JSONP 到 SSE、Websocket
網頁端IM通信技術快速入門:短輪詢、長輪詢、SSE、WebSocket
4、什麼是聊天消息的可靠性
如圖是一條消息發送的核心步驟,整個過程中可以分為兩個部分,消息由客户端發送到服務端(第1、2步),服務端將消息推送至另一個客户端(第3步),發送過程中任何一步出現問題都會導致消息發送失敗。
PS:限於篇幅原因,本文不想深入探討,有興趣可詳讀《零基礎IM開發入門(三):什麼是IM系統的可靠性?》。
5、如何保證聊天消息觸必達?
我們參考使用了TCP/IP協議中的ACK機制來實現防丟邏輯(《TCP/IP詳解 - 第17章·TCP:傳輸控制協議》)。ACK機制是TCP/IP協議三次握手重要的一環(請詳讀《腦殘式網絡編程入門(一):跟着動畫來學TCP三次握手和四次揮手》),用於確認對方發送信息無誤。
ACK響應機制如下:
1)發送者發送消息時會攜帶一個消息標識符(此處使用發送方id和消息發送時間戳)、並在本地維護一個“等待ACK消息列表”;
2)接收者收到消息後對消息進行存儲得到消息id;
3)隨後再將該標識回傳給發送方(ACK消息);
4)發送方收到ACK消息後將消息從“等待ACK消息列表”刪除;
5)當發送方沒有在約定時間內收到ACK消息時,就需要執行失敗消息處理邏輯:自動重發、客户端標記發送失敗等。
服務端實現與客户端稍有不同,服務端需要要維護全量用户的消息,使用定時任務檢查等待ACK消息列表效率比較低,此處通過mq的延遲消息來實現:當消息發出時同時發送一個延遲mq,延遲消息被消費時對應的消息仍在等待ACK列表中,則表示消息未能在規定時間內被確認,需要進行重試發送。
如圖為完整的ACK實現機制:
另外客户端也會在頁面刷新、WebSocket重連時觸發http接口重新拉取當前會話的所有消息進行渲染,保證消息不丟失。
PS:相關資料可進一步閲讀:
- IM消息送達保證機制實現(一):保證在線實時消息的可靠投遞阿里
- IM技術分享(四):閒魚億級IM消息系統的可靠投遞優化實踐融雲
- 技術分享:全面揭秘億級IM消息的可靠投遞機制
- 一套億級用户的IM架構技術乾貨(下篇):可靠性、有序性、弱網優化等
6、如何對聊天消息去重?
消息重推解決了消息丟失的問題,但是因為ACK消息本身就可能會丟失從而導致消息重複,因此我們需要保證推送消息和重推消息有相同且唯一的消息id,接收方可以根據該消息id進行數據去重。
具體是:
1)發送方:客户端使用發送人id和消息發送時間戳作為唯一的ACK標識,傳遞給服務端;
2)服務端:使用雪花算法接收到的消息生成消息id,將ACK標識與消息id建立映射關係;
服務端再將消息id推送至發送方和接收方。
一個完整的消息發送流程如圖所示:
7、心跳保活機制
IM系統中接收和發送消息都是使用長連接實現,但是如果連接斷開,那發送和接收數據就會出現問題。在客服業務中,人工客服席位有限,系統需要可靠的機制保證人工客服資源有效利用。為此我們在應用層設計心跳消息,使用該機制更新用户當前狀態、保證會話有序退出。心跳機制設計如下。
客户端:設置定時器,用户建立連接後,定時發送(30s)心跳消息給服務端。
服務端:
- 1)接收心跳消息,更新心跳時間;
- 2)設置定時任務,定時掃描在線用户上次心跳時間,執行以下邏輯;
- 3)上次心跳時間超出30s,將其標記為離線狀態,關閉連接,等待用户重連;
- 4)上次心跳時間超出2分鐘則認為用户已經徹底離開,執行會話關閉邏輯釋放人工客服資源。
應用層心跳消息僅用於保活和狀態更新,因此數據結構設計十分精簡,不攜帶額外信息。
- 關於心跳保活這方面的資料,可以進一步閲讀:
- 為何基於TCP協議的移動端IM仍然需要心跳保活機制?
- 一文讀懂即時通訊應用中的網絡心跳包機制:作用、原理、實現思路等
- 微信團隊原創分享:Android版微信後台保活實戰分享(網絡保活篇)
- 融雲技術分享:融雲安卓端IM產品的網絡鏈路保活技術實踐
- 跟着源碼學IM(五):正確理解IM長連接、心跳及重連機制,並動手實現阿里
- IM技術分享(五):閒魚億級IM消息系統的及時性優化實踐
- 萬字長文:手把手教你實現一套高效的IM長連接自適應心跳保活機制
8、消息協議的設計
在IM系統中消息格式的設計也十分重要,良好的數據格式可以準確傳遞消息內容並具有極高的可讀性。
我們根據消息類型和發送流向將消息數據格式大致分為如下幾部分:
1)消息類型:用於描述消息的用途、流向,如心跳消息、用户/客服消息、系統消息等;
2)客服id:接收或者發送消息的客服標識;
3)用户id:接收或者發送消息的用户標識;
4)消息內容:實際的消息,與消息類型相關;
5)消息格式:用於描述用户/客服消息格式,如文本、圖片、視頻、訂單卡片、優惠券等;
6)消息文本:消息的展示內容。
PS:IM協議設計相關資料可進一步閲讀:簡述移動端IM開發的那些坑:
- 架構設計、通信協議和客户端
- 理論聯繫實際:一套典型的IM通信協議設計
- 詳解APP與後台通信數據格式的演進:從文本協議到二進制協議
- IM通訊協議專題學習(一):Protobuf從入門到精通,一篇就夠!
9、本文小結
轉轉客服IM系統通過引入WebSocket協議、ACK機制、消息重推和數據去重等策略,有效應保障了消息傳遞過程中的實時性、可靠性和完整性。這些技術的應用,不僅提升了用户與客服之間的溝通效率,也為轉轉平台提供了更加穩定、高效的服務支持。在未來的發展中,我們將繼續優化和完善,以應對不斷變化的需求和用户期望,為用户提供更加優質的服務體驗。
10、參考資料
[0] TCP/IP詳解 - 第17章·TCP:傳輸控制協議》
[1] Web端即時通訊技術盤點:短輪詢、Comet、Websocket、SSE
[2] 詳解Web端通信方式的演進:從Ajax、JSONP 到 SSE、Websocket
[3] 網頁端IM通信技術快速入門:短輪詢、長輪詢、SSE、WebSocket
[4] Web端即時通訊實踐乾貨:如何讓你的WebSocket斷網重連更快速?
[5] 簡述移動端IM開發的那些坑:架構設計、通信協議和客户端
[6] IM消息送達保證機制實現(一):保證在線實時消息的可靠投遞
[7] 阿里IM技術分享(四):閒魚億級IM消息系統的可靠投遞優化實踐
[8] 阿里IM技術分享(五):閒魚億級IM消息系統的及時性優化實踐
[9] 融雲技術分享:全面揭秘億級IM消息的可靠投遞機制
[10] 一套億級用户的IM架構技術乾貨(下篇):可靠性、有序性、弱網優化等
[11] 理解IM消息“可靠性”和“一致性”問題,以及解決方案探討
[12] 微信團隊分享:來看看微信十年前的IM消息收發架構,你做到了嗎
[13] 阿里IM技術分享(七):閒魚IM的在線、離線聊天數據同步機制優化實踐
[14] IM羣聊消息如此複雜,如何保證不丟不重?
[15] 開發IM是自己設計協議用字節流好還是字符流好?
[16] 零基礎IM開發入門(一):什麼是IM系統?
[17] 零基礎IM開發入門(二):什麼是IM系統的實時性?
[18] 零基礎IM開發入門(三):什麼是IM系統的可靠性?
[19] 零基礎IM開發入門(四):什麼是IM系統的消息時序一致性?
[20] 腦殘式網絡編程入門(一):跟着動畫來學TCP三次握手和四次揮手
(本文已同步發佈於:http://www.52im.net/thread-4874-1-1.html)