3300万激增高并发用户:作业帮直播课如何做到低延迟?

近年来在线教育业务蓬勃发展,业务模式和教学场景也在不断的推陈出新,新的模式和场景对直播技术提出了新的挑战,特别是沉浸式课堂教学对直播的实时性和互动性要求越来越高,传统的基于 rtmp 的直播技术已无法满足这一诉求。

在这种大趋势和自身业务发展需要的驱动下,国内在线教育企业作业帮自研出一套能适应高并发低延迟的互动直播系统,并已投入到生产环境稳定运行。疫情期间,仅2月3日至3月9日,作业帮直播课就成功完成了对3300万学员的完美承接,在直播技术领域备受认可。在直播系统背后,是作业帮海量的用户和数据积累的支撑。

本次分享立足在线教育的直播场景,着重介绍高并发低延迟直播架构的设计思路、技术要点和实践经验,并精选 3 个关键要点进行深入剖析,以期达到听众既能了解整个技术架构的全貌,又能触及到一些关键核心技术要点的效果。

大家下午好,今天我们聊的话题是——在线教育场景下的高并发低延迟直播技术。由于这个话题涉及到的技术面比较广,今天会挑一些重点,来谈谈作业帮在这块的实践经验,希望对大家能有所启发或者帮助。(回放视频戳这里

首先,简单自我介绍下,我毕业后加入百度,在百度知道从事相关的业务、基础架构的研发。目前在作业帮,负责作业帮直播技术团队,主要是两块,一块是长连接 & 实时消息分发相关的技术研发;一块是大直播、实时音视频相关的技术研发。

在线教育的业务场景

在谈技术话题之前,我们不妨一起来看看,在线教育的主要业务场景有哪些?我这里大概做了一些分类,大体上覆盖了几家头部在线教育公司的主要业务场景。详细分类如下:

  • 1v1 辅导业务,比如答疑类、1v1 英语辅导等,主要特点是要能够实现 1v1 的双向互动
  • 大班课,比较主流的一个业务场景,一个主讲老师,很多学生,主要特点是单向直播,允许一定的延迟,为了增强互动性,会有一个 1v1 连麦的功能。
  • 互动大班课,开始流行的 1 个业务场景,也是一个主讲老师,很多学生,它的特点是要求低延迟,同时老师能够看到所有学生的实时画面,已经初步有了线下课堂的氛围了,至少老师能够 touch 到学生的课堂反应和表现。
  • 超级小班课,在互动大班的基础上,对全体学生进行分组,每个小组之间可以实时互动,类似视频会议。
  • 小班课,真正意义上的小班课,1 个老师和少量学生,彼此之间可以音视频实时互动,视频会议模式。

技术抽象

对上述业务场景进行抽象,实际上可包含 3 大类技术:

  • 1v1 实时音视频技术(虽然 1v 多或者 6v6 包含 1v1, 但因其较为特殊,可以有特殊的实现方式,保证通信质量)
  • 1v 多直播技术,包括高延迟和低延迟两种
  • 6v6 多人实时音视频技术(这里的 6 是泛指)

上述业务场景的实现无非就是这些技术的组合,对于研发人员而言,需要做的就是根据实际的业务场景,选择合适的技术模型。

今天我们重点要谈的是——高并发低延迟直播技术,这里涉及到两个关键要点,一个是高并发;一个是低延迟。为了方便讨论,我们可以假定一个业务场景如下:

假定当前有 100 位主讲老师同时进行授课,此时分布在全国各地的 500w 位学生要同时听这些老师讲课,为了保证课堂的效果,要求直播过程的延迟要低,如果让你来设计这套系统,该如何做?

这个场景虽然是假定,但是在实际业务中是确实存在的,特别是几家头部公司,用户规模本来就大,当推出一些大师课、免费公益课、试听课时,用户的报名规模很容易达到这个数量级。

核心技术要点

关于如何做到高并发和低延迟,每个人都会有自己的思路和想法,我这里也梳理了几个关键技术要点,但不是全部,供大家参考。

低延迟:

  • 协议的选取,传输层通常会选择 UDP 协议,应用层往往是一组协议栈,可以采用标准的协议,也可以自定义协议
  • 直播是一个非常长的链路,需要在每一个环节压缩耗时,比如前处理、网络传输等
  • buffer 的控制很关键,如果能根据网络情况动态控制是比较理想的,比如 WebRTC 的 neteq

高并发:

  • 单机性能尽可能高,优化到极致
  • 集群、分布式 IDC
  • 分层树状架构

特别要引起注意的是,在考虑高并发和低延迟的同时,还需要兼顾考虑总体质量和成本。

总体架构

了解了相关的技术要点之后,接下来我们一起来看看总体的系统架构该如何设计。如上图所示,这只是一个简化版的架构图,实际的系统构成远比这个要复杂得多,但这个图基本可以呈现总体的架构设计思想,也覆盖了系统的关键核心链路。为了通俗解释整个流程,我举例来介绍下。假定主讲在北京,我们需要将主讲的流推上来,第一步是通过(上图)红色的线访问调度系统,调度系统根据主讲的 IP 等信息分析出主讲的地理位置和运营商信息,内部通过运行一套策略,最终反馈给主讲一个最佳的推流 IP,这个 IP 实际上对应着机房里的一台服务器。

说到策略,需要依据业务场景进行选择,至少有几个点需要考虑:一是就近接入,当然就近可能不一定是最好的,但大概率不会出错;二是至少要做 CPU 和网络负载调度,因为机房里面的机器很多,如果某些机器的 CPU 和网络负载已经很高,就不应该再给其调度流量;三是其他复杂策略,比如加入 AI、网络测速、路由选择等功能。总之,经过一系列测算会反馈给主讲最佳 IP,之后主讲就可以把流推到这个服务器上。zrtcpush 接收到流之后,会立即将流转推到同 IDC 机房的 zrelay 中继服务上,中继服务将流的信息同时注册到(上图)红色的 S3 调度系统之中,至此,推流结束(获取最佳 IP—推流边缘节点—中继服务—调度系统信息注册—推流结束)。

那么,在广州的学生如何看到老师的直播画面呢?假设该学生第一个进入直播间,那么同样首先需要向调度系统发出请求,调度系统根据策略返回最佳 IP 后,就开始从广州机房拉流,由于该学生第一个进入,此时还没有老师的直播流,zrtcpull 就会向同 IDC 的 zrelay 发出拉流请求,然而此时的 zrelay 也没有流,它会向调度系统查询,调度系统根据之前的注册信息,告诉广州的 zrelay 应该从北京的 zrelay 拉流,广州的 zrelay 拉流成功之后,再分发给 zrtcpull,然后由它反馈给学生;第二个广州的学生进来后,如果命中了与第一个学生相同的服务器,可以直接分发;后面的其他学生进来可能会被分到其它服务器,如果没有流就再次重复第一个学生进来时的操作,但是跨机房的拉流则不需要了。

至此,核心技术流程基本比较清楚,实际上可能更为复杂,这里再做一个简单总结:

系统架构主要由两大部分构成:

  • 全局智能调度系统
    • 就近接入,选择最佳路由
    • 媒体流信息全局管理
    • CPU、网络负载控制
  • 多 IDC 分布式分发系统
    • zrelay,负责各 IDC 内部之间的 rtc 流中继分发
    • zrtcpush,就近接收用户的推流
    • zrtcpull,就近接收用户的拉流
    • 推流和拉流之所以分开,主要考虑保护源站,防止大并发的拉流影响推流

了解了总体的系统架构之后,接下来我们以协议简化、多核分发模型,上行推流优化三个技术要点为切入点,来拆解一下系统的实现细节和实践经验。

协议简化

整套系统是基于流来设计的,没有房间的概念,系统并不维护流和流之间的关系,这样系统就可以做得更加轻量级,而轻量级的系统更容易做到高并发,因为环节和流程越复杂,按照木桶理论,瓶颈点会更多。

在协议简化上,我们主要做了两点,一是通道建立协议的简化;二是中继分发协议的简化。

在建立 RTC 传输通道之前,一般需要进行 SDP 的交换,这块的交换我们没有采用长连接协议,而是通过简单的 http 协议。以用户拉流为例作为说明:

  1. 客户端需要拉流,发送 /signaling/pull 请求到信令服务,信令服务收到请求之后,转发给 zrtc 服务。
  2. zrtc 服务收到拉流请求之后,会生成一个 offer(包含详细的会话描述信息)返给信令服务
  3. 信令服务将 offer 信息作为 http response 返回给客户端
  4. 客户端收到 response 之后,会生成一个 answer,然后发送 /signaling/sendanswer 请求给信令服务,信令服务将 answer 转发给 zrtc 服务
  5. 至此,通信双方均拿到了对端的 SDP 信息,可以继续后续的 ICE 流程

对于 WebRTC 而言,为了保证安全,通道建立环节,还有一个 DTLS 过程协商秘钥,用于后续的 RTP 包加密。这块我们做成了可选的,如果应用场景对安全性要求没那么高,可以不用加密,从而提升性能。

除了边缘分发以外,中继分发也至关重要,两个 IDC 之间的媒体流传输选择何种协议值得认真考量,当然这里的选择有很多,我们选择了 KCP 协议,主要考虑了以下两点:

  • KCP 协议以牺牲带宽为代价,来换取 30%~40% 的延迟降低,且最大延迟能够降低 3 倍,这个贴合系统低延迟的诉求,同时服务端的带宽是可保障的
  • KCP 是可靠的传输协议,这样上层就不用关注两个 IDC 之间的丢包问题,简化系统设计

多核分发模型

为了能够支持高并发,首先单机的性能要高,尽可能优化到极致,这样我们就可以用极少的服务器,来获得更高的系统吞吐量,下面我们重点谈谈分发模型。

为了方便说明问题,这里先设定一个场景,比如有多个用户从服务器 A 拉取流 B。首先,我们需要将不同的用户分散到不同的 CPU 核进行处理,一个通用的办法就是按照 uid % CPU 核心数进行分散。用户分散之后,接下来我们需要考虑另外一个问题,不同的 CPU 分发相同的媒体流,这个原始的媒体流是从中继分发服务拿到的,那么我们是每个 CPU 获取一份?还是只有一个 CPU 获取,然后其他 CPU 共享这份媒体流?显然是第二种方式更优一些。通常每路媒体流都有一个流名称,可用通过将流名称转换成数字,比如 crc32,然后对 CPU 核数进行取模,选择一个 CPU,选定的 CPU 从中继分发服务获取媒体流,其它的 CPU 核从选定的 CPU 核共享媒体流。这是大体的分发模型,有几个细节大家需要重点考虑:

  • 底层的 socket 收发做到高性能,尽量优化到极致
  • 每个 CPU 核维护一组队列用于和其它 CPU 核共享数据,利用无锁队列和消息通知,来避免多线程加锁,从而提升性能
  • 采用共享指针,避免数据的反复拷贝,影响性能
  • 注意多队列网卡

基于以上分发模型,用线上真实的媒体流进行评测,性能数据如下: 同时有 76 路媒体流进行分发,每路媒体流的平均码率在 300kbps 到 400kbps 之间,一台配置为 Intel® Xeon® Silver 4110 CPU @ 2.10GHz,32 核心,128G 内存,2Gbps 外网带宽的服务器,能够稳定的支持 4500 路拉流。

上行推流优化

对于 1 对多的直播场景,上行的推流质量是非常重要的,上行推流不稳定会导致大面积的拉流体验比较差。比如上行推流链路有丢包或者乱序,如果控制不好,可能会导致拉流端大量的重传,降低性能。

这里可以引入转发控制模块,针对音频和视频,可以分别制定不同的策略。对于视频,当发现上行的媒体流存在丢包或者乱序时,暂时可以先不转发。什么时候继续转发呢?如果已经有序了,比如丢包恢复或者乱序的包过来了等,可以继续转发。另外一种情况是,丢包一直没有恢复,但是下一个 I 帧已经收到了,那么 I 帧之前的数据包可以全部丢弃,重新开始发送新的 I 帧。这里面涉及到帧完整性判断技术,具体可以参考 WebRTC 源码。

对于音频,有丢包或者乱序,暂时不转发,直到有序或者最多等待 min(10*rtt, 300ms)后开始转发。这里可能会引入一定的延迟,可以根据实际情况灵活设置。这里涉及到接收端的 RTT 探测技术,具体可以参考 WebRTC 源码。

下图是基于上述架构的一组 IDC 实际效果测试,连续测试 24 小时,4 个 IDC 未断流,延迟没增大,音视频完全同步。

总结

最后,再做一个总结,对于研发而言,需要根据不同的业务场景,选择合适的架构模型。

关于高并发:

  • 如无必要,架构尽可能设计的轻量级,越重的设计瓶颈点越多
    • 轻量级的协议和流程
  • 单机尽可能高性能
    • 底层 socket 收发优化到极致
    • 多核分发模型
    • 利用无锁队列 + 消息通知避免多线程加锁
    • 利用共享指针或者其它技术,降低频繁的内存拷贝
    • 注意多队列网卡
  • 低延迟
    • UDP 协议
    • IDC 内部中继分发可考虑 KCP,降低延迟
    • 全链路优化,buffer 合理控制,可参考 WebRTC neteq 动态自适应抖动缓冲,降低延迟

优秀的系统,绝非一日之功,需要我们保持耐心,以工匠精神,持续的打磨和精进,才能趋于至善!

问答环节:

1. 弱网优化可以多分享分享吗?

弱网优化一般的思路还是动态码率自适应、nack/fec、自适应动态缓冲区(neteq)等等,具体可以多参考 WebRTC 的源码。

2. 想问问老师直播里面的带宽,存储,转码成本都比较高,这块有好的控制方法吗?

带宽可以从降低码率的角度考虑,比如教育场景,PPT 和头像是可以分离的,PPT 不需要通过流来传输,这样可以极大的降低带宽。存储,可以对视频进行合理的压缩,另外很多云厂商提供的存储服务成本也还好。至于转码,是否一定需要实时转码?能否转到端上进行,比如端上推多流等等?

3. 想问问老师,音频协议这些 17、18 年开始就比较成熟了,最近几年比较值得关注的方向是什么?

你这里应该指的是音频的编解码吧,目前还是 opus、AAC 用的比较多,具体的前沿方向研究不多,声网出了一个开源的抗丢包音频编解码器 SOLO 可以关注下。随着 RTC 应用场景越来越丰富,这种抗弱网抗丢包的编码器会不会是一个重点方向

4. 请问怎么衡量 WebRTC 音视频质量,有哪些关键的指标可以参考,业界较为成熟的方案能否推荐一下。

主要还是延迟、卡顿率等,WebRTC 本身也采集了很多数据用于辅助分析这些指标,比如接收帧率、解码帧率、渲染帧率,丢包,抖动,发送 / 接收码率等。声网最近推出了一个体验质量标准 XLA,你可以关注下。

5. 请问你们在操作系统层做了优化吗?

没有做特别的调优,针对网卡调优过,主要是网卡多队列,具体要看你的服务器,有的是已经就调好了。

6. 一台机器有多少个 worker?

一般和机器的 CPU 核心数是相同的

7. 一台机器有多少个 worker,推流是单独的机器吧,那个机器性能测试的推流数应该是指流名数对吧?

一般和机器的 CPU 核心数是相同的,推流是单独的机器,推流数就是指同时进行推流的个数,也可以理解为流名数。

8. 没有房间的概念,教育方面的业务如何做实时互动,比如课件展示?

低延迟直播系统没有房间的概念,但是业务层有各个流关系的管理,也就是所谓的房间是业务来控制的。

9. 您好,我想问,内网丢包率不高的情况下,kcp 和 tcp 有区别吗?

没有实测过,如果完全不丢包或者丢包很低的情况,应该区别不大,但是跨地域的 IDC 之间的传输一般还是公网传输,毕竟专线的成本太高。

10. 主要开发语言是 c++ 吗?

智能调度系统、信令服务是 golang,核心的流分发系统是 c++

嘉宾介绍:

周赵鹏,作业帮直播技术负责人,负责公司长连接 & 实时消息分发、大直播、实时音视频等相关技术的研究和开发。近年来主要关注和研究高性能网络服务、高并发低延迟直播架构、多云服务融合架构、实时音视频通信、WebRTC 等技术。

  • 发表于:
  • 本文为 InfoQ 中文站特供稿件
  • 首发地址https://www.infoq.cn/article/cc65U1HxA8Pzql8E6BQm
  • 如有侵权,请联系 yunjia_community@tencent.com 删除。

扫码关注云+社区

领取腾讯云代金券