P2P技术如何拯救一家直播网站
眾所周知運維成本是直播網站最大的成本組成,運維成本則主要體現在帶寬,而伴隨主播與用戶對視頻清晰度以及連麥的需求不斷提升,直播帶寬也在與日俱增。本文整理自學霸君音視頻技術負責人袁榮喜在LiveVideoStackCon 2017上的分享,通過實踐案例講解了如何使用P2P技術將帶寬和延遲降低到傳統技術的1/3,并詳細介紹了P2P分發算法的架構設計和技術實現細節。
演講 / 袁榮喜
整理 / LiveVideoStack
大家好,我是來自學霸君的袁榮喜,我演講的主題是《P2P技術是如何拯救一家直播網站的》,眾所周知直播網站最大的成本就是運維成本,當然還會有一部分主播的成本,而運維成本的主要表現就在于帶寬,從目前統計來看,一個直播網站的成本大概有30%來源于帶寬。
一個直播網站的窘境
我的一個朋友是做直播平臺的,每天大概有10萬人觀看,在經歷了2016年的千播大戰之后,他們升級了觀看體驗——主要是提高了分辨率和清晰度,但成本也因此提高到了原本的5倍,也就是假如原本是20G,現在每天要付出100G的帶寬流量,這對于一個普通的平臺來說成本是致命的。
基本情況
那么我們先來看下這家平臺的具體情況:它是南方三線城市的直播平臺,PC占有率大概有70%,移動端占有率在30%左右,碼流也從320x240,20K升級到640x480,大概每秒的單路碼流在800kbps左右,也就是100KB/S的樣子 。因為它和YY類似,都屬于聊天室形式的,也就說每一個觀看端都會有上麥的需求,也因此必須要解決延遲的問題,如果延遲太大,在上麥的過程中和下麥的過程中有可能會產生信息不對等,由此丟失一部分信息,因此對于觀看端延遲要求在兩秒以內,而麥與麥之間的延遲要控制在500毫秒,這就對延遲有非常高的要求。
成本
前面提到平臺觀看人數能達到10萬以上,它每天在邊緣節點上就需要用83G來扛住邊緣節點的分發,由于系統是分布式的,因此在邊緣節點與邊緣節點之間也需要做分發,這部分又會需要2G的BGP帶寬流量,這個帶寬很大。因此它需大概100臺物理機來抗,對于私有云搭建的成本我并不是很了解,如果按照公有云來計算的話,每個月大概要支付百萬以上的成本,這對于一個三線城市的小公司來說壓力是巨大的,況且還有比較沉重的業務和寥寥無幾的融資渠道。
存活的希望
那么平臺想要繼續存活,就需要將帶寬成本降到現在的1/3,也就是只用30G就能解決問題。將產品遷移到CDN上是一個解決方法,畢竟各大云廠商目前的優惠力度還是比較大的,但他們在前期采用CDN做測試時發現會有兩個問題:首先是延遲問題,在很多分享中也有提到CDN的延遲至少是3-5秒,有時候更大,尤其是在大規模直播下;而第二個問題則是每個觀看端都會有上麥的需求,但連麥服務是需要額外付費的,綜合計算使用CDN也無法降低成本,甚至還有增加。
那么我們考慮是否可以通過調整編解碼來實現,大家都知道H.265從編碼效果上來說可以減掉一半的成本,我們也在小規模內嘗試了替換,不過發現存在CPU消耗的問題,因為觀看端的設備比較多樣,既有移動端、也有PC端,并且設備的性能參差不齊,在較差的機器上會頻繁出現卡頓,根據統計大致有50%的用戶無法接受這個方案。
那么既然是傳輸產生的高成本,是否可以對傳輸方式做優化——P2P技術,它早期源于音頻分享和文件分享,并且是下一代CDN的主流技術。在做這樣一個系統之前首先需要調研目前用戶端上傳的最大帶寬,我們發現有50%以上用戶的上傳帶寬在1.6mbs以上,平均來看大概可以做到640KB/S 碼率的上傳程度。前期調查后,我們確立了系統設計的三個目標:降成本、控延遲和保質量,也就是在保證用戶體驗下將成本降低1/3,在后期的小規模測試和大規模替換中的效果還是比較滿意的。
流媒體P2P分發系統
網絡架構
接下來我們重點講解這個分發系統的具體細節,首先上圖是系統的架構圖,大致分為四層:最上層是BGP——用來協調邊緣節點與邊緣節點之間的分發;第二層是邊緣節點,它主要是用來分發媒體數據到觀看端;第三層我們在客戶端層面上做了一層超級節點,用來分攤大部分邊緣節點的分發壓力,這一層的主要目的就是為了節省帶寬;第四層是普通節點,這主要是由于有些端可能是4G或者一些比較差的WiFi,它們可能沒有上傳的能力。那么在旁邊還有一個小紅圈,它表示推流的過程,因為業務中對上麥、連麥有需求,我們沒有用連麥系統,而是采用了一種推流加連麥的方式來做,使用的是可靠UDP,也就是RUDP技術。
三部分網絡
整個網絡分為三部分:一個是推流部分,就是我們要保證流是要推到邊緣節點上;第二是邊緣節點與邊緣節點要做一個可靠、快速的分發,讓所有數據盡量快到達Edge server,這樣保證延遲比較小;第三部分是P2P,當數據到達Edge server上,我們要快速通過P2P網絡分發到各個觀看端進行播放。在這個系統中有個錨點,就是所有Edge sever都應該盡快拿到所有的視頻數據和音頻數據。
在具體介紹這三部分網絡實現方式之前,我們先來看下流媒體中最重要的一部分——分發切片,也就說數據如何打包才能符合我們的要求和場景。最早我們采用HLS的方式,也就是一秒打一個分片,不過它會引來延遲的問題,從實際的測試中我們發現會有大于一秒的延遲,但如果分片太小了就又會引入其他問題。經過測試,我們最終采用一幀為一個分片,這樣可以帶來三個好處:首先是粒度小,每一幀大概在幾十毫秒,這樣我們可以盡量去優化這個延遲;第二點就是實現簡單,因為每一個編解碼都是基于幀的編解碼,這樣天然的就是一個分片模式;第三點是組織比較靈活,可以與播放buffer無縫接合。
推流
接下來我們具體講解這三個網絡是如何實現的。首先是推流,推流一般來說會用到RTMP或者說TCP的方式,但它會帶來兩個問題:在網絡不好的情況下,因為每個端都需要上傳和上麥的操作,這樣會引來延遲問題,我們測試發現TCP最大的延遲有可能達到1.2秒,這是不可接受的;第二個就是在弱網環境下大量傳輸數據,它會產生TCP連接頻繁的斷開,這就會造成數據推不上去或者間斷性的推上去,從而導致觀看節點頻繁卡頓。
UDP可以很好的解決延遲和連接的問題,但由于它的不可靠,會產生丟包問題,因此我們借助了WebRTC的GCC 原理,在UDP之上設計了一層可靠,保證數據可以盡快、盡好的傳到Edge server上。上圖是它的原理,它通過一系列并發的發包,最后再通過確認機制來回饋要重傳的包或者說什么時候重傳包。
單路徑RUDP
數據到了Edge server上之后就要把數據快速的傳輸到其他Edge server,我們最初的設計和CDN一樣——通過BGP server單線的中轉,也就是Edge server將數據傳到BGP server,再由BGP server傳輸到其他Edge server上,這種做法最大的好處在于實現簡單,并且鏈路查找問題比較明確。但同時它也存在兩個問題:首先就是成本比較高,因為BGP的成本是整個Edge server邊緣節點帶寬成本 的10倍;第二點因為它是建立在私有云上,而私有云每個機房都有防火墻,而當網站頻繁受到攻擊時就有可能變換防火墻策略,這樣就有可能導致某條鏈路不通或者到BGP不通,由此就會影響Edge server無法接收數據包,從而導致觀看節點卡頓。
多路徑RUDP
由此我們在RUDP之上設計了一個多鏈路的傳輸(如上圖):首先Edge server之間是直連的,同時有多個BGP server來做relay ,中間是無狀態的,也就是即使某一條鏈路出現問題,其他幾條鏈路還在并行傳數據,這樣就能保證盡量的可靠和可用。其次為了節省成本這里還有一個設計原則,Edge server之間盡量保證直連,不走BGP或者少量走BGP,這樣就可以節省一大部分BGP流量,根據統計可以節省80%的成本。
P2P網絡構建
要在P2P網絡上做數據分發,就要構建一個魯棒性的P2P網絡——也就是動態可靠的P2P網絡,構建這樣一個網絡需要分為三步:首先是連接——客戶端之間要相互建立連接;第二是節點之間的評估;第三是節點分層。
P2P連接
對于連接,穿越是一個非常頭疼的問題,而我們在最初的版本也是基于NAT的方式來做的,但同時引來一個問題——穿越里只有60%,也就是說有可能在某個防火墻或者NAT后面的節點擁有很好的上傳資源,卻沒有得到利用。因此我們通過STUN協議做了一個多端口的猜測機制,通過這個機制可以將整個穿越率優化到80%。但依舊有一部分無法穿透,這是由于有些廠商會設置黑名單機制導致無法穿越,因此我們設計了云端協調穿越時機的機制,從而將穿越率優化到大概90%,也基本達到我們的預期。
完成節點之間穿越,就可以做P2P通訊了。首先要建立心跳關系和心跳的狀態交換,不過雖然從原則上來講,一個節點跟所有的節點或者跟大部分節點能通訊是好的,但從實際效果來看并非如此,因為直播時有可能是一千個節點甚至更多節點,而一個節點不可能于這么多節點 頻繁的做心跳或者信息交換,否則本身的帶寬就會被這種探測包消耗掉,因此我們一般最多會選擇40個節點,而這40個節點也并非是固定不變的,它會有優勝劣汰的機制,當有節點被淘汰時我們就會從沒有穿越的節點中繼續穿越,然后達到一個平衡,如此就會形成一種群居效應——好的節點會盡量聚集在一起。
節點評估
在確定了鄰居和通訊狀態以及探測機制后,我們就要對整個通訊做評估。評估分為兩部分:一個是評價鄰居,主要通過丟包率、RTT、通信命中率以及流媒體傳輸的穩定性計算出一個親和度分值;第二就是評估自己,主要通過CPU、帶寬復用、網絡類型等計算出一個load(負載)值,通過它我們就能知道本地節點跟其他節點大概處于什么位置,我們就能進行網絡策略調節、通訊的QOS、節點區分以及網絡收斂。我們早期的網絡收斂是通過對照表的方式實現的——也就是經驗值,但在網絡高度動態時,會出現網絡波動以及與鄰居之間的通訊失效,因此我們采用一個簡單的神經網絡和一個收斂函數f(x) 來做參數的調整決策。
節點分層
完成節點評估后,我們需要根據評估結果做節點區分,也就是要解決超級節點和普通節點的問題。超級節點最重要的作用就是分攤Edge server的分發壓力,同時還可能承擔一部分網絡節點管理工作。超級節點的產生方式有兩種:一種是自我推薦,這種方式它需要得到Edge server的許可;第二種是Edge server自身的分發壓力過于繁重,就會從質量比較好的普通節點中挑選一些比較穩定的成為超級節點。由于網絡是動態的,有可能會出現衰減、分割、斷開的情況,因此超級節點并非是永久的,當超級節點的網絡出現衰減,評估函數f(x) 會作出反應降級為普通節點,這其實是一個高度動態的循環過程 。
P2P分發媒體數據
成功構建網絡之后就是如何做數據分發,我們將它分為三步:先推、后拉、再補償,我們下面逐一做詳細介紹。
三階段——推
我們假定已經選舉好一個超級節點,那么邊緣節點就會按照上圖向下推送媒體數據到超級節點,超級節點再向下推給其他節點或超級節點,這個推送本身是樹型結構,而P2P是圖形結構,因此我們引入了預先訂閱的機制。舉例說明,假設某個節點播放到媒體包的第十秒,它就要開始訂閱第二十秒到三十秒的包。那么訂閱是如何完成的呢?超級節點在確定自己身份時會確定一個區域值,那么普通節點就會向所在區域的超級節點訂閱(有可能會向多個超級節點訂閱),對前面的例子而言,節點有可能會向超級節點A訂閱一三五七九秒的包,向B訂閱二四六八十秒的包。而超級節點也會做預先訂閱,比如對于超級節點A而言,如果它自身負責一三五七九秒的包,那么直接向Edge server訂閱;相反則需要向負責對應分片 的超級節點做訂閱。
有了這樣的訂閱關系,Edge server和超級節點就會根據訂閱的記憶信息向下推送。而這個推送路徑一般最多達到兩層,因為三層的拓撲太長,會引來延遲,同時路徑過長,一旦某一個節點退出,受影響的節點太多。
三階段——拉
我們前面提到整個協議是建立在UDP之上的,而在推送過程中UDP有可能會出現丟包,這時我們就需要向鄰居拉取,那么如何拉取?首先我們通過gossip協議確定拉取的目標:也就是在單位時間內定時發送本地緩沖區有哪些包,把這些包通過gossip協議交換出去,鄰居節點之間就會知道其他節點包的信息,假如出現丟包的情況,就可以通過gossip信息找到需要去拉取的節點,而拉取節點的選擇是通過前面提到的親和度分值盡量選取近的、通訊友好的節點,而這個拉取過程有可能會是多次的,如果出現拉取失敗,則會在一個RTT周期 之后向其他節點拉取,它是一個循環的隨機過程。
三階段——補償
通過上述的手段我們就可以盡力把包拉取到本地,也可以減少對Edge server的依賴,但我們無法排除這樣一種可能性,就是我的周圍鄰居都沒有這個包,那么此時我們就只能向Edge server拉取,我們在前面有提到錨點的概念,Edge server應該具有大部分或者全部的流媒體信息。
向Edge sever發起補償的條件一般有兩種:第一種是在整個P2P網絡中稀缺的包;第二種是迫切需要播放的包。但這里面還存在一個風險,也就是頻繁的發起補償請求其實對Edge server是非常大的沖擊,尤其是大量存在稀缺塊的時候 ,因此我們需要限制Edge server單位時間內的補償請求,但由于有了這種限制可能會導致某些節點補償失敗,這時它就會通過前面拉取的過程,通過gossip協議做查找拉取 ,而不是所有的都向Edge server拉取,這也是為了防止Edge server瞬間被補償請求打死掉。
三階段播放buffer
當所有數據全部到了本地,本地會有一個播放緩沖區,包 括WebRTC都會有JitterBuffer這樣的播放緩沖,我們也設計了一個Buffer,它與前面講到的三階段一一對應。
第一個是推區間,也就是負責推流區域的緩沖,相當于一個Push的JitterBuffer,這里設置的400ms緩沖主要是為了防止過度拉取,因為推送的流是不規則的,UDP會出現抖動、丟包、延遲,同時P2P多路徑傳輸之間本身的問題會引來抖動,這個buffer就是為了防止太快進行拉取用的 。第二個是拉區間,在這個區間我們一般會向鄰居拉取三次,這之間的緩沖也就是與鄰居之間的3個RTT來回。如果三次沒拉到,就會進入補償區,補償區就會向服務器拉取4次,如果沒有拉取到數據則會返回拉區間。
經過這樣的循環過程最終包被播放,對于已經播放的包是否要刪除呢?我們知道P2P是對等系統,可能別人缺少你已經播放的包,向你拉取,但你播放結束后就直接刪除了,這就導致沒有命中,這種現象肯定不是我們希望看到的,因此我們將已經播放的分片放到緩沖區中保留3秒,盡量使得拉取請求命中。以上就是整個分發過程。?
最終效果
下圖是Edge server的對比數據,我們在Edge server上做了一個開關——將它等分為開啟P2P分發和不開啟P2P分發,對同一個Edge sever以一天為單位收集數據:每5分鐘采集一次當時每秒出去的帶寬,圖中紅色的線表示沒有開啟P2P,也就是完全使用Edge server來中轉,藍色的線表示開啟P2P。圖中我們可以看到在高峰期,開啟P2P從Edge server輸出的流量大概不到100mb/s ,而未開啟P2P的則是達到了將近480mb/s ,也就是在 Edge server上可以節省到原來的1/4。
經過算法的升級,我們最終實現了這樣的效果:邊緣節點帶寬大致降到24G;BGP由于采用了多鏈路保障和直鏈的方式,大概降到原來的1/3-1/4的比例;因為帶寬的降低,對物理服務器的依賴也就自然減少,目前服務器降到41臺左右。這里值得一提的是,通過測試我們所有的端延遲大概在1秒左右,最大延遲在2秒左右,連麥延遲在200~800毫秒之間 ,與原先的情況相比,在保留固有功能和連麥系統等服務沒受到影響的情況下,我們節省了大概1/3的帶寬,基本達到了我們最初的要求,同時我們也還在不斷優化這個網絡模型,也歡迎感興趣的小伙伴與我探討。
以上是本次的分享,感謝大家。
WebRTCon 2018 8折報名
WebRTC天然不具備服務端能力,如何實現高性能、穩定的服務端能力成為繞不過的話題。我們特別開設了“WebRTC服務端開發”專題,并邀請本文分享者袁榮喜擔任出品人,一同與網易云、蘇寧文創、觸寶電話等技術大咖分享接入網關服務、協議適配、服務穩定性和行業應用。
如果你擁有音視頻領域獨當一面的能力,歡迎申請成為講師,分享你的實踐和洞察,請聯系 speaker@livevideostack.com。更多詳情掃描下圖二維碼或點擊閱讀原文。
超強干貨來襲 云風專訪:近40年碼齡,通宵達旦的技術人生
總結
以上是生活随笔為你收集整理的P2P技术如何拯救一家直播网站的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 假期不能错过的音视频领域技术进展
- 下一篇: 深度学习为图片压缩算法赋能:节省55%带