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