构建基于浏览器的Web P2P网络直播
摘要
在2021年的互聯網時代,越來越多的網絡直播節目相繼涌現。瀏覽器是用戶最易接觸的渠道之一,聚集了大量觀看直播的用戶。當用戶們同時觀看直播內容時,服務器承受的負載隨著用戶量的增加而增大,會導致播放的卡頓,延遲等用戶體驗的下降;而且高昂的服務器帶寬成本也不容忽視。
那么是否存在一套解決方案,在保證用戶體驗與服務質量的前提下,又可以有效的降低服務器的負載與帶寬呢?那就是接下來要介紹的Web P2P技術了。
一、Web P2P的前世今生
2010年,Adobe在Flash Player 10.0推出了實時流媒體RTMFP,使用RTMFP在Flash Player之間進行P2P成為可能。在隨后的幾年內,該協議在網絡上如雨后春筍般的被廣泛部署,但是從2020年末開始,各家的瀏覽器都已經徹底不支持Flash了。我們又應該如何在Web里面使用P2P呢?
誕生于2011年的WebRTC技術,在Google、Microsoft、Apple、Mozilla等大廠的支持下,成為了H5的標準,并且Chrome、Edge、Safari、Firefox等主流瀏覽器也都支持WebRTC。所以就可以通過WebRTC來實現P2P了。
下文把基于WebRTC實現的HTML5 P2P簡稱為H5 P2P。
二、WebRTC簡介
WebRTC的總體架構如下圖所示,如果是瀏覽器開發者,通常需要關注并實現WebRTC C++ API,而我們的目標是基于瀏覽器實現P2P,所以只需要關注Web API即可。
整體的H5 P2P都是基于RTCPeerConnection和RTCDataChannel來實現的,只是實現數據層面的P2P,不包含音視頻通信的內容。
三、總體架構
1. H5 P2P客戶端
主要包含通信模塊、傳輸模塊、節點管理、調度模塊、存儲模塊和統計模塊:
通信模塊:主要負責和服務端(上圖中的Index、CPS、Tracker)進行信令的通信
傳輸模塊:包含HTTPClient和基于RTCDataChannel實現的RTCClient(上傳&下載)
節點管理:不同用戶間P2P主被動連接、斷開、淘汰、資源信息同步
調度模塊:根據buffer水位、分片信息、節點質量和規模進行CDN/P2P調度
存儲模塊:負責媒體數據的存儲、排序、以及淘汰
統計模塊:包含系統運行的埋點的實現
2. H5 P2P服務端
主要包含Index、CPS、Tracker和Stun服務:
Index:索引服務,提供其他服務地址。
CPS:資源屬性服務,將直播流的URL轉換為RID。
Tracker:資源追蹤服務,主要存儲用戶和資源之間的索引關系。
MQTT:信令服務,轉發用戶間建立P2P連接的offer/answer/candidate SDP協議。
Stun:采用coturn部署,主要用戶節點之間建立對等連接的UDP穿透。
四、階段流程
下圖簡略的展示了整體方案的各個階段、流程:
Step 1:網頁打開,首先向Index服務請求其他服務的地址,保存于內存中
Step 2:客戶端收到直播請求,向CPS發送請求將URL轉換為資源ID(RID),相同內容相同清晰度的直播流的資源ID(RID)是相同的,表明可以互相分享數據
Step 3:客戶端首先會啟動HTTPClient進行數據下載,優先保證秒播。同時做以下2件事
a. 客戶端向Tracker服務發送Address協議,獲取其他正在觀看該直播的用戶地址,待后續建立連接,分享數據
b. 客戶端向Tracker服務發送Publish協議,將自己的信息告知Tracker,使得別人可以即使搜索到自己,進行數據分享
Step 4:向從Tracker獲取的其他用戶建立連接,彼此交換信息,數據分享
五、連接建立
假設在Peer1和Peer2之間傳輸數據,那么在傳輸之前,首先需要建立連接。在WebRTC中,瀏覽器間通過ICE框架建立對等連接,主要包含Stun、TURN,下面分別簡單介紹一下:
1. ICE
ICE(Interactive Connectivity Establishment)提供的是一種P2P連接框架,統一各種NAT穿透技術(STUN,TURN)?;趏ffer/answer/candidate模式,通過多次地址交換,然后進行連通性測試,穿透用戶網絡間各類防火墻。ICE主要包含STUN、TURN等協議。
2. STUN
STUN(Simple Traversal of UDP Through NAT)允許位于NAT設備后的客戶端找到自己的公網IP/Port地址,以及查詢出自己的NAT設備類型,通過這些信息使得兩個同時位于NAT后的2個設備之間建立UDP通信,具體的STUN流程可以參考RFC5389,不在此贅述。
3. TURN
通俗的說,TURN(Traversal Using Relays around NAT)就是中繼/轉發,并不是所有的設備之間都可以依靠STUN來建立連接的,那么當兩個設備之間無法依靠STUN建立連接時,就會啟用TURN來進行數據中轉。
由于我們的目標是節約帶寬,如果采用TURN轉發的話,并不能減少服務器的帶寬消耗,另外一個原因是,在整個建立連接的過程中,有別于傳統的視頻通話只能1對1,這里的傳輸模式是1對多傳輸,所以并不要求每個連接都可靠,因此在H5 P2P項目我們僅僅通過STUN來實現連接的建立。
六、數據下載
1. 資源編碼
Web P2P網絡直播方案也可以同時支持任意多路直播同時進行,必須保證各路直播流之間的獨立性,因此,我們提供一種統一的資源編碼方式,用來標識唯一的一路直播流。這里主要借用直播流中的StreamId,通過計算MD5,生成固定長度的資源ID(RID),不僅保證了內容相同,清晰度不同,RID也不同;也保證了相同的內容,由于斷流切換源地址,導致URL變化,保證資源ID(RID)相同。
2. 下載調度
在整個直播過程中,由于可播放緩存的數據是非常小的,通常不超過10秒?因此如何實時的根據當前的網絡狀態、網絡環境,作出使用CDN,又或是使用P2P的決策呢?這里,我們的最佳實踐主要如下:
a. 為了保障用戶的觀看體驗,當緩沖區的可播數據低于某個閾值(例如5秒),就切換CDN下載
b. 當發現某一片未來的數據,周圍的人都沒有的時候,即使可供播放的buffer還有很多,不滿足a策略的描述,這個時候,為了盡快的分發數據,也應該及時切換至CDN,出現這種情況時,往往表示這個節點以及他連接的節點都處于整個直播數據分發的頂層,為了防止出現 “一人下載多人圍觀” 的場景,本著更快的下載導致更早的上傳理念,不僅僅要求該節點自身盡快CDN下載,同時也保證周圍用戶某個比例(例如15%)也盡快采用CDN下載,從而使得整體數據的分發效率提升。
3. P2P任務分配
a. 任務編號
對于每一個數據塊都有一個自然數編號(sn),對于這個數據塊,我們按照固定長度(默認16KB)的大小進行切分,簡稱chunk_index,那么(Rid, sn, chunk_index)就可以唯一確定一個數據片了。
b.分配請求
分配過程類似于撲克中的發牌機制,唯一的要求是對方這個節點擁有這個數據塊,并且質量越好的節點,越多越分配靠近播放點的、重要的數據。下圖簡明扼要的展示了3個相同質量節點分配2個數據塊(9個數據片)的過程。
4. 內存控制
對于Web P2P來說,所有的媒體數據都緩存在內存中,但是,也并不是會緩存完整的數據。對于直播來說,所有用戶的觀看點都是非常接近的,因此,內存始終緩存播放點前后的固定大小的數據。因為只有這部分數據分享利用率才是最高的。
如上圖所示,有顏色的數據塊都是當前在內存緩存中的數據,其中紅色數據塊表示當前的播放點,黃色數據塊表示過去已經播放過的內容,綠色數據塊表示未來還未播放的內容,可以看到當播放點從100移動至101時,97號數據塊就被淘汰了,同時103號數據塊作為最新的數據被下載下來并緩存到內存中。
七、工程實踐
1. Goggle FlatBuffers
不管是服務器通信還是點對點的P2P傳輸均采用了FlatBuffers。FlatBuffers具有跨平臺、內存和效率速度高,擴展靈活等優勢,被我們全平臺統一采用,大幅降低了通信成本。
2. 多瀏覽器兼容
線上支持WebRTC的瀏覽器不僅種類繁多,而且同一個產品的不同版本之間也可能千差萬別。為了解決這一痛點,最終采用了WebRTC adapter.js來解決這一挑戰。
3. 內存池
盡管Node.js自帶垃圾回收機制,以及內存管理策略,但是GC和內存分配也是需要成本的,況且Web端是單線程環境,任何性能的提升都會減少時間上的損耗,進而可以處理更大吞吐的網絡層傳輸以及業務邏輯。所以在內存分配上我們采用了內存池策略,基本實現了100%的內存重復使用。
八、技術結果
下圖展示某次大型直播的帶寬分時統計,顯示在19:25到21:00階段內的真實數據,隨著人數的涌入和離去,Web P2P全程帶來可觀的帶寬收益。最終評估Web P2P,在不影響用戶使用體驗的前提下,不僅可以有效降低服務器的負載壓力,并且能夠降低可觀比例的帶寬成本。
九、思考總結
目前,該技術已經大規模應用在各網絡直播中,良好的解決了服務質量與帶寬成本的平衡。但是,我們也發現在超大規模的直播下,存在了一些提升的空間,主要如下:
(1) coturn與MQTT的服務性能不高,會制約直播的服務質量。
(2) 接入邊緣網絡,打造多元化網絡,緩解CDN負載,并且可以進一步降低CDN的帶寬成本。
總結
以上是生活随笔為你收集整理的构建基于浏览器的Web P2P网络直播的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: python中的运算和运算符
- 下一篇: 直播客户端和浏览器使用桌面共享时出现黑屏