构建基于浏览器的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即可。

image.png

整体的H5 P2P都是基于RTCPeerConnection和RTCDataChannel来实现的,只是实现数据层面的P2P,不包含音视频通信的内容。

三、总体架构

image.png

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穿透。

四、阶段流程

下图简略的展示了整体方案的各个阶段、流程:

image.png

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)。基于offer/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个数据片)的过程。

image.png

4. 内存控制

对于Web P2P来说,所有的媒体数据都缓存在内存中,但是,也并不是会缓存完整的数据。对于直播来说,所有用户的观看点都是非常接近的,因此,内存始终缓存播放点前后的固定大小的数据。因为只有这部分数据分享利用率才是最高的。

image.png

如上图所示,有颜色的数据块都是当前在内存缓存中的数据,其中红色数据块表示当前的播放点,黄色数据块表示过去已经播放过的内容,绿色数据块表示未来还未播放的内容,可以看到当播放点从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,在不影响用户使用体验的前提下,不仅可以有效降低服务器的负载压力,并且能够降低可观比例的带宽成本。

image.png

九、思考总结

目前,该技术已经大规模应用在各网络直播中,良好的解决了服务质量与带宽成本的平衡。但是,我们也发现在超大规模的直播下,存在了一些提升的空间,主要如下:

(1) coturn与MQTT的服务性能不高,会制约直播的服务质量。

(2) 接入边缘网络,打造多元化网络,缓解CDN负载,并且可以进一步降低CDN的带宽成本。

相关实践学习
Serverless极速搭建Hexo博客
本场景介绍如何使用阿里云函数计算服务命令行工具快速搭建一个Hexo博客。
相关文章
|
9天前
|
人工智能 前端开发 计算机视觉
Inpaint-Web:纯浏览器端实现的开源图像处理工具
在刷短视频时,常看到情侣在景区拍照被路人“抢镜”,男朋友用手机将路人“P”掉,既贴心又有趣。最近我发现了一个纯前端实现的开源项目——inpaint-web,可在浏览器端删除照片中的部分内容,非常酷。该项目基于 WebGPU 和 WASM 技术,支持图像修复与放大,已在 GitHub 上获得 5.1k Star。项目地址:[GitHub](https://github.com/lxfater/inpaint-web)。
49 3
 Inpaint-Web:纯浏览器端实现的开源图像处理工具
|
12天前
|
SQL 安全 前端开发
PHP与现代Web开发:构建高效的网络应用
【10月更文挑战第37天】在数字化时代,PHP作为一门强大的服务器端脚本语言,持续影响着Web开发的面貌。本文将深入探讨PHP在现代Web开发中的角色,包括其核心优势、面临的挑战以及如何利用PHP构建高效、安全的网络应用。通过具体代码示例和最佳实践的分享,旨在为开发者提供实用指南,帮助他们在不断变化的技术环境中保持竞争力。
|
14天前
|
PHP 开发者
深入浅出PHP:构建你的第一个Web应用
【10月更文挑战第35天】在数字时代的浪潮中,掌握编程技能已成为通往未来的钥匙。本文将带你从零开始,一步步走进PHP的世界,解锁创建动态网页的魔法。通过浅显易懂的语言和实际代码示例,我们将共同打造一个简单但功能强大的Web应用。无论你是编程新手还是希望扩展技能的老手,这篇文章都将是你的理想选择。让我们一起探索PHP的魅力,开启你的编程之旅!
|
16天前
|
缓存 前端开发 JavaScript
构建高性能与用户体验并重的现代Web应用
构建高性能与用户体验并重的现代Web应用
32 5
|
14天前
|
开发框架 前端开发 JavaScript
利用Python和Flask构建轻量级Web应用的实战指南
利用Python和Flask构建轻量级Web应用的实战指南
44 2
|
18天前
|
监控 前端开发 JavaScript
探索微前端架构:构建可扩展的现代Web应用
【10月更文挑战第29天】本文探讨了微前端架构的核心概念、优势及实施策略,通过将大型前端应用拆分为多个独立的微应用,提高开发效率、增强可维护性,并支持灵活的技术选型。实际案例包括Spotify和Zalando的成功应用。
|
16天前
|
前端开发 JavaScript jenkins
构建高效、可维护的Web应用
构建高效、可维护的Web应用
36 2
|
17天前
|
机器学习/深度学习 自然语言处理 前端开发
前端神经网络入门:Brain.js - 详细介绍和对比不同的实现 - CNN、RNN、DNN、FFNN -无需准备环境打开浏览器即可测试运行-支持WebGPU加速
本文介绍了如何使用 JavaScript 神经网络库 **Brain.js** 实现不同类型的神经网络,包括前馈神经网络(FFNN)、深度神经网络(DNN)和循环神经网络(RNN)。通过简单的示例和代码,帮助前端开发者快速入门并理解神经网络的基本概念。文章还对比了各类神经网络的特点和适用场景,并简要介绍了卷积神经网络(CNN)的替代方案。
|
20天前
|
存储 缓存 网络协议
计算机网络常见面试题(二):浏览器中输入URL返回页面过程、HTTP协议特点,GET、POST的区别,Cookie与Session
计算机网络常见面试题(二):浏览器中输入URL返回页面过程、HTTP协议特点、状态码、报文格式,GET、POST的区别,DNS的解析过程、数字证书、Cookie与Session,对称加密和非对称加密
|
22天前
|
前端开发 JavaScript API
前端框架新探索:Svelte在构建高性能Web应用中的优势
【10月更文挑战第26天】近年来,前端技术飞速发展,Svelte凭借独特的编译时优化和简洁的API设计,成为构建高性能Web应用的优选。本文介绍Svelte的特点和优势,包括编译而非虚拟DOM、组件化开发、状态管理及响应式更新机制,并通过示例代码展示其使用方法。
36 2
下一篇
无影云桌面