前言
直播業務現在特別火爆,也給人們的互動方式帶來了很多新的改變,比如刷禮物、彈幕、排行榜等等。面對巨大的流量規模,直播技術的發展也備受關注。作為一個技術愛好者,相信你也會對直播的技術比較感興趣,于是我去翻了幾篇文章,了解了直播的技術方案,發現涉及到的技術細節太多,有部分已經是知識盲區,如音頻、視頻的編碼傳輸等。
直播中彈幕是一個非常亮眼和重要的功能,相比于秒殺架構,直播彈幕系統也有很多有趣的知識可以挖掘,一起來 YY 下如何設計一個直播彈幕系統,不對的地方還請有經驗的大佬指出。
直播彈幕系統
直播彈幕是一個讀寫 QPS 要求都很高,假設一個直播間有 100w 用戶同時在線觀看,假設彈幕的提交頻率為有 10000條/秒,那么需要每秒同時推送給在線用戶的次數為 100w * 10000。由此可見,讀請求的吞吐量需要遠大于寫請求,這點類似于 IM 實時聊天。、
架構設計考慮以下幾個場景:
- 支持直播彈幕回放
- 用戶進入直播間可以推送最新幾秒的彈幕數據
- 長連模式和短連模式可以做降級切換
MVP版本
為了不影響讀寫的性能,采用讀寫分離架構。
- 寫服務:若不考慮歷史彈幕可回放,可以直接使用 redis 作為唯一存儲。若考慮支持彈幕的回放,數據還是需要持久化,可以考慮使用 MySQL 或者 TiDB,暫且認為寫入不是較大的瓶頸。如果有更高性能的寫需求,HBase、OpenTSDB 等都可以解決問題。
- 讀服務:Redis 主要用于讀緩存,緩存直播間最新的彈幕數據,采用直播間 ID 作為 Key。系統讀服務最大 QPS = Redis 集群QPS。
Redis 存儲結構選擇:SortedSet。
- 提交彈幕:ZADD,score 設置為時間戳。進一步優化可以只存儲時間的 delta 值,減少數據存儲量。
- 彈幕查詢:ZRANGEBYSCORE 定時輪詢彈幕數據。
有什么問題?
- 系統性能與 Redis 集群容量強相關,性能提升需要擴容 Redis,成本高。
- Redis 重復請求較多,相同直播間會存在很多重復的輪詢請求。

緩存優化
如果能讓最新的實時彈幕數據都能命中本地緩存,那性能是最高的,同時大幅度降低了 Redis 的讀取壓力。所以彈幕讀服務可以每秒輪詢 Redis 數據,構建本地緩存。
熱點問題:
- 假設同時在線的直播間有 10000 個,讀服務機器有 50 臺,那么每秒輪詢 Redis 的 QPS = 10000 * 50 = 50w,讀取請求線性膨脹。
- 本地內存的使用量也隨直播間的數量增長而膨脹,每個直播間的緩存的數據量降低,導致本地緩存的命中率降低,容易導致 GC 頻繁。

熱點優化
如何降低本地緩存的使用量?
- 因為火爆的直播間會占據整個平臺大部分的流量,可以只針對火爆的直播間開啟本地緩存。
- 通過路由控制同一個直播間的請求分發到固定的幾臺機器,例如一致性 Hash 算法。通過減少讀服務機器上的直播間數量,達到降低本地緩存使用量的目的。

上述方法可以有效地解決問題,但是不能解決流量不均衡的問題。不同直播間分配的機器資源不是拍腦袋定的,需要有理論依據, 可以根據直播間的一些數據指標進行動態分配機器資源。
- 增加對直播間數據指標的統計,如單機 QPS、集群 QPS、單機直播間在線數等。
- 關于自適應的負載均衡又是一個可以深挖的話題,在這里我們討論幾個常用的方案,有可能結合起來使用效果更好。分桶:不同 QPS 的范圍段劃分為不同的桶,根據桶范圍的不同分配的機器數量也是不同的。最大最小公平分配:根據直播間的 QPS 劃分資源需求的權重比例,根據總機器的數量和權重比例來分配機器數量。啟發式算法:遺傳算法、蟻群算法等。
單元化架構
單元化可以說是解決性能容量以及容災的殺手锏。實現單元化需要完成有很多重要的工作,如數據同步 DRC、流量調度等,在此不作展開。

推薦一篇攜程的 DRC 實踐: 攜程異地多活-MySQL實時雙向(多向)復制實踐
客戶端長連接推送
為了保障客戶端消息的推送性能和實時性,長連接基本是必備的,最新的消息可以直接采用長連接實時推送。
- Push Server 從 Redis 中獲取用戶和直播間的訂閱關系以及長連接信息。
- 連接代理只負責與客戶端保持長連接。
- 海量的消息推送需要批量壓縮。

彈幕回放
增加一組專門用于回放的 Redis 集群,同時增加回放的本地緩存,其余設計與上述方案保持一致。
總結
抱著學習的心態,思考了直播彈幕系統的架構應該如何設計,本文主要討論了以下幾個點:
- 讀寫分離的架構設計
- 通過緩存優化讀性能
- 通過路由規則解決直播間流量不均衡的熱點問題
- 長連接的方案設計以及客戶端的消息推送
暫時就 YY 這么多吧,做好一個系統還有需要細節需要考慮:高可用、監控、限流降級、延遲優化等。有什么錯誤和好的實踐經驗歡迎留言。