技術系列課回顧 | 網易雲信線上萬人連麥技術大揭秘
本文根據網易雲信資深音視頻服務端開發工程師陳策在《MCtalk Live#5:網易雲信線上萬人連麥技術大揭秘》線上直播分享整理。
導讀
大家好,我是來自網易雲信的陳策。連麥作為一個強互動場景,單房間內的高並發一直是個比較復雜的問題。這次給大家分享網易雲信在萬人連麥這個場景上的探索和實踐。
比較典型的萬人連麥需求場景有視頻會議研討會、低延遲直播、在線教育大班課、類 Club House(多人語聊房)等。對於萬人連麥的需求場景市面上普遍的解決方案是“RTC+CDN”的方式,即限制上麥主播人數在小規模(50人左右)進行 RTC 互動,然后轉推到 CDN,大規模的觀眾再通過 CDN 直播拉流的方式實現。這種解決方案在業務上限制了上麥人數,並且觀眾和主播之間有較大的聲畫延遲,無法滿足雲信的業務要求。例如,在游戲語音場景中,會有大量的用戶開公麥,需要做到所有人都可以上麥,並且上麥人數不限制。那么,網易雲信是如何解決這個問題的呢,本文將和大家分享我們在這個問題上的探索。
信令的技術難點
信令並發、弱網和高可用問題
我們在這個問題的探討分為幾個方面:信令、音頻技術、視頻技術以及服務器間的網絡傳輸,先來看看信令的實現。
RTC 是使用長連接進行雙向通知的。假設某個語聊房主播有一萬人,那么只要其中某一個主播上/下麥或者加入/離開房間,都會觸發對其它 9999 人的信令通知。這種隨機的用戶行為會讓服務器瞬時壓力非常大。
傳統的中心化單點服務器顯然是支撐不了萬人房間這么大並發的,必須使用分布式架構來分散壓力。假如一個萬人房間分布在多台服務器上,那么服務器之間還要實時同步 Nx(N-1) 的用戶狀態和發布訂閱關系,同時為了實現高可用,還需要支持某台服務器崩潰重啟后的數據同步。
另一方面,媒體服務器一般會做成分布式網狀架構來降低點對點的延遲。但如果信令服務器也保持每一個節點都平等的網狀架構,那么每一個節點都要維護全量的級聯關系。這其中錯綜復雜的消息同步性問題將會極難維護。
網易雲信分布式樹狀架構
為了實現高並發和高可用,結合信令都是在房間內部傳遞,房間之間不會有信令交互這一業務特點,我們將信令服務器設計成分布式樹狀架構,每個房間是一顆獨立的樹,如下圖:
- 根節點:房間管理服務器,管理和存儲所有的用戶狀態和訂閱關系。
- 子節點:作為邊緣服務器,負責用戶就近接入。
這種樹狀架構可以有效分散各節點的廣播壓力。根節點會盡量根據就近用戶的原則進行分配,避免其與子節點之間鏈路過長,同時子節點盡量只充當消息 proxy,不參與業務,把業務集中到根節點上,從而避免信令的同步亂序等問題。
根節點使用緩存加數據庫來保證業務數據存儲的性能和可靠性。子節點由於不涉及用戶業務狀態,在崩潰重啟后,只需要客戶端信令的長連接重連,不用進行類似重新加入房間的操作,做到對用戶無感知。在遇到子節點宕機的情況下,則會依靠客戶端的超時機制,重新請求調度。通過這一系列手段,實現服務的高可用。
信令弱網問題
在RTC架構中,由於信令非常多且交互復雜,一般采用信令和媒體通道分離的策略。但這會引入一個問題,就是兩者的抗弱網能力不一致。媒體一般使用Udp傳輸,很容易做到在30%丟包下依然能流暢觀看。但信令一般使用Tcp傳輸,30%丟包下信令就基本不可用了。
為了解決這個問題,我們使用Quic作為信令加速通道,來保證信令媒體的弱網對抗能力一致性。同時在Quic連不通時(可能存在用戶網絡對某些Udp端口不友好),也能退避到Websocket,以此來保證信令在弱網下的高可用。
音頻的技術難點
混音和選路的缺陷
在多人連麥場景中,最復雜的就是多個用戶同時說話場景下的音頻並發問題。
假設萬人房間中每個主播都上麥,由於語音的特性,每個主播理論上都要訂閱其它所有人(視頻可以按需訂閱,但音頻理論上應該全訂閱)。如果每個客戶端都訂閱 N-1(9999) 路流,不僅僅是流量的浪費,客戶端也支持不了如此多的下行,而且在現實場景中,只要超過 3 個人同時說話,其他人基本上就聽不清楚內容了。
解決這個問題的方法一般有音頻選路和服務端混音兩種。但在萬人房間的場景下這兩種解決方案都有所缺陷:
- 音頻選路是在 N 路音頻中選擇音量最大的幾路進行下發(一般 2~3 路)。這個方案確實能夠解決上述問題,但它有一個前置條件:與客戶端直連的邊緣服務器上必須匯集全量的音頻流,這樣才能選路。所以,即使是 48 核的服務器,也難以支持萬路並發。
- 服務端混音是解碼 N 路混成1路,或者選路之后再解碼3路混成1路。前者的問題主要是MCU 服務器很難頂住這么大的轉碼壓力,而且耗時過長。后者還是會有上述音頻選路的缺陷。其實MCU最大的缺點是單點問題,崩潰后會影響全量的用戶,很容易成為系統瓶頸。
網易雲信的分布式選路
為了解決音頻的上述問題,我們采用了在服務器級聯之前預選路的方案。假設一個萬人房間平均分布在 20 台邊緣服務器上,每台服務器有 500 路上行流,如果不使用級聯預選路,那么每台服務器互相級聯后,都需要拉全量的 10000 路流才能供下行選路使用。
當我們使用級聯預選路方案后(默認 3路),每台服務器只需要拉 3x(20-1) 路流就可以,之后再從本機的 500 路流加上級聯的 3x(20-1) 路流中進行二次選路,選出最終音量最大的3路流下發給客戶端。通過這種方式,實現音頻的全訂閱。假設在 M 台服務器上傳輸 N 路音頻流,服務器傳輸數據量的數量級就從 N^2 下降到 M^2。
視頻的技術難點
Simulcast/Svc 和碼率壓制在大房間的局限
由於客戶端性能限制,一般能同時解碼渲染的視頻流路數不會太多,可以通過“被訂閱才發流”這種方式來規避上述媒體並發問題。
萬人房間的視頻技術難點主要在於 QoS,RTC 中服務端的 QoS 手段主要有兩個:
- 利用 Simulcast/Svc 切流
- 通過 RTCP 壓制發送端編碼碼率
Simulcast 和 Svc 的本質是將用戶的下行帶寬分層在分發對應的流,但在萬人房間中,用戶帶寬往往分布很散,機械的分層並不能讓大多數用戶都有最好的體驗。RTCP 碼率壓制是根據接收端帶寬反饋給發送端的編碼器,編出最適合的碼率,其最適用的場景是 1v1,在萬人房間中會很難決策。
網易雲信 QoS 策略
為了讓盡可能多的用戶都獲得匹配其網絡的視頻流,我們制定了如下的 QoS 策略:首先我們會根據下行帶寬將用戶從高到低分為4個層級,發送端同時使用 Simulcast+Svc 編碼,例如720p/30fps,720p/15fps,720p/8fps,180p/30fps,服務器根據用戶層級為其分配對應的數據流。
這種方法的優點是能夠實現每一個用戶都能匹配對應其帶寬的視頻流,但有一個顯而易見的缺點,就是分配不夠平均。比如一個萬人房間中,大部分用戶的帶寬都命中 720p/15fps 這一層,其它少量用戶分散在另外三層上,那么實際上這個房間大多數用戶的視頻體驗都不是最佳。
為了解決這個問題,還需要在分層編碼的基礎上再結合碼率壓制:首先將用戶的帶寬按照從高到低排序,取 topN% 用戶的最低帶寬反饋給發送端,指導最高層級 (720p/30fps) 的編碼碼率,以此讓 topN% 的用戶都能命中體驗最好的數據流。N 可以用戶設置,也可以根據房間內用戶的下行帶寬情況動態變化。
下圖以 Top60% 舉例:
還有一種場景是當用戶上行帶寬不足時,假設只有 1.2M,根本無法實現 Simulcast+Svc。這種情況下,我們會讓客戶端只編碼一路 720p 的單流,然后在房間中引入一個 MCU,將單流轉發給它,MCU 在轉碼時再使用 Simulcast+Svc,並回推給 SFU,以此來匹配我們的下行 QoS 策略。
服務器之間網絡傳輸的技術難點
跨運營商和跨國傳輸
實際開發的過程中,我們還遇到了一些服務器之間網絡傳輸問題,這里也和大家分享一下。
比如為了減少最后一公里距離,我們會使用一些單線機房作為邊緣節點,不同運營商的單線機房如果直接級聯,他們的網絡傳輸很明顯是不可控的。想要從架構層面解決這個問題,就必須在級聯網絡中再引入一個三線/BGP 機房作為中轉,而這又要考慮中轉服務器的節點位置分配和單點崩潰問題,這樣無疑大大增加了調度的復雜性。
另一種情況是跨國場景下的機器級聯,服務器之間的公網路由不一定是最優,抖動也可能非常大,中間一公里的網絡完全不可控。
WE-CAN
為了解決類似問題,我們將服務器之間的傳輸模塊抽象出來,引入了自己的公網基建,大規模分布式實時傳輸網:WE-CAN(Communications Acceleration Network)。在全球主要地區都部署節點,節點之間不斷探測網絡質量並上報,中心決策模塊收到上報后綜合運營商,實時網絡質量,帶寬成本等信息后計算任意兩個節點之間的最短路徑並生成路由表,再下發給各個節點作為下一跳的路由參考。WE-CAN 跟業務無關,完全是傳輸層解決方案。通過這種方式,媒體級聯只需要在包頭打上目標地址,再投遞給 WE-CAN,完全不需要考慮業務之外的傳輸問題。
總結
以上技術方案就是本次分享的全部內容,通過雲信的萬人連麥技術,將服務升級為無狀態,不需要限制房間最大人數和同時上麥人數,並且支持水平彈性擴容,輕松應對突發流量,秒級匹配用戶網絡。
當然任何系統的搭建都不是一蹴而就的,其中每一個點,我們都是踩坑無數。網易雲信也將繼續打磨音視頻技術,給行業帶來更好的服務。
作者介紹
陳策,網易雲信資深音視頻服務端開發工程師,負責雲信全球 RTC 網絡的搭建和擔任核心開發,在媒體數據傳輸和 RTC 全棧架構設計方面有豐富的經驗。