天天看點

P2P技術将直播帶寬降低75%

實時直播經過去年的千播大戰後已經成為網際網路應用的标配技術,但直播平台的成本卻一直居高不下,各個平台除了挖主播、挖網紅以外,其背後高額的帶寬費用也是他們最大的一塊成本。現階段直播技術在傳輸方面分為兩塊:CDN和連麥系統,CDN負責流媒體的分發傳輸,連麥系統負責解決同時多個主播間互動的實時通信傳輸問題。我們始終認為基于CDN+連麥系統的直播技術是一個高成本高消耗的技術,從各大直播平台紛紛虧損就驗證了這一點。除了帶寬成本,延遲問題也是現在直播技術一個硬傷。我們很早就意識到現在傳統的直播技術是無法大規模進行線上教育互動直播,是以學霸君從2016年下半年就開始研發基于UDP和P2P技術的互動直播技術架構。整個系統的設計目标是:

1.      端到端延遲控制在秒級範圍之内。

2.      在不影響視訊品質的情況下盡力節省分發帶寬。

整個基于P2P技術的分發架構在一個10W+直播平台上進行了9個月的測試和調優,初步達成了設計目标。那整個系統是怎麼設計的?使用了那些技術來達成目标的?那接下來重點來說一說架構設計和技術細節。

2.P2P分發網絡架構

         整個傳輸分發網絡我們把連麥系統和分發系統合二為一,将分布式推流與邊緣節點分發作為一套傳輸體系,通過服務之間的P2P通信和路由選擇來實作連麥的最小時延,架構如下圖:

圖1

         整個傳輸分發網絡分為三部分:推流部分、服務之間P2P和客戶節點P2P。這個傳輸網絡有一個系統錨點:假定推流者speaker推到Edge server上是不會發生丢包和延遲的,Edge server會通過服務間P2P快速将收到的流資料分發到其他的Edge server,而且在這個過程也不會發生延遲和丢包。為什麼需要這樣一個錨點?因為在客戶節點的P2P網絡需要保證流暢性和最小延遲,也就是要求所有的Edge server在最短時間周期内擁有的完整的資料,至于為什麼要這樣,後面我們在流補償環節重點介紹。

         我将通過整個流資料傳輸過程來解析具體的技術細節,但在這之前首先要解決的就是媒體資料分片問題,所有的傳輸過程會基于分片(segment)來設計。

2.1媒體資料分片

         媒體資料分片是整個分發傳輸體系中最為基礎的部分,我們在設計分片時主要考慮的是時延和消耗的問題,分片如果太大,傳輸的時延就會越高,例如:HLS。如果分片太細,網絡中回饋封包就會很多,對P2P網絡來說額外的消耗也是個問題。最後我們借鑒了RTP和FLV中的經驗,采用按幀來做資料分片,這樣做有以下幾個好處:

ü  按幀分片延遲粒度小,可以在幀傳輸進行延時優化

ü  實作簡單,與編解碼器編碼原則一緻

ü  組合靈活,可以實作播放buffer無縫結合。

每一個分片稱作為segment,用一個自增長的32位ID來表示唯一性,傳輸過程都是以這個ID為标示來确定資料的完整性。

2.2推流與連麥

         确定好了媒體分片就可以進行推流了,我們把推流和分發的路徑合二為一,上麥者是将流資料segment推送到離自己最近的Edge server上,而不是推送到專門的連麥系統上。我們推流傳輸使用的是RUDP傳輸算法,這個RUDP是采用了類似BBR基于延遲和丢包來設計的擁塞算法,并且對封包做了擁塞丢棄,示意圖如下:

圖2

關于RUDP的細節可以參考我的另一篇文章《UDP是怎麼實作可靠的》。至于為什麼不采用RTP或者RTMP/TCP來推流,RTP雖然是基于UDP的,但需要通過RTCP和NACK來保證可靠性,需要設計擁塞算法,也需要對RTP進行改造擴充,而且還受RTP協定本身的限制,是以我們并沒有直接采用RTP。對于RTMP/TCP設計是很簡單,但在弱網環境延遲很大,而且容易引起重連,是以在設計之初就否定了。

2.3server間的P2P

         因為整個傳輸分發網絡是分布式的,由多個edge server組成,而且其他的連麥者可能在其他的edge server上。是以基于系統錨點,媒體資料分片到edge server上必須盡快的分發到其他的edge server上。最早我們采用的是統一用BGP server來中轉,這樣的耗費的BGP帶寬很多,而且BGP server一旦異常,整個edge server之間的通信就中斷了。其實大部分時間跨營運商之間的edge server之間延遲也沒有想象的那麼大,這可以考慮使用Edge server之間點對點通信來解決問題,是以我們設計了一個基于RUDP無視窗多路徑的傳輸模型來進行edge server之間的通信,如下圖:

圖3

上圖的通信模型是一個多路徑并聯通信模型,我們在RUDP發送前添加了一個路徑路由表,這個路由表記錄了各個路徑的分發機率,RUDP每次向接收端發送包時會通過路由表中的機率來選取路徑,那麼确定路由表機率就是一個非常重要的事情。我們是通過RUDP實時的ACK回報和路徑實時ping探測來得到網絡的狀态(丢包,延遲,抖動),再将網絡狀态參數輸入到逼近函數來确定各條路由的機率,這裡有條原則:如果edge server之間直連的延遲和丢包足夠小的情況下,直連通信路由的機率将會接近100%,如果某一條路由出現周期性斷開或者延遲超過200ms,它的機率會接近0。以下是整個路由機率評估的過程示意圖:

圖4

3.P2P網絡建構過程

         媒體流資料通過edgeserver間的P2P多路徑傳輸網絡到達各個edge server上,接下每個edge server需要将流資料分片下發到各個客戶節點上,我們針對了上麥節點做了傳輸特殊處理讓時延更小,過程和普通的RTC通信模型相似,這裡就不贅述了。觀看節點上分發采用的自組織P2P網絡進行資料分發的,既然是通過P2P下發的,那麼就要在客戶節點群建構一個P2P網絡,這個網絡是怎麼建構的?具體分為三步:連接配接、評估、分層。

3.1連接配接

         客戶節點程式是運作在客戶機上的,大部分客戶節點都會在路由器或者NAT後面,他們之間要互相建立連接配接,必須穿越彼此的NAT和防火牆。雖然現在穿越NAT的方法,例如:STUN、ICE等。但穿越連通率始終是一個問題,如果穿越率太低,會讓很多優質的節點資源得不到充分利用。在設計穿越方案時直連連通率放在第一位,我們通過修改STUN協定設計了一種基于端口多次猜測和嘗試的穿越機制,先會通過類似STUN協定判斷NAT類型、NAT端口變化規律、NAT是否有黑名單機制等資訊,然後将這些資訊存到轄區連接配接中的edge server中。當有夥伴節點來與它穿越,會交換彼此的這些資訊,不同的排列組合會有不同的穿越政策,每一次穿越的過程和結果都會記錄到我們的背景資料庫,我們會周期将這些資料進行分析并調整協商穿越政策。如下圖:

圖5

         穿越完成後,節點之間就會進行連接配接握手和身份證書(關于為什麼要證書後面細講),建立通信聯系和鄰居關系。那麼鄰居關系是怎麼動态變化的呢?

3.2鄰居關系與評估

鄰居問題

         連接配接一旦完成,節點與節點之間就成為鄰居,彼此會進行狀态交換和心跳,那麼問題來了,一個直播系統有成千上萬的節點參與,如果都兩兩相連的話光心跳通信就可以将客戶節點的上傳帶寬占滿。我們基于這個原則設計了一個節點LRU淘汰連結清單,連結清單中保持40個聯系的鄰居節點。但老的節點會退出,新的節點會加入,LRU會根據鄰居與自己的通信狀态來進行LRU新增和淘汰,原則如下:

就近原則,内網優先,同城同一營商網絡次之。

周期性評測延遲和媒體分片命中率,末位劣汰。

當LRU清單中節點不足40個時會從備用節點清單中選取進行新的節點進行連接配接并加入到LRU中。

節點評估

         每個客戶節點的計算能力、通信能力、網絡分區等都不一樣,這使得我們必須對每個節點做一個評價,對一個節點的評價分為兩部分:鄰居節點對自己的評價和自己對自己的評估。鄰居評價主要是:

RTT

丢包率

請求命中率

通過這三個參數會對每個鄰居計算出一個親和力分值score,這個值會用于後面的分發選擇。

評估自己主要是:

CPU、記憶體

網絡類型,WIFI/4G/有線網絡

上傳帶寬

節點會周期性計算這兩類參數,通過一個網絡QOS收斂函數來計算節點能力和對鄰居QOS政策。

3.3節點分層

         節點評估最終的目的是讓有能力的節點成為超級節點(super node)來分擔edge server的分發壓力。那麼一個節點成為超級節點的條件是什麼呢?有以下幾個條件:

有足夠的上傳帶寬,4G和弱WIFI下不能成為超級節點

有空閑的CPU和記憶體,計算能力不夠的低端移動裝置不能成為超級節點

對鄰居通信友好,不是通信孤島

得到edge server的任命,和edgeserver之間通信順暢。

超級節點如果性能衰減了怎麼辦?答案是會退化成普通節點,因為節點評估是周期性實時進行的,如果發現節點性能衰減,edge server會讓其退化。

         既然任命了超級節點,那麼超級節點是怎麼工作的?每一個超級節點在被任命時都會配置設定到一個分組ID,edge server會根據自己轄區超級節點數量進行分組,每個分組有多個超級節點組成,分組内的超級節點群負擔自己分組的媒體分片分發。例如:有5個超級節點分組,這時機關周期内有1 ~ 20個segment,那麼第一個分組負責1、6、11、16編号的segment分發,以此類推第二組負責2、7、12、17 …。這樣做是防止單個超級節點失效造成網絡,增強P2P分發的穩定性。示意圖如下:

圖6

4.P2P流媒體分發過程

         通過上面的P2P網絡建構過程我們知道整個P2P網絡其實是一個分層有向圖分發網絡,那麼具體是怎麼進行流資料分發的呢?也分三步:先推(push)、後拉(pull)、再補償,下面來仔細解釋是怎麼實作的。

4.1push

         在介紹超級節點時有提到會根據segment ID将資料推到對應的超級節點分組群上,超級節點收到這些segment後怎麼要怎麼處理呢?按照P2P設計的原理應該将資料轉發到其他分組的超級節點或者普通節點上,但是如果都這樣推有可能會造成網絡發送備援而消耗過多的帶寬。為了解決這個問題設計了一個預先訂閱機制,原理就是每個P2P客戶節點會根據自己緩沖區最大的segment ID來進行預訂,提前預訂10秒以後的媒體資料分片,預訂請求是根據節點評估出來的親和力值score來做權衡随機請求,收到這些請求的超級節點會将預訂的分片請求資訊儲存下,等到edge server推送到這個分片到這個超級節點,它就會無條件轉發這些被預訂的封包給發起預訂的節點,如下圖所示:

圖7

從上圖中可以看出以下幾個原則:

從edge server到所有節點路徑最多兩層,這樣做是為了控制鍊路延遲

不同分組supernode之間會互相訂閱對應分組的segment

普通node隻會向super node發起訂閱

4.2pull

         資料segment通過預先點閱的方式進行push推送到各個客戶節點,但網絡是會丢包,super node也有可能會中途退出,這樣就會造成最終的節點發生丢包,那丢包了我們怎麼辦?我們設計一個向鄰居拉取缺失分片的機制,大緻的流程如下:

1.      節點周期性檢查丢失分片的資訊和收到分片的資訊,建構一個gossip協定向鄰居交換緩沖區資訊

2.      節點收到鄰居的gossip資訊,将對方擁有的分片資訊記錄到本地

3.      本地根據記錄鄰居的分片資訊查找自己丢失的分片,通過鄰居親和力值score進行權衡随機選取鄰居,并向選取的鄰居發起pull請求

4.      收到鄰居拉取分片請求,将分片發往請求的節點。

整個步驟會周期性嘗試多次拉取,示意圖如下:

圖8

         這裡值得一說的是因為會周期性交換緩沖區的gossip資訊,這意味着緩沖區的gossip資訊越小越好,我們設計了一個類似bloom filter來描述gossip資訊,不僅可以減小gossip封包的資料大小,而且比對速度也很快。

4.3補償

         因為P2P的客戶節點是不穩定的,有可能某個segment通過拉取多次還是沒有收到,這個segment又臨近播放位置,那麼缺失這個segment的節點會直接向edge server請求補償讓其盡快傳送這個分片,這樣做的目的是防止因為P2P通信造成丢包的卡頓。這也就是說每個edge server需要擁有所有分片資料,也是系統錨點。流程入下圖:

圖9

這個流程大部分情況下沒有問題,但如果同一時刻大部分客戶節點都缺失某幾個segment分片時,會有大量的補償請求到edge server上會造成網絡風暴。我們在應對這個問題設計了個稀缺評估和拒絕服務的機制。這個機制當機關時間内太多個補償請求到達edge server會拒絕自己能承受之外的請求,隻重發承受範圍之内的分片。而且這個過程還會對補償請求做稀缺評估,如果某個分片大部分節點都沒有,它會主動将這個分片通過super node群再推送一次。

4.4緩沖buffer與時延控制

         通過上面的三個階段可以将所有資料segment分發到每個客戶節點上,但客戶節點需要一個緩沖buffer來配合這個三個階段和本地的播放,buffer如果緩沖時間過長,會引起不必要的延遲,如果過短會造成卡頓和三個階段不完整。為此我們設計了一個三階段buffer動态緩沖區,入下圖所示:

圖10

來解釋下上圖的各個區間的意思:

          Push區間:因為分片是通過不同的supernode推送過來的,那麼必然會造成一定的抖動,是以在buffer最開始的頭上會有一個jitter緩沖階段,直到第一個鄰居節點gossip資訊中有這個分片push位置結束,這個階段一般100 ~ 300ms。

          Pull區間:分片時序進入pull區間後,會周期性檢查丢失的分片,根據gossip掌握的鄰居進行權衡拉取,會進行3次嘗試,每一次嘗試時間是本地節點與鄰居之間的RTT值。3次失敗進入補償區間。

          補償區間:分片時序進入補償區間後,也會周期檢查丢失的分片,根據丢失的分片ID直接向edge server請求拉取,嘗試4次,每次嘗試時間一個本地節點與edge server之間的RTT。如果4失敗進行waiting狀态,等待鄰居gossip或者edge server主動推送。

          過期區間:被播放後的分片會放到這個過期區間中而不是立即删除掉,為什麼呢?因為每一個節點的播放時間點不同,有可能本地播放的分片正是其他節點丢失的分片,有可能其他節點會通過pull來拉取,是以我們會把播放後的分片放在過期區間3秒後再删除。

4.5秒開問題

         上面分發的三個階段和buffer控制解決了流持續分發和播放延遲控制問題,但現階段所有的直播技術必須要有秒開,其實P2P分發在解決秒開比單純的server CDN轉發來的更加簡單。秒開就是使用者進入直播間時瞬間能看到主播的視訊圖像,那秒開的宗旨是新進入的客戶節點要求服務端邊緣節點從視訊的上一個GOP關鍵幀開始發送資料,客戶節點再根據視訊編碼器從這個GOP關鍵幀0等待加速播放即可 。我們在P2P分發網絡中新進入的節點會收Edgeserver的上一個GOP關鍵幀分片ID,客戶節點根據這個ID從各個鄰居中快速拉取整個GOP分片資料,而不是單純的讓Edge server來發,秒開的速度平均縮短了100 毫秒。

5.P2P内容授權

         直播分發技術除了傳輸分發以外,還需要考慮内容防盜和授權,P2P系統中更加需要考慮系統安全性。我們引入了CA憑證和雙端協商加密方案來保證鍊路的合法性。大緻的做法是每個合法的節點單元(edge和所有的客戶節點)會向CA發起合法校驗,如果檢驗通過,CA會根據節點的ID、節點RSA公鑰、授權起始時間、授權終止時間等資訊利用CA的RSA進行證書生成。每個拿到證書的節點單元需要和其他的節點進行通信,會先交換證書,校驗對方證書的合法性,讓後利用證書中的RSA公鑰加密一個對稱加密算法的KEY傳回給證書方,證書方收到加密的KEY會用RSA私鑰解密得到對稱加密的KEY,這樣雙方就完成了合法性校驗并利用這個交換的KEY進行封包加解密通信了。流程如下圖:

圖11

6.線上資料對比

         上面的技術分析隻是幫助讀者這了解這個系統的運作機理,除了這個以外,當然需要公布下線上資料來佐證下系統可行性,下圖是一個10萬+線上使用了這套P2P的直播平台線上對比資料。我們在同一個edge server上的同一個直播間對象中,把一半的使用者節點關閉P2P,一半的使用者開啟P2P,來觀察一天中同一個Edge server上這兩部分使用者群的帶寬消耗情況。