阿里IM技術分享(五):閒魚億級IM訊息系統的及時性優化實踐

語言: CN / TW / HK

本文由阿里閒魚技術團隊有攸分享,原題“向訊息延遲說bybye:閒魚訊息及時到達方案”,即時通訊網有修訂和改動。 文中部分連結如無法點選, 請從社群原文中開啟: http://www.52im.net/thread-3706-1-1.html ,或點選下文的“ 閱讀原文 ”!

1、引言

IM訊息作為閒魚使用者重要的交易諮詢工具,核心目標有兩點:

1)第一是保證使用者的訊息不丟失;

2)第二是保證使用者的訊息及時送達接收方。

IM訊息根據訊息的接收方裝置是否線上,分為離線和線上推送。資料顯示目前閒魚每天有超過一半以上的IM訊息是走線上通道的,而線上訊息的到達率、及時性是直接影響使用者體驗的。

本文將根據閒魚IM訊息系統在訊息及時性方面的優化實踐,詳細分析了IM線上通道面臨的各種技術問題,並通過相應的技術手段來優化從而保證使用者訊息的及時到達。

PS: 如果您對IM訊息可靠性還沒有概念,建議先閱讀這篇入門文章《 零基礎IM開發入門(二):什麼是IM系統的實時性? 》。

2、系列文章

本文是系列文章的第5篇,總目錄如下:

1)《 阿里IM技術分享(一):企業級IM王者——釘釘在後端架構上的過人之處

2)《 阿里IM技術分享(二):閒魚IM基於Flutter的移動端跨端改造實踐

3)《 阿里IM技術分享(三):閒魚億級IM訊息系統的架構演進之路

4)《 阿里IM技術分享(四):閒魚億級IM訊息系統的可靠投遞優化實踐

5)《阿里IM技術分享(五):閒魚億級IM訊息系統的及時性優化實踐》( * 本文

3、當前面臨的問題

3.1 端內長連線中斷

在IM場景中,使用者與雲端通訊頻繁,且為了實現使用者的訊息及時到達,往往採用雲端下推訊息的方式觸達使用者,所以使用者線上時裝置與雲端會維持一條TCP長連線通道,可以更輕量級的與服務端進行互動,現代IM即時通訊的下行訊息都是通過長連下發的。

當前閒魚IM訊息系統使用的是ACCS長連線,ACCS是淘寶無線提供的全雙工、低延時、高安全的通道服務。

但由於使用者裝置網路狀態的不確定性,可能會發生各種各樣的網路異常情況導致ACCS長連線通道中斷。而長連線一旦意外中斷,就會導致使用者無法及時收到線上訊息。

針對這個問題,我們需要儘可能及時的感知到長連中斷並嘗試重連。具體的優化思路會在本文後面的內容中分享。

3.2 下推的訊息未達

感知長連中斷並重連只能在大多數時間保證長連線的有效性,但是在長連線無效或不穩定期間下推的訊息客戶端可能根本收不到。

簡單說就是僅僅有重連機制無法保證下行訊息必達,可能有以下場景導致下行訊息失敗:

1) 服務端傳送下行訊息時長連暢通,訊息在傳輸路上通道斷掉,客戶端無法收到;

2) 裝置的線上狀態存在延遲,服務端下行訊息時認為裝置線上,實際上裝置已經離線,無法收到;

3) 客戶端收到了下行訊息,但端上後續處理失敗(比如落庫失敗,訊息沒有成功展示給使用者)。

我們通過資料埋點統計得出,ACCS長連線的下行成功率在97%左右。

ACCS長連線的下行成功率的統計方法如下:

ACCS下行成功率 = 通過ACCS成功下行且客戶端收到的訊息量 / 服務端認為通過ACCS成功下行的訊息量

有心急的同學就要問了,丟了3%的訊息嗎?

並沒有! 這3%的訊息不會丟失,只是不保證及時觸達給使用者。

我們的訊息同步模型是推拉結合模式 ,在使用者拉取訊息時會拉取到裝置當前位點與服務端最新位點的所有訊息,accs下行失敗的訊息會通過主動拉模式獲取到,但客戶端主動拉取訊息的觸發時機有限。

當前客戶端主動拉取訊息的觸發時機主要有以下幾個:

1) 使用者冷啟動app,主動同步訊息;

2) 使用者主動下拉重新整理;

3) app後臺切換前臺;

4) 收到一條推送訊息,客戶端發現新訊息的位點跟本地最新的位點有gap,觸發同步。

可見: 上述主動同步訊息的觸發很大程度上依賴使用者行為或者有沒有收到新訊息,難以保證訊息及時到達。

如果是使用者高頻開啟的IM軟體,這樣也不會有太大的問題。但是閒魚app的活躍度較低,有時候甚至依賴IM訊息拉活,而且一條延遲的訊息觸達可能導致使用者錯過一筆交易,閒魚訊息不允許有這樣的延遲發生。

基於上述分析,我們先描述一個數據指標來反映現狀。

通過上面的描述可知: accs訊息並不全都是推下來的,也可能是主動拉下來的。如果是推,必定可以及時到達;如果是拉,則受限於使用者行為。

拉的這部分訊息,我們定義為accs訊息補償到達,然後計算accs訊息補償到達耗時,訊息範圍限定為服務端accs成功下行但是客戶端通過主動拉取同步到的訊息,以往的版本這個資料在60分鐘左右。

注意: 這個資料並不是訊息觸達到使用者的耗時,因為如果線上轉離線觸達,拉取到訊息的時間取決於使用者行為( 使用者何時打開了app ),但這個資料也能大致反映線上訊息的到達延遲狀況。

ACCS長連線的訊息補償到達耗時的統計方法如下:

ACCS訊息補償到達耗時 = 客戶端通過拉獲取到ACCS訊息的時間 - 服務端ACCS下行時間

接下來本文將從長連線的重連和未達訊息重發兩個方面詳細講述我們是如何優化線上通道穩定性的,從而優化並保證訊息的及時到達。

4、優化手段1:增加長連線重連機制

4.1 長連線為什麼會中斷?

有因必有果,我們先來分析下有哪些原因會導致連線中斷。

對於IM這種場景下來說,通常可能有以下原因:

1) 使用者裝置斷網;

2) 裝置發生了網路切換;

3) 裝置處於弱網環境,網路不穩定;

4) 裝置網路正常,TCP連線由於NAT超時導致連線被運營商中斷。

對於APP來說,如果是使用者操作導致網路狀態變化的情況,會有網路狀態變化事件通知,這種情況可以監聽事件並主動嘗試重連。但現實中的大多數情況都是“意料之外”( 正如上面列舉的這些斷網可能性一樣 )。

那麼既然“意料之外”的斷網無法預知, 技術上可以如何有效的感知到各種異常狀況呢?

PS: 如果要透徹理解斷網、弱網、TCP連結有效性,並不是本文能講的清楚的,可以參照下面的資料深入理解一下,值得好好學習。

關於TCP連結本身的有效性問題,可以讀以下兩篇:

1)《為何基於TCP協議的移動端IM仍然需要心跳保活機制?》

2)《 不為人知的網路程式設計(十二):徹底搞懂TCP協議層的KeepAlive保活機制

關於行動網路的複雜性問題,可以從以下幾篇入門的科普文章學習一下:

1)《IM開發者的零基礎通訊技術入門(十一):為什麼WiFi訊號差?一文即懂!》

2)《IM開發者的零基礎通訊技術入門(十二):上網絡卡頓?網路掉線?一文即懂!》

3)《IM開發者的零基礎通訊技術入門(十三):為什麼手機訊號差?一文即懂!》

4)《IM開發者的零基礎通訊技術入門(十四):高鐵上無線上網有多難?一文即懂!》

關於移動弱網帶來的各種問題、優化方案等,可以通過以下幾篇系統學習一下:

1)《 現代移動端網路短連線的優化手段總結:請求速度、弱網適應、安全保障

2)《 移動端IM開發者必讀(一):通俗易懂,理解行動網路的“弱”和“慢”

3)《 移動端IM開發者必讀(二):史上最全移動弱網路優化方法總結

4)《 百度APP移動端網路深度優化實踐分享(三):移動端弱網優化篇

4.2 心跳檢測機制

像大多數鏈路保活場景一樣,IM這種場景下最有效的檢測手段就是心跳檢測( 如果你對TCP鏈路保活還沒有什麼概念,建議先讀《 為何基於TCP協議的移動端IM仍然需要心跳保活機制?

)。

原理就是: 客戶端通過定時傳送心跳包,服務端收到心跳包後再反饋給客戶端,通過客戶端和服務端這一來一去的配合,就可以實現客戶服和服務端各自都能感知到連線是否中斷。

從及時性效果來看: 心跳間隔越短越好,而頻繁的心跳檢測勢必會帶來使用者流量以及電量的損耗,所以我們的實現目標是如何儘可能少的心跳檢測而又儘量及時地感知到長連中斷的意外情況。

狀態機+訊息心跳佇列:

在心跳協議設計上,要注意心跳包的核心目標是檢測長連通道是否暢通,客戶端主動上行心跳包且能收到服務端回包,就認為長連通道健康。所以心跳的上行訊息以及回包的資料包應儘可能小。一般來說,通過協議頭標識心跳包及響應即可( 這樣就能節省協議包大小 )。

PS: 關於心跳機制的入門文章可以詳讀 一文讀懂即時通訊應用中的網路心跳包機制:作用、原理、實現思路等

》。

4.3 心跳策略

心跳策略是實現我們上述目標的核心機制,本文僅簡單列舉幾種心跳策略。

比如以下這幾種:

1) 短心跳檢測 初始狀態連續 ping 3次 收到 ack 後,可以認為進入穩定狀態;

2) 常規固定時長心跳(根據app狀態不同,頻率可調Mid+,Mid-, Long);

3) 自適應心跳 根據裝置網路狀態變化自動適應的心跳間隔;

4) 冗餘心跳,app後臺切前臺,主動心跳一次。

關於心跳策略的詳細設計甚至可以單獨寫一篇文章,有興趣的同學可以閱讀以下推薦的文章繼續深入研究。

1)《微信團隊原創分享:Android版微信後臺保活實戰分享(網路保活篇)》

2)《移動端IM實踐:實現Android版微信的智慧心跳機制》

3)《移動端IM實踐:WhatsApp、Line、微信的心跳策略分析》

4)《 融雲技術分享:融雲安卓端IM產品的網路鏈路保活技術實踐

5)《 Web端即時通訊實踐乾貨:如何讓你的WebSocket斷網重連更快速?

6)《 正確理解IM長連線的心跳及重連機制,並動手實現 (有完整IM原始碼)》

7)《一種Android端IM智慧心跳演算法的設計與實現探討(含樣例程式碼)》

8)《手把手教你用Netty實現網路通訊程式的心跳機制、斷線重連機制》

5、優化手段2:訊息ACK應答與重發機制

5.1 概述

為了解決上面的問題,我們同時也引入了訊息ACK應答與重發機制。

整體思路是: 客戶端在收到ACCS訊息並處理成功後,給服務端回一個ACK應答包,服務端下發ACCS訊息時將訊息加入重試佇列,收到ACK應答包後更新訊息到達狀態,並終止重試。

整體設計流程圖如下:

該方案的難點即重試處理器的實現設計,接下來我們將重點講述這部分的詳細設計。

5.3 延遲重試設計

如上圖所示:

1) 每通過accs下發一條訊息,先插入到Timeline中,初始狀態為未達,然後生產一條延遲N秒的延遲訊息;

2) 每次消費到延遲訊息後,讀取tablestore中該訊息的到達狀態,如到達則終止延遲,否則繼續;

3) 每次重試先判斷裝置是否線上,如果裝置不線上,轉發離線通道並終止重試,如果裝置線上,則重推未到達的訊息,並再次延遲N秒消費;

4) 每條訊息的重試生命週期中用的同一條延遲訊息,最多重試消費M次,超過次數不再重試並打日誌埋點(後續可以監控這種情況並基於這個資料進行優化)。

附錄:參考資料

[1]  為何基於TCP協議的移動端IM仍然需要心跳保活機制?

[2] 

徹底搞懂TCP協議層的KeepAlive保活機制

[3]  現代IM系統中聊天訊息的同步和儲存方案探討

[4]  現代移動端網路短連線的優化手段總結:請求速度、弱網適應、安全保障

[5]  史上最全移動弱網路優化方法總結

[6]  上網絡卡頓?網路掉線?一文即懂!

[7]  為什麼手機訊號差?一文即懂!

[8]  移動端IM實踐:實現Android版微信的智慧心跳機制

[9] 

融雲技術分享:融雲安卓端IM產品的網路鏈路保活技術實踐

[10]  Web端即時通訊實踐乾貨:如何讓你的WebSocket斷網重連更快速?

補充: 文中部分連結如無法點選, 請從(52im.net)社群原文中開啟: http://www.52im.net/thread-3726-1-1.html ,或點選下文的“ 閱讀原文 ”!

「其他文章」