1. 程式人生 > >技術實踐 | 聊聊網易雲信的信令網路庫實踐

技術實踐 | 聊聊網易雲信的信令網路庫實踐

導讀:信令作為實時音視訊技術架構中的重要一環,是對建立實時音視訊通訊起到關鍵橋樑性的作用。本文將從信令的概念著手,分享在網易雲信新一代音視訊技術架構下,信令的基本互動流程設計以及信令網路庫的模組設計和重連優化等。 ## 什麼是信令 我們都知道,WebRTC 是通過 RTCPeerConnection 來做到端與端之間的實時音視訊通訊的,那他們是怎麼知道對方網路位置(網路資料)?支援何種編解碼器(媒體元資料)?何時開啟或關閉通訊(會話控制)的呢? 這就需要建立一條通道來交換這些資訊,而這協商的資訊就是**信令**,這條通道也就是**信令通道。**我們這裡所說的信令網路庫的作用就是為端和信令伺服器交換信令提供網路傳輸的通道。那信令的作用到底是什麼? 在實時通訊中,信令的主要作用體現在以下四個方面: - 媒體功能的協商和設定。 - 標識和驗證會話參與者的身份(交換 SDP 物件中的資訊:媒體型別、編解碼器、頻寬等元資料)。 - 控制媒體會話、指示進度、更改會話和終止會話。 - 當會話雙方同時嘗試建立或更改會話時,實施雙佔用分解。 總之,信令就是協調音視訊實時通訊的過程,一旦信令服務建立好了,兩個客戶端之間建立了連線,理論上它們就可以進行點對點通訊了。而值得注意的是,**WebRTC 標準本身沒有規定信令交換的通訊方式,信令服務根據自身的情況實現。**這也為我們自定義信令伺服器提供了可能。 ## 單 PeerConnection 方案 2020年,在疫情的衝擊下,音視訊通訊市場得到了爆發式的增長,在視訊會議、線上教育、線上金融、雲遊戲等各個領域都得到了長足的發展。同時,客戶也對各種應用場景下的音視訊的高效能、低延時等方面提出了更高的要求。 在2020年11月,網易雲信釋出了**新一代音視訊技術架構**(簡稱 NERTC),詳細內容可檢視文末介紹,為了提升產品效能,我們從高可用、高併發、高效能、高擴充套件等方向進行了全流程的技術升級,包括新一代音視訊融合通訊服務端系統、新一代音視訊 SDK 以及新一代音視訊引擎,其中就包括單 PeerConnection 的重構方案。 單 PeerConnection 方案涉及了信令伺服器的重構。其基本設計原則是主要是: - iOS/Android/windows/Mac/Web 多端協議一致,均採用 **Websocket **互動,節省伺服器資源,簡化伺服器程式碼邏輯。 - 端側只建立 2 個 Peerconnection,一個只負責傳送(sendonly),一個只負責接收(recvonly)。 - 採用自定義信令協議進行互動。 - 簡化協商流程,減少首屏時間。 單 PeerConnection 方案基本的信令互動流程如下: ![](https://cdn.nlark.com/yuque/0/2021/png/226702/1614842431607-59c57fd0-c234-4e4f-af38-8a8f1d32acd5.png) ## WebSocket 通訊簡介 前文可知,為了達到多端(Web)信令協議互動一致性,所以採用了 WebSocket 作為信令傳輸。那 WebSocket 有什麼特點? 說到 WebSocket,就離不開 HTTP。HTTP 一般限制每次連線只處理一個請求,伺服器處理完客戶的請求,並收到客戶的應答後,即斷開連線,不過 HTTP 也有 Keep-Alive 功能保持連線,使得單次連線內可以傳送多次請求。 WebSocket 是基於 HTTP 協議的,而區別於 HTTP 的最大特點在於伺服器可以主動向客戶端推送資訊,客戶端也可以主動向伺服器傳送資訊,是真正的雙向平等對話;而 HTTP 只能從端側發起請求。從下圖可以清晰看出 HTTP 與 WebSocket 之前的區別。 ![2.jpg](https://cdn.nlark.com/yuque/0/2021/jpeg/226702/1614842619179-7ad97681-1319-4448-9d2f-1465b878aa71.jpeg) ### 握手(Handshake) WebSocket 是建立在 TCP 長連線基礎之上的,為了實現 WebSocket 的通訊,借用 HTTP 協議完成了一次握手過程。具體的握手過程是這樣的: 1. 客戶端發起握手請求 ``` GET /chat HTTP/1.1 Host: server.example.com Upgrade: websocket Connection: Upgrade Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ== Origin: http://example.com Sec-WebSocket-Protocol: chat, superchat Sec-WebSocket-Version: 13 ``` 2. 伺服器返回應答 ``` HTTP/1.1 101 Switching Protocols Upgrade: websocket Connection: Upgrade Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo= Sec-WebSocket-Protocol: chat ``` 這樣表示握手成功。握手成功之後,客戶端和伺服器就建立了雙向的資料通道,可以互發訊息(資料),不需要客戶端每次都發起請求。 ### 連線保持 常規做法大家都知道,那就是心跳機制,通過判斷心跳是否有響應來判斷連結是否可用。WebSocket 也不例外,其協議規定了 ping/pong 機制來保持連線。我們可以看下 ping/pong 在 [RFC6455](https://datatracker.ietf.org/doc/rfc6455/) 具體是怎麼規定的。 > 5.5.2. Ping The Ping frame contains an opcode of 0x9. A Ping frame MAY include "Application data". Upon receipt of a Ping frame, an endpoint MUST send a Pong frame in response, unless it already received a Close frame. It SHOULD respond with Pong frame as soon as is practical. Pong frames are discussed in Section 5.5.3. An endpoint MAY send a Ping frame any time after the connection is established and before the connection is closed. NOTE: A Ping frame may serve either as a keepalive or as a means to verify that the remote endpoint is still responsive. > 5.5.3. Pong The Pong frame contains an opcode of 0xA. Section 5.5.2 details requirements that apply to both Ping and Pong frames. A Pong frame sent in response to a Ping frame must have identical "Application data" as found in the message body of the Ping frame being replied to. If an endpoint receives a Ping frame and has not yet sent Pong frame(s) in response to previous Ping frame(s), the endpoint MAY elect to send a Pong frame for only the most recently processed Ping frame. A Pong frame MAY be sent unsolicited. This serves as a unidirectional heartbeat. A response to an unsolicited Pong frame is not expected. 由上可知,一端通過傳送 ping 幀,對端收到 ping 幀後,響應一個 pong 幀,這樣就完成了一次連線的檢測,並且它是單向的。 ### 關閉連線 當關閉 WebSocket 通訊的時候,發起端需要傳送一個關閉幀,對方收到關閉幀,如果不曾傳送過關閉幀,則需要傳送一個關閉幀作為響應。關閉幀可以攜帶應用資料,用來說明關閉的原因。最後等到底層 TCP 連線關閉後,整個 WebSocket 通訊就完全關閉了。 ## 在 NERTC 中的應用 網易雲信在新一代音視訊技術架構中,是如何使用這些技術來實現信令的傳輸通道呢?下面我們先從模組設計方面和重連方面的設計做一些介紹。 一個基本原則:**應用層不需要關心訊息丟失、亂序、重發等問題**。眾所周知,TCP 提供可靠的傳輸服務,通過 TCP 連線傳送的資料無差錯、不丟失、不重複且按序到達。TCP 通過校驗、重傳控制、序號標識、滑動視窗、確認應答來實現可靠傳輸,如丟包時的重發控制,還可以對次序亂掉的分包進行順序控制。這樣,我們只需要解決的就是重連和重發問題。 ### 網路庫架構設計 下圖是 NERTC 的網路庫架構圖設計: ![3.jpg](https://cdn.nlark.com/yuque/0/2021/jpeg/226702/1614843032124-ad130b52-9f98-4e21-95e2-63d91bc7ae8a.jpeg) 我們簡單介紹其中的幾個模組: - API 部分定義的是網路庫模組的對外介面,由應用層呼叫。 - Transport 模組主要抽象了訊息收發和連線管理的功能,ws/wss 以及未來的 quic 協議擴充套件等都基於其虛介面實現。 - Message 模組管理 WebSocket 子協議的組包和解包。 - SendBuffer 管理 Message 的快取和重發。 - Peer 作為核心模組,實現對 Transport、Message 等所有模組的控制。 接下去我們看下快連線快重試和重連設計優化。 ### 快速連線和重連設計 下面我們按不同的場景來介紹下網路庫是如何運作以及做了哪些工作,部分場景可以結合下圖來理解。 - 場景一:首次連線時的退避策略。當應用層(簡稱 SDK)首次嘗試連線時,如果 WebSocket 連線成功,則返回 SDK,SDK 發起信令協商;如果連線失敗,使用每隔 200/400/600ms 的快速重試多次連線的策略。如果最終都失敗的話,則回撥給應用層 onFail 事件。 - 場景二:連線成功後的斷開重連策略。 如果已經連線成功,因為網路原因或者伺服器等原因導致連線斷開,則觸發此策略。觸發方式分為兩種,一種方式是,WebSocket 通知了 onClose 事件;另外一種是主動探測的方式,通過 ping/pong 機制來觸發重連。後者通過傳送 ping 幀三次,每次間隔 3s,超時1.5s,如果三次均無法得到 pong 幀,則認為網路或者伺服器異常,觸發此場景的重連策略。具體的策略是,每隔 2/4/6s 觸發一次連線嘗試,如果最終還是失敗的話,回撥給應用層 onDisconnected 事件。 - 場景三:應用層收到 onDisconnected 事件,會切換新的 server 地址,重新通過網路庫嘗試連線,直到 server 用盡,退出當次通話。 - 場景四:當網路切換或者恢復可用時,應用層會主動觸發重連。 - 場景五:伺服器主動關閉連線時,網路庫清空訊息快取,並且不再嘗試重連。 重連場景如下圖所示: ![4.jpg](https://cdn.nlark.com/yuque/0/2021/jpeg/226702/1614843050070-ee0753c3-8045-4e52-9426-c0643a5272ba.jpeg) ### 訊息快取和重發 其實上面的時序圖已經包含了訊息傳送和重發的時機。訊息分為兩種: - Request 型別訊息,需要等待伺服器的 Response,需要快取。 - Notification 型別訊息,不進入訊息快取,當然也有可能是伺服器傳送過來的通知。 我們這裡只講端側的訊息傳送。訊息的傳送和重發都只在連線成功後觸發。 每一條訊息都會先進入傳送快取佇列(即 SendBuffer),如果當前連線正常,則直接傳送,如果連線斷開則會暫停傳送,等待連線或重連成功之後才會恢復傳送。因為依賴於 TCP 的可靠傳輸,所以訊息的重發均只發生在重連成功之後。當連線被伺服器 kick off 或者應用層主動關閉時,peer 物件會清空所有快取訊息,不再做傳送或重發的嘗試。 ### 弱網下的表現 在弱網下,我們的測試資料發現,最多能抗50%的丟包率。在實測場景中,高丟包率情況下,媒體流 的 QoS 激進策略下會發送大量的冗餘包和重傳包,從而導致頻寬資源幾近耗盡。這會導致什麼呢? WebSocket 連線可能會失敗,訊息可能無法收發。我們都知道 TCP 是通過滑動視窗和擁塞視窗來做的流控。當已發位元組數等於對方的接收視窗位元組數時,就會導致傳送視窗滿(TCP Window FULL)了,從而無法再發送資料。有興趣的同學可以瞭解 TCP 相關的概念。這種情況下基本只能通過觸發斷開重連的機制,關閉老的連線,新建連線進行訊息的重發。 ## 未來規劃 為了增強弱網情況下的抗性,我們會考慮 UDP 作為優化的方向,比如說 QUIC(Quick UDP Internet Connection)協議等,目前也基本已經接近產品化階段,這裡就不再贅述,敬請期待 QUIC 版本的表現。 ## 總結 本文從信令的基本概念以及作用入手,簡要地講述了網易雲信新一代音視訊技術架構中信令網路庫基本互動流程、模組化設計思路以及網路庫基本運作方式。另外,也對連線重試和重連的設計和優化做了一些論述。同時,也非常歡迎跟我們交流更多關於信令網路庫的實現。 ### 作者簡介 丁永鋒,網易雲信資深客戶端開發工程師,一直致力於客戶端跨平臺開發,目前負責音視訊客戶端跨平臺 SDK 開發,曾負責 Unity&Cocos2dx SDK 以及網易雲信 IM SDK 的跨平臺開發工作。 ### 延伸閱讀 - [網易雲信流媒體首席架構師:新一代音視訊技術架構如何構建?](https://mp.weixin.qq.com/s/M9hVjcaT9KwCOy3HQkfeQQ) - [聊聊WebRTC閘道器伺服器2:如何選擇PeerConnection方案?](http://yunxin.163.com/blog/we