轉轉客服IM系統:高效溝通背后的技術挑戰和解決方案
前言
在當今互聯網時代,高效的用戶服務是提升用戶體驗的關鍵。轉轉自研的客服IM系統作為用戶與客服溝通的橋梁,承擔著傳遞信息、解決問題的關鍵角色。然而,消息數據的流轉并非一帆風順,本文將深入探討IM系統在消息傳遞過程中遇到的問題和挑戰,以及相應的技術解決方案。
如圖是IM系統中一條消息的流轉鏈路:
IM消息流轉
相較于普通web系統,IM系統的消息數據流轉鏈路更長、更復雜。從客戶端到服務端,再從服務端到另一個客戶端,任何一個環節的故障都可能導致消息延遲、丟失、亂序或重復,從而影響用戶體驗。
網絡波動和客戶端設備性能的不穩定性是影響IM系統性能的主要因素,這些因素可能導致消息的實時性、可靠性和完整性受到威脅。
實時性
首當其沖的就是消息延遲問題:當一條消息發出后,我們的系統需要確保這條消息最快被接收人感知并獲取到,并且保證資源消耗較少。這里關鍵的幾個點是:最快觸達,且耗費資源少。
方案1:長短輪詢
在PC web早期,大部分應用都是采用“一問一答”的請求響應模式來獲取數據,IM系統采用客戶端輪詢的方式,定期、高頻輪詢獲取服務端的新消息。這種方式開發成本較低、容易實現,但是高頻輪詢很多請求是無用請求,客戶端浪費流量和電量,服務端資源壓力很大。
后來基于短輪詢進化出長輪詢模式,相較于前者,后者在請求時獲取到新數據時不會立即返回,而是在服務端保持連接等待一段時間,如果等待期間有新消息就立即返回響應,長輪詢僅僅解決了客戶端的無用消耗,但是服務端資源高負載情況依然未能解決。
方案2:WebSocket
隨著HTML5的出現,全雙工的WebSocket成為解決這一問題的關鍵。基于WebSocket實現的IM服務,客戶端和服務端只需要完成一次握手,就可以創建持久的長連接,并進行隨時的雙向數據傳輸。
長短輪詢 | WebSocket | |
瀏覽器支持情況 | ?所有瀏覽器都支持 | 大部分主流瀏覽器支持 |
服務器負載 | 高負載 | ?取決于實際消息量 |
客戶端延遲 | 非實時;取決于輪詢頻率 | ?實時 |
客戶端資源消耗 | 大 | ?低 |
實現復雜度 | ?低 | 高 |
經過比較轉轉客服IM系統采用了WebSocket協議,具體使用方式見WebSocket使用。當服務端接收到新消息時,可以通過建立的WebSocket連接,直接進行推送,保證了消息到達的實時性。
可靠性
如圖是一條消息發送的核心步驟,整個過程中可以分為兩個部分,消息由客戶端發送到服務端(第1、2步),服務端將消息推送至另一個客戶端(第3步),發送過程中任何一步出現問題都會導致消息發送失敗。
消息發送示例
如何保證消息觸達?
我們參考使用了TCP/IP協議中的ACK機制來實現防丟邏輯。ACK機制是TCP/IP協議三次握手重要的一環,用于確認對方發送信息無誤。ACK響應機制如下:
- 發送者發送消息時會攜帶一個消息標識符(此處使用發送方id和消息發送時間戳)、并在本地維護一個“等待ACK消息列表”
- 接收者收到消息后對消息進行存儲得到消息id
- 隨后再將該標識回傳給發送方(ACK消息)
- 發送方收到ACK消息后將消息從“等待ACK消息列表”刪除
- 當發送方沒有在約定時間內收到ACK消息時,就需要執行失敗消息處理邏輯:自動重發、客戶端標記發送失敗等
服務端實現與客戶端稍有不同,服務端需要要維護全量用戶的消息,使用定時任務檢查等待ACK消息列表效率比較低,此處通過mq的延遲消息來實現:
當消息發出時同時發送一個延遲mq,延遲消息被消費時對應的消息仍在等待ACK列表中,則表示消息未能在規定時間內被確認,需要進行重試發送。
如圖為完整的ACK實現機制:
ACK機制
另外客戶端也會在頁面刷新、WebSocket重連時觸發http接口重新拉取當前會話的所有消息進行渲染,保證消息不丟失。
數據重復
消息重推解決了消息丟失的問題,但是因為ACK消息本身就可能會丟失從而導致消息重復,因此我們需要保證推送消息和重推消息有相同且唯一的消息id,接收方可以根據該消息id進行數據去重。
發送方:客戶端使用發送人id和消息發送時間戳作為唯一的ACK標識,傳遞給服務端。
服務端:使用雪花算法接收到的消息生成消息id,將ACK標識與消息id建立映射關系;服務端再將消息id推送至發送方和接收方。
一個完整的消息發送流程如圖所示:
IM消息流轉
心跳機制
IM系統中接收和發送消息都是使用長連接實現,但是如果連接斷開,那發送和接收數據就會出現問題。在客服業務中,人工客服席位有限,系統需要可靠的機制保證人工客服資源有效利用。
為此我們在應用層設計心跳消息,使用該機制更新用戶當前狀態、保證會話有序退出。
心跳機制設計如下:
客戶端
- 設置定時器,用戶建立連接后,定時發送(30s)心跳消息給服務端
服務端
- 接收心跳消息,更新心跳時間
- 設置定時任務,定時掃描在線用戶上次心跳時間,執行以下邏輯
- 上次心跳時間超出30s,將其標記為離線狀態,關閉連接,等待用戶重連
- 上次心跳時間超出2分鐘則認為用戶已經徹底離開,執行會話關閉邏輯釋放人工客服資源
應用層心跳消息僅用于保活和狀態更新,因此數據結構設計十分精簡,不攜帶額外信息。
消息協議
在IM系統中消息格式的設計也十分重要,良好的數據格式可以準確傳遞消息內容并具有極高的可讀性。我們根據消息類型和發送流向將消息數據格式大致分為如下幾部分:
- 消息類型:用于描述消息的用途、流向,如心跳消息、用戶/客服消息、系統消息等
- 客服id:接收或者發送消息的客服標識
- 用戶id:接收或者發送消息的用戶標識
- 消息內容:實際的消息,與消息類型相關
- 消息格式:用于描述用戶/客服消息格式,如文本、圖片、視頻、訂單卡片、優惠券等
- 消息文本:消息的展示內容
不同類型的消息
結語
轉轉客服IM系統通過引入WebSocket協議、ACK機制、消息重推和數據去重等策略,有效應保障了消息傳遞過程中的實時性、可靠性和完整性。這些技術的應用,不僅提升了用戶與客服之間的溝通效率,也為轉轉平臺提供了更加穩定、高效的服務支持。
在未來的發展中,我們將繼續優化和完善,以應對不斷變化的需求和用戶期望,為用戶提供更加優質的服務體驗。