玩轉直播系列之訊息模組演進(3)

vivo網際網路技術發表於2021-05-24

一、背景

即時訊息(IM)系統是直播系統重要的組成部分,一個穩定的,有容錯的,靈活的,支援高併發的訊息模組是影響直播系統使用者體驗的重要因素。IM長連線服務在直播系統有發揮著舉足輕重的作用。

本篇文章針對秀場直播,簡單地描述一下訊息模型,說明一下我們訊息模型的架構,並結合我們一年以來,通過處理不同的業務線上問題,來進行演進式的訊息模型架構的升級與調整,將此整理成文,並分享給大家。

在目前大部分主流的直播業務中,推拉流是實現直播業務最基本的技術點,訊息技術則是實現觀看直播的所有使用者和主播實現互動的關鍵技術點,通過直播IM系統模組,我們可以完成公屏互動,彩色彈幕,全網送禮廣播,私信,PK等核心秀場直播的功能開發。"IM訊息"作為使用者和使用者,使用者和主播之間"溝通"的資訊橋樑,如何保證"資訊橋樑"的在高併發場景下保持穩定可靠,是直播系統演進過程中一個重要的話題。

二、直播訊息業務

在直播業務中,有幾個核心的關於訊息模型的概念,我們先簡單地介紹一下,方便大家對直播相關的訊息模型有一個整體上的理解。

2.1 主播與使用者

主播和觀眾,對於IM系統來說,都是一個普通使用者,都會有一個唯一使用者標識,也是IM分發到點對點訊息的重要標識。

2.2 房間號

一個主播對應一個房間號(RoomId),主播在開播之前,進行身份資訊驗證之後,就會繫結唯一的房間號,房間號是IM系統進行直播間訊息分發的重要標識。

2.3 訊息型別劃分

按照直播業務特性,IM訊息劃分的方式有很多方式,例如按照接收方維度進行劃分,按照直播間訊息業務型別進行劃分,按照訊息的優先順序,儲存方式都可以進行不同的劃分等等方式。

通常,我們按照接收方維度進行劃分有如下幾個型別的訊息:

  • 點對點訊息(單播訊息)

  • 直播間訊息(群播訊息)

  • 廣播訊息

按照具體的業務場景有如下幾個型別的訊息:

  • 禮物訊息

  • 公屏訊息

  • PK訊息

  • 業務通知類訊息

訊息能夠實時準確地分發到對應的群體或者單個使用者終端都是非常必要的。當然好一點的IM訊息模型也能夠賦能業務一些新的能力,例如如下的能力:

  • 統計每個直播間的實時線上人數

  • 捕獲使用者進出直播間的事件

  • 統計每個使用者實時進入直播間的時間

2.4 訊息優先順序

直播的訊息是有優先順序的,這一點是很重要的,與微信,QQ等聊天IM產品不一樣的地方是直播間訊息是分優先順序的。

微信等聊天訊息產品,不管是私聊還是群聊,每個人傳送訊息的優先順序基本上是一樣的,不存在誰的訊息優先順序高,誰的訊息優先順序低,都需要將訊息準確實時地分發到各個業務終端,但是直播因為業務場景的不同,訊息分發的優先順序也是不一樣的。

舉例來說,如果一個直播間每秒只能渲染15~20個訊息,如果一個熱點直播間一秒鐘產生的訊息量大於20條或者更多,如果不做訊息優先順序的控制,直接實時分發訊息,那麼導致的結果就是直播間公屏客戶端渲染卡頓,禮物彈框渲染過快,使用者觀看體驗大幅下降,所以我們要針對不同業務型別的訊息,給出不同的訊息優先順序。

舉例來說,禮物訊息大於公屏訊息,同等業務型別的訊息,大額禮物的訊息優先順序又大於小額禮物的訊息,高等級使用者的公屏訊息優先順序高於低等級使用者或者匿名使用者的公屏訊息,在做業務訊息分發的時候,需要根據實際的訊息優先順序,選擇性地進行訊息準確地分發。

三、訊息技術點

3.1 訊息架構模型

3.2 短輪詢 VS 長連結

3.2.1 短輪詢

3.2.1.1 短輪詢的業務模型

首先我們先簡單地描述一下短輪詢時間的過程和基本設計思想:

  • 客戶端每隔2s輪詢伺服器介面,引數是roomId和timestamp,timestamp第一次傳遞0或者null。

  • 伺服器根據roomId和timestamp查詢該房間在timestamp時間戳後產生的訊息事件,返回限定條數的訊息例如(例如返回10~15條,當然在這個timestamp之後產生的訊息數遠遠大於15條,不過因為客戶端渲染能力有限和過多的訊息展示,會影響使用者體驗,所以限制返回的條數),並且同時返回這些訊息中最後一條訊息產生的時間戳timestamp,作為客戶端下次請求伺服器的基準請求時間戳。

  • 以此反覆,這樣就可以每隔2s按照各個終端要求,更新每個直播間的最新訊息了

整體的主體思想如上圖所示,不過具體的時間可以再做精細化處理,後續再做具體的說明和細節說明。

3.2.1.2 短輪詢的儲存模型

短輪詢的訊息儲存與正常的長連線的訊息儲存有一定的區別,不存在訊息擴散的問題,我們需要做的訊息儲存需要達到如下的業務目標:

  • 訊息插入時間複雜度要相對比較低;

  • 訊息查詢的複雜度要相對比較低;

  • 訊息的儲存的結構體要相對比較小,不能佔用太大的記憶體空間或者磁碟空間;

  • 歷史訊息能夠按照業務需要做磁碟持久化儲存;

結合上述4點的技術要求,畢竟經過小組成員的討論,我們決定使用Redis的SortedSet資料結構進行儲存,具體實現思路:按照直播間產品業務型別,將業務訊息劃分為如下四大型別:禮物,公屏,PK,通知。

一個直播間的訊息使用四個Redis的SortedSet資料結構進行儲存,SortedSet的key分別是"live::roomId::gift","live::roomId::chat","live::roomId::notify","live::roomId::pk",score分別是訊息真實產生的時間戳,value就是序列化好的json字串,如下圖所示:

客戶端輪詢的時候,服務端查詢的邏輯如下所示:

很多同學會疑問,為什麼不適用Redis的list的資料結構呢?如下圖會進行詳細的說明:

最後我們再對比一下Redis的SortedSet和Redis的List這兩個資料結構在直播訊息儲存的時候,時間複雜度的相關分析。

以上,就是我們使用Redis的SortedSet資料結構進行訊息儲存的一些簡單的設計思考,後續我們也會提到端輪詢的編碼時候,需要的注意點。

3.2.1.3 短輪詢的時間控制

短輪詢的時間控制及其重要,我們需要在直播觀眾觀看體驗QoE和伺服器壓力之間找到一個很好的平衡點。

輪詢的間隔時間太長,使用者體驗就會下降很多,直播觀看體驗就會變差,會有"一頓一頓"的感覺。短輪詢的頻率過高,會導致伺服器的壓力過大,也會出現很多次"空輪詢",所謂的"空輪詢"就是無效輪詢,也就是在上一秒有效輪詢返回有效訊息之後,間隔期直播間沒有產生新的訊息,就會出現無效的輪詢。

vivo直播目前每日的輪詢次數是10+億次,晚觀看直播高峰期的時候,伺服器和Redis的CPU負載都會上升,dubbo的服務提供方的執行緒池一直處於高水位線上,這塊需要根據機器的和Redis的實時負載的壓力,做伺服器的水平擴容和Redis Cluster的節點擴容,甚至讓一些超高熱度值的直播間負載到指定的Redis Cluster叢集上,做到物理隔離,享受到"VIP"服務,確保各個直播間的訊息相互不影響。

直播人數不一樣的直播間,輪詢的時間也是可以配置的,例如人數比較少的直播,百人以下的直播間,可以設定比較高頻的輪詢頻率,例如1.5s左右,超過300人以上的,1000人以下可以2s左右,萬人直播間可以設定2.5s左右,這些配置應該都可以通過配置中心實時下發,客戶端能夠實時更新輪詢的時間,調整的頻率可以根據實際直播間使用者體驗的效果,並且結合伺服器的負載,找到一個輪詢間隔的相對最佳值。
image

3.2.1.4 短輪詢的注意點

1)服務端需要校驗客戶端傳遞過來的時間戳:這一點非常重要,試想一下,如果觀眾在觀看直播的時候,將直播退出後臺,客戶端輪詢程式暫停,當使用者恢復直播觀看畫面程式的時候,客戶端傳遞過來的時間就會是非常老舊甚至過期的時間,這個時間會導致伺服器查詢Redis時出現慢查,如果出現大量的伺服器慢查的話,會導致伺服器連線Redis的連線無法快速釋放,也會拖慢整個伺服器的效能,會出現一瞬間大量的輪詢介面超時,服務質量和QoE會下降很多。

2)客戶端需要校驗重複訊息:在極端情況下,客戶端有可能收到重複的訊息,產生的原因可能如下,在某一個時刻客戶端發出roomId=888888&timestamp=t1的請求,因為網路不穩定或者伺服器GC的原因,導致該請求處理比較慢,耗時超過2s,但是因為輪詢時間到了,客戶端又發出了roomId=888888&timestamp=t1的請求,伺服器返回相同的資料,就會出現客戶端重複渲染相同的訊息進行展示,這樣也會影響使用者體驗,所以每一個客戶端有必要對重複訊息進行校驗。

3)海量資料無法實時返回渲染的問題:設想一下,如果一個熱度極大的直播間,每秒鐘產生的訊息量是數千或者上萬的時候,按照上面的儲存和查詢思路是有漏洞的,因為我們每次因為各個因素的限制,每次只返回10~20條訊息,那麼我們需要很長的時間才能把這熱度很多的一秒鐘的資料全部返回,這樣就會造成最新的訊息無法快速優先返回,所以輪詢返回的訊息也可以按照訊息優先順序進行選擇性丟棄。

客戶端輪詢服務伺服器查詢直播間的訊息的好處是顯而易見的,訊息的分發是非常實時和準確的,很難出現因為網路顫抖導致訊息無法到達的場景,不過壞處也是非常明顯的,伺服器在業務高峰期的負載壓力很大,如果直播間的所有訊息都是通過輪詢分發的,長期以往,伺服器是很難通過水平擴容的方式來達到線性增長的。

3.2.2 長連線

3.2.2.1 長連線的架構模型

從流程上來說,如上圖所示,整體直播長連線的流程:

手機客戶端首先通過http請求長連線伺服器,獲取TCP長連線的IP地址,長連線伺服器根據路由和負載策略,返回最優的可連線的IP列表。

手機客戶端根據長連線伺服器返回的IP列表,進行長連線的客戶端的連線請求接入,長連線伺服器收到連線請求,進而建立連線。

手機客戶端傳送鑑權資訊,進行通訊資訊的鑑權和身份資訊確認,最後長連線建立完成,長連伺服器需要對連線進行管理,心跳監測,斷線重連等操作。

長連線伺服器叢集的基本架構圖如下所示,按照地域進行業務劃分,不同地域的終端機器按需接入;

3.2.2.2 長連線建立和管理

為了使訊息即時、高效、安全地觸達使用者,直播客戶端和IM系統建立了一條加密的全雙工資料通路,收發訊息均使用該通道,當大量使用者線上的時候,維護這些連線、保持會話,需要用到大量記憶體和CPU資源。

IM接入層儘量保持功能簡潔,業務邏輯下沉到後面邏輯服務中進行處理,為了防止釋出的時候,重啟程式會導致大量的外網裝置重新建立連線,影響使用者體驗。接入層提供熱更新的釋出方案:連線維護,賬號管理等不經常改動的基礎邏輯放入主程式中,業務邏輯採用so外掛的方式嵌入到程式的,修改業務邏輯時只需要重新載入一次外掛即可,可以保證與裝置的長連線不受影響。

3.2.2.3長連線保活

長連線建立後,如果中間網路斷開,服務端和客戶端都無法感知,造成假線上的情況。因此維護好這個“長連線”一個關鍵的問題在於能夠讓這個“長連線”能夠在中間鏈路出現問題時,讓連線的兩端能夠快速得到通知,然後通過重連來建立新的可用連線,從而讓我們這個長連線一直保持高可用狀態。IM在服務端開啟了keeplive保活探測機制和在客戶端啟用了智慧心跳。

  • 利用keeplive保活探測功能,可以探知客戶端崩潰、中間網路端開和中間裝置因超時刪除連線相關的連線表等意外情況,從而保證在意外發生時,服務端可以釋放半開啟的TCP連線。

  • 客戶端啟動智慧心跳不僅能在消耗極少的電和網路流量條件下,通知伺服器客戶端存活狀態、定時的重新整理NAT內外網IP對映表,還能在網路變更時自動重連長連線。

3.2.3 直播間IM訊息分發

IM長連線分發訊息的整體流程圖

在整合客戶端,IM長連線伺服器模組和直播業務伺服器模組這三個模組的時候,整體訊息的分發邏輯遵循如下的基本原則:

  • 單播,群播,廣播訊息所有的訊息都是由直播業務伺服器呼叫IM長連線伺服器的介面,將需要分發的訊息分發到各個業務直播間。

  • 業務伺服器對直播間產生的事件進行對應的業務型別做響應的處理,例如送禮扣減虛擬貨幣,傳送公屏進行文字健康校驗等。

  • 客戶端接受直播業務伺服器的信令控制,訊息是通過長連線通道分發還是http短輪詢分發,都是由直播業務伺服器控制,客戶端遮蔽底層訊息獲取的方式細節,客戶端上層接受統一的訊息資料格式,進行對應的業務型別訊息處理渲染。

3.2.3.1直播間成員管理和訊息分發

直播間成員是直播間最重要的基礎後設資料,單個直播間的使用者量實際上是無上限的,且呈現大直播若干個(大於30W同時線上)、中直播間幾百個、小直播幾萬個這樣分佈,如何管理直播間成員是一個直播間系統架構中核心功能之一,常見的方式有如下兩種:

1.為直播間分配固定分片,使用者與具體的分片存在對映關係,每個分片中儲存使用者相對隨機。

採用固定分片的方式演算法實現簡單,但是對於使用者少的直播間有可能分片承載的使用者數量少,對於使用者大的直播間有可能分片承載使用者量又比較大,固定分片存在天然伸縮性差的特點。

2.動態分片,規定分片使用者數,當使用者數超過閾值時,增加一個新的分片,分片數量可以隨著使用者數增加而變化。

動態分片可以根據直播間人數自動生成分片,滿了就開闢新片,儘量使每個分片的使用者數達到閾值,但已有分片的使用者數量隨著使用者進出直播間變化,維護複雜度比較高。

3.2.3.2 直播間訊息分發

直播間中有進出場訊息、文字訊息、禮物訊息和公屏訊息等多種多樣訊息,訊息的重要程度不一樣,可為每個訊息設定相應的優先順序。

不同優先順序的訊息放在不同的訊息佇列中,高優先順序的訊息優先傳送給客戶端,訊息堆積超過限制時,丟棄最早、低優先順序的訊息。另外,直播間訊息屬於實時性訊息,使用者獲取歷史訊息、離線訊息的意義不大,訊息採用讀擴散的方式儲存組織。直播間訊息傳送時,根據直播間成員分片通知對應的訊息傳送服務,再把訊息分別下發給分片中對應的每一個使用者,為了實時、高效地把直播間訊息下發給使用者,當使用者有多條未接收訊息時,下發服務採用批量下發的方式將多條訊息傳送給使用者。

3.2.3.3 長連線的訊息壓縮

在使用TCP長連線分發直播間訊息的時候,也需要注意訊息體的大小,如果某一個時刻,分發訊息的數量比較大,或者同一個訊息在做群播場景的時候,群播的使用者比較多,IM連線層的機房的出口頻寬就會成為訊息分發的瓶頸。所以如何有效的控制每一個訊息的大小,壓縮每一個訊息的大小,是我們也需要思考的問題,我們目前通過兩種方式來來做相關訊息體結構的優化:

使用protobuf協議資料交換格式

相同型別的訊息進行合併傳送

經過我們線上測試,使用protobuf資料交換格式,平均每一個訊息節省43%的位元組大小,可以大大幫助我們節省機房出口頻寬。

3.2.3.4 塊訊息

所謂塊訊息,也是我們借鑑其他直播平臺的技術方案,也就是多個訊息進行合併傳送,直播業務伺服器不是產生一個訊息就立馬呼叫IM長連線伺服器叢集直接進行訊息的分發。主要思想,就是以直播間為維度,每隔1s或者2s,以勻速的時間間隔將在這個時間段業務系統產生的訊息進行分發。

每秒分發1020個訊息,如果每秒中,業務伺服器積累到的訊息大於1020個,那就按照訊息的優先順序進行丟棄,如果這10~20個訊息的優先順序都比較高,例如都是禮物型別的訊息,則將訊息放在後一個訊息塊進行傳送,這樣做的好處有如下三個;

合併訊息,可以減少傳輸多餘的訊息頭,多個訊息一起傳送,在自定義的TCP傳輸協議中,可以共用訊息頭,進一步減少訊息位元組數大小;

防止出現訊息風暴,直播業務伺服器可以很方便的控制訊息分發的速度,不會無限制的分發訊息到直播客戶端,客戶端無法處理如此多的訊息;

友好的使用者體驗,直播間的訊息因為流速正常,渲染的節奏比較均勻,會帶來很好的使用者直播體驗,整個直播效果會很流暢

3.3 訊息丟棄

不管是http短輪詢還是長連線,在高熱度值直播間出現的時候,都會存在訊息丟棄的情況,例如在遊戲直播中,有出現比較精彩瞬間的時候,評論公屏數會瞬間增加,同時送低價值的禮物的訊息也會瞬間增加很多,用來表示對自己選手精彩操作的支援,那麼伺服器通過IM長連線或者http短輪詢每秒分發的訊息數就會數千或者上萬,一瞬間的訊息突增,會導致客戶端出現如下幾個問題;

客戶端通過長連線獲取的訊息突增,下行頻寬壓力突增,其他業務可能會受到影響(例如禮物的svga無法及時下載播放);

客戶端無法快速處理渲染如此多的禮物和公屏訊息,CPU壓力突增,音視訊處理也會受到影響;

因訊息存在積壓,導致會展示過期已久訊息的可能,使用者體驗(QoE)指標會下降。

所以,因為這些原因,訊息是存在丟棄的必要的,舉一個簡單的例子,禮物的優先順序一定是高於公屏訊息的,PK進度條的訊息一定是高於全網廣播類訊息的,高價值禮物的訊息又高於低價值禮物的訊息。

根據這些業務理論,我們在真實程式碼開發中,可以做如下的控制:

結合具體業務特點,給各個業務型別的訊息劃分出不同等級,在訊息分發觸發流控的時候,根據訊息優先順序選擇性丟棄低優先順序訊息。

訊息結構體新增建立時間和傳送時間兩個欄位,在實際呼叫長連線通道的時候,需要判斷當前時間與訊息的建立時間是夠間隔過大,如果過大,則直接丟棄訊息。

增益訊息(糾正訊息),在業務開發中,訊息的設計中,儘量地去設計增益訊息,增益訊息指的是後續到達的訊息能夠包含前續到達的訊息,舉例來說,9點10的訊息,主播A和主播B的PK值是20比10,那麼9點11分分發的PK訊息值就是22比10,而不能分發增量訊息2:0,希望客戶端做PK條的累加(20+2 :10+0),但是存在訊息因為網路顫抖或者前置訊息丟棄,導致訊息丟棄,所以分發增益訊息或者糾正訊息會能夠幫助業務重新恢復正常。

四、寫在最後

任何一個直播系統,隨著業務的發展和直播間人氣不斷的增加,訊息系統遇到的問題和挑戰也會隨之而來,不管是長連線的訊息風暴,還是海量http短輪詢的請求,都會導致伺服器壓力的劇增,都是我們需要不斷解決和優化的。我們要針對每一個時期的業務特點,做直播訊息的持續升級,做可演進的訊息模組,確保訊息分發的能力能夠確保業務的持續發展。

vivo直播訊息模組也是逐步演進的,演進的動力主要來自於因為業務的發展,隨著業務形態的多樣化,觀看的使用者數越來越多,系統的功能也會逐步增多,也會遇到各種效能瓶頸,為了解決實際遇到的效能問題,會逐一進行程式碼分析,介面效能瓶頸的分析,然後給出對應的解決方案或者解耦方案,訊息模組也不例外,希望這篇文章能夠給大家帶來相關直播訊息模組的設計啟發。

作者:vivo網際網路技術-LinDu、Li Guolin

相關文章