本文来自百家云技术总监张弩在LiveVideoStackCon 2017大会上的分享,并由LiveVideoStack整理而成。张弩分别从服务器端与架构端介绍了百家云的整体结构演进,并对未来行业的发展方向进行了分析、展望。
文 / 张弩
整理 / LiveVideoStack
本次的分享内容可以分为四个部分。第一部分是简介,帮助大家了解一下百家云这家公司以及它的产品情况。第二和第三部分将分别从服务器端和客户端来描述整体结构的演进。最后会站在百家云的角度来分析一下整个行业未来发展的方向。
1. 简介
1.1 公司背景&平台简介
百家互联,是一家在线教育公司,成立于2014年,它旗下有个品牌叫跟谁学。百家云团队原来主要是在跟谁学这个平台上面做一个音视频的教育工具,随着业务和技术的发展,逐渐的稳定并且有了一定的质量保障以后,就考虑是不是可以给一些其它的机构,或者第三方的行业提供一些解决方案,我们在原来的研发基础上,提供了SAAS层的服务,就逐渐演化出了百家云这家公司。当然以后的百家云会更加独立的为这些行业,或者说教育行业的一些客户去提供服务,总之百家云可能会提供一些音视频的整体的解决方案给大家。
1.2 个人简介
我毕业于西安电子科技大学。在视频会议公司工作超过了七年,负责软件视频会议系统设计、开发。2012年我进入了91外教当研发总监。2014年加入了跟谁学,主要负责在线教育系统的开发,直到百家云成立后任技术总监。
1.3 在线教学系统的基本业务需求
如果我们能够实现了以上的功能,满足了基本的业务需求,那么作为一个在线教育的产品就比较完善了,能够提供出标准的一些服务了。
1.4 特色功能
百家云在教育行业领域深耕了多年,以上是一些简单的、有特色的功能。首先整个产品是全终端覆盖的,或者说主流的一些终端都会有覆盖。例如Windows、MAC、IOS、安卓,包括微信场景,PC和手机的浏览器,这些场景下应该都是全终端覆盖的。当然还有对于应用形态或者业内形态来说,大班课、小班课、一对一、双师、展会直播都是有比较好的覆盖。第三个特点就是我们在移动教学场景下实现了多路音视频互动的,这对于教学场景来说也是一个新的探索。第四个特点就是我们实现了动态PPT全终端支持,因为动态PPT是能够比较好地还原老师授课内容的一个特别好的功能。第五个特点是云端录制的多路音视频支持,我们能够把多路音视频都录下来,在学生终端进行复习的时候来回看。另外随着海外教学的不断深入,业内这块的发展也非常好,我们对于全球数据节点的部署和海外专线优化也做了很多工作。当然对于视频会议来说,还会有基本的能力,即私有化部署能力,但是对于云平台来说,为了能够提供出私有化部署的能力也做了很多的努力。
1.5 界面展示
从上面可以看到我们一个基本的界面展示。最上面一排是PC端的一个基本的界面,下面一排是手机端APP的一个横板和竖板的界面。其中非常经典的界面就是左上角那个界面,它的左边是我们的白板区,右边是我们的视频区。当有多路视频时,视频区就会进行一个排列。
2. 服务器端结构演化
2.1 信令服务器
1) 初期单服务器模式
首先,对于信令服务器整个的演变,我们最早是要做一款线上的教学产品,也参考了当时已有的一些系统,规划了关于信令服务器的一个简单的业务模型。我们在最底层采用TCP的网络连接;从协议层来看,我们有HTTP的一些接口,当然我们最核心的是用WebSocket做一个应用层的开发;对于逻辑部分,也就是房间的管理、连接的管理部分。另外还有白板、聊天、以及音视频管理的一些模块;在接口层我们提供一些接口方便用户做一些接口的操作。对于整套系统来说,特点就是”单服务器”,很快就可以上线,但是它的问题也很多,比如负载、安全、内部结构,业务耦合,接下来我们提出了一些解决方案。
2) 拆分独立服务
我们很快就进行了服务器的升级并有了一个整体的思路就是提升系统的负载、扩大用户的覆盖面、优化单服务器中部分模块的性能问题。在最底层我们对一次登陆模块进行了拆分。登陆模块拆分出来以后主要承担了所有用户的统一登陆、身份校验以及简单的负载均衡职责。另外我们有大量的Agent代理模块,这些模块主要解决用户的覆盖和端口的问题。我们在代理这一层做了各种端口的映射,让用户能够无缝的连上。对于性能这块,我们发现聊天服务器有很严重的性能问题,所以我们也单独对聊天服务器做了一些优化。
3) 聊天服务器思路
也许大家会觉得聊天是一个比较简单的、常规的业务,不会有什么性能问题,但是如果我们集中在一个特别大的聊天室内,每个用户的相互聊天互相都要被看到的话,整个带宽产生的数据量很大。如果用户的带宽不够大,那么文字聊天就会非常严重的影响到整个音视频流的稳定性。所以我们对于整个聊天的业务按照一个四象限法则的方法进行分析,我们认为有一些消息是可以被适当的选择丢弃的,这样可以非常明显的降低我们整个聊天服务器的系统负担,我们按照这个思路对聊天服务器的整个结构做了一些改动。
首先我们在最底层按照房间进行调控,对于房间里面,我们会把不同的用户分成组进行调控。每一个组里面的消息的队列,根据重要性可以进行选择丢弃。我们也会对一些重要的、非实时的消息专门做一种队列,当用户的负载不高的时候,我们实时的把这些消息推送给用户。
有了这套改动以后,我们整个聊天服务器的稳定性和用户的体验并没有受到非常大的影响,而且重要的消息依然是有效的传递给了用户。但是我们核心服务器的单点问题依然很明显,它的解耦性还没有完全的解开,房间服务器的负载也没有很好的解决。下面我们又对房间服务器进行了一次拆分。
4) 拆分房间服务器
我们这次拆分的重点是把房间服务器的单点变成多点,并且每个点可扩展,根据用户的负载在线上进行随时增减服务器。第二个就是我们根据信令流的一些特点,把服务器分成几个模块。首先我们有广播代理模块,这个模块主要是为了去接受用户的连接,并且处理第一道的消息信令;第二个就是我们中间有一层叫做消息路由的模块,这个模块主要是将收到所有的信令,根据信令的不同要么做转发,要么交给后面的一些模块去处理。后面两块是用户信息的管理和房间信息的管理,这两块主要是信息的记录和保存以及持久化的工作。所以基于这一套模型,我们支持各自模块能够在线上随时的根据负载情况进行部署,而且我们的任何一个消息,根据不同的类型能够在消息里面进行流转,例如用户查找、激光笔等消息信令的处理利用这套模型就会非常的高效。
5) 接入层优化
接下来就是我们接入层的优化。对于SLB模块,它主要是解决了我们单点覆盖的问题,并且大量的减少了我们自己部署Agent代理的数量。在SLB后面我们自己搭了一层Nginx分发的业务,这部分的业务主要是为了我们的核心业务做一些负载平衡。比如我们不会把所有的用户分到一套服务器节点上,而是均匀的分散在不同的服务器节点。一旦中间后端有一个服务器有问题,也不会影响到整个房间内的所有用户。另外,我们在Agent这一层有大量的连接日志的系统,而且我们还有对于各种协议、业务线的一些分离,这些都是由Agent的方式来解决。当然SLB还有一个比较重要的一点,那就是它能为我们提供比较好的防火墙的功能。
6) HTTPS、WSS支持
我们在后期对信令服务器也做了一些HTTPS和WebSoketS的支持。但是这个也会给我们带来一些问题,所以我们并不会把整个系统全量的支持HTTPS,我们只是对最外层的接口层进行HTTPS的支持,对于系统内部,我们还是用各种协议进行高效的保障。对于整个系统来说,虽然支持HTTPS或者WebSockets,但是对于整个系统的影响没有那么大。所以经过之前的所有的优化以后,我们做了一个完整的信令服务器的结构图。
首先对于用户来说,通过域名解析的方法连到具有均衡负载的SLB接口层,再通过后期的分发连接到广播代理层。如果有些信令是需要被持久化,或者被记录,我们就分发到用户管理或者房间管理模块。Redis在持久化的这部分承担了很重要的职责。最后,Sinker这部分是对于一些外部的系统或者内部的系统提供一些接口层的支持,包括各种系统之间相互消息的转发,或者触发性的事件。
2.2 媒体服务器
1) 初级服务器模型
我们下面说一下媒体的部分,对于音视频数据转发的这部分服务器的演化。首先,我们参考了当时已有的直播系统并快速搭了这一套系统结构,但是我们后来发现我们教学的整个业务和这种秀场类的业务是有很大的区别的。首先我们的房间数很多,我们在初期就有几千节课或者上千节课,这就相当于有一千多个主播了,但是对于一个秀场类型的话,一千多个主播已经是挺多的,然而对于教学来说,一千多个教室才是刚起步,但是每个教室里面的人又非常的少或者大部分情况都很少。这种情况下就造成了大量的资源浪费,并且造成了中心服务器节点的压力的巨增。所以,对于教学系统来说,采用一个传统的直播模型是不太合适的。于是我们为了节省资源,便做了一个平级互推的方案。
从上面结构图可以看到数据流是没有任何浪费的,任何一个推流的用户都会把自己的数据流推给它就近的一个媒体服务器,拉流的用户就会从它就近的一个媒体服务器上去拉流,相互之间把数据落下来。但是带来的问题却很严重,首先,拉流的服务器是要知道推流服务器的具体信息的,客户端要把信息要带到媒体服务器上,所以整个服务器和客户端的解耦又是很严重的问题。第二,对于服务器的流控就变得很复杂,因为任何一个媒体服务器,它往外面转发的时候,不知道转给的是一个户,还是另外一个服务器,如果是另外一个服务器,那个服务器上还挂了好几千学生,对于流控来说,我们就变得极其的复杂。第三,因为它是一个平级结构,任何媒体服务器之间,都会相互进行传递数据,对于这些两点之间,或者两个媒体服务器之间的这个网络状况就非常的不稳定,假如说其中任何一个媒体服务器之间有网络的波动,都会影响到这堂课里面的其他用户,所以运维的难度也变得极其的大,我们很难找出在整个服务器机群内部,所有的服务器之间都能够无缝的很好的传递数据,当然最后的话就是数据路由太简单了,因为就是个平级的,就是一级转发的一个结构,更复杂的一些环境很难适应的所以,我们根据教学场景做了一个两级服务器结构的调整。
2) 互动直播模型
对于两级的结构,我们有一层推流的服务器和一层拉流的服务器,推流用户会把自己的流推送给推流服务器,拉流用户从拉流服务器上拉流。这种结构的调整让单教室的用户容量能够达到100万路,这已经远远满足我们一个单教室用户的极限了。而且,对于各个服务器上的流控变得非常简单。另外,运维工作也减少了,特别是对于我们的大量的一对一业务来说,降低了最少几十毫秒的网络延迟。我们使用了这套互动模型以后,到目前为止支撑我们的业务和整个的扩展都没有太大的问题。
3) CDN支持的演变
前面介绍的是我们自己的媒体服务器,另外我们还有对CDN的一些使用。一开始我们是自建RTMP、HLS服务器,因为我们是一个全终端的用户,无论在微信这一端,还是浏览器这一端,大量的现在还在使用RTMP或者HLS这种基于TCP的协议。但是随着CDN这块业务越来越稳定、体验越来越好,我们也使用了CDN,从刚开始选择一家CDN由于覆盖上有问题,到后来我们就选择了多家,但是这又造成了多家互推责任,导致问题解决不了。后来我们又发现这其中还有很大的问题,就是各家CDN对于数据和编码的支持力度也不太一样。所以我们就不太再依赖于CDN做类似于转码这种事情,我们只依赖于它做分发,而且不做这种多家的相互的分发,而是由统一的服务器做完所有的数据处理以后,直接推送给多家CDN,由多家CDN去完成覆盖的动作和转发的动作。我们对于CDN这块的支持,就逐渐演化成这个样子,而且从我们现在情况看起来,CDN的厂商的支持效果也越来越好。
4) 海外线路支持
对于海外线路支持这块,我们也做了很多工作。另外对于这一块的扩容和灾备就比较简单了。我们可以根据用户规模和特点实现物理隔离服务器资源,或者不同物理机房进行相互备份。
3. 客户端迭代
1) 客户端初期
前面介绍的主要是服务器端的这块,我们下面就介绍一下客户端这一块。客户端一开始被设定为全终端支持,经过一定的技术选型,我们决定使用Chrome这一套框架。初期的客户端具有一些基本的功能,后期我们对它也进行了扩展。
2) 扩展基础功能
上图是我们全终端的一个结构示意图。最下面的Html5 UI这一层开发之后,我们在Windows Web、PC Web或者移动端的Web上的一整套UI就全搞定了。这一层依赖于Chrome的中间件与上层打交道。在功能这一层我们提供了音视频的一些基本的模块。对于最上面的协议层,我们是基于UDP或者TCP进行音视频的传输。在HLS和RTMP这个模块进行编解码,然后发送给我们的CDN的集群。Web Socket协议模块主要指的是信令模块,在这层我们还提供了混音功能,由于服务器端进行混音的代价很高,所以我们在客户端实现混音。这个好处就是对整个卡顿的控制和业务的简洁度都有很大的提升。另外第三方推流是我们一些特殊的功能,我们能够把推出的RTMP的数据流接收到系统内,再由系统把它分发出去,比如展会直播这种场景用的比较多。
3) APP端
在APP这一端实现了基本功能以后,我们发现了一个比较严重的问题,就是用H5在上层做了这套业务以后,它的整个体验和原声Native的体验差别比较大,所以我们也做了一层Native的UI。当然对APP端很严重的问题就是它的音视频调优的功能。
4) 性能优化
上图是我们对性能方面做的一些简单的介绍。在我们看来最重要的有三个方面。一个是低延时和少卡顿,解决的方案例如有客户端Buffer的控制。VOIP音频编码特指的是在互动场景下,我们会选用编码和解码时间更短的一些音频编码,比如WebRTC用的FEC。传输编码这块,我们特指的是在TCP和UDP之间做选择。TCP最重要的问题就是TCP发生了拥堵以后,它的整个数据恢复的能力是有些慢的。我们可以通过TCP调优,或者把整个的发送重传机制做调优,但是对于客户端来说我们是没法调的,我们只能在服务器端做一些优化。对于客户端来说也没有什么好办法,所以对于互动场景下,我们主推的还是用UDP的方式。对于丢包重传,从UDP角度来说,所有的数据包都是不可靠到达的,这些包丢了以后,整个音频的体验就会受到很大影响。所以不同的重传策略对于应用来说就有很大的影响。在我们内部,针对于不同档位的丢包都有不同的重传的策略:重传一次,重传多次,在一个RTT的周期范围内,是否要再多重传一次,这都有一些特殊的一些考量,包括FEC的应用。
还有很重要的一块就是音视频体验里面有杂音、回音、啸叫。在我们看来PC上表现可能略微好一些,但在安卓上的体验更差。我们进行适配了很多不同的安卓的机型,包括声音的采样率,整个频谱高了以后,回音的处理确实是要差很多的。所以我们在互动场景下选择的音频编码的采样可能也会比较低。对于硬件手机这块,硬件本身的降噪、混音一致、自动增益都有一些体现。在这一部分,因为我们的用户的数量还不能够达到像QQ,或者其他厂商这么高,所以我们的适配的数量也不会有他们这么多。上述两项我们并没有一个最终的结论性的东西,所以我们也在不断的前进和摸索过程中。
最后就是多线程的编解码和硬件的编解码。我们发现我们一个双师的业务,针对于不同的硬件,我们能够在异地两个多媒体教室之间,通过1080P的视频数据能够很好的还原一个上课场景。这种场景下对于视频的清晰度的要求和实时性的要求就会很高,所以在这块编码的效率就要提升很多。但是我们上了硬件编解码以后,对于编解码阶段来说其实没有提升那么多。在后续的所有的拉伸的处理上,还有整个数据的分发上都会有些特殊的考量在里面。
在客户端迭代这个阶段我们认为最重要一点就是我们会提供更多的SAAS、SDK、API的支持。我们希望我们的产品能够应用到不同的教育机构或者不同的行业当中去。对于API这块,主要是一些账号、房间管理、基础数据的查询工作。
4. 未来优化方向
最后我们谈一下在百家云的场景下,未来有哪些东西是更加重要的。首先就是在浏览器的场景下能够对H5 WebRTC的支持;第二点就是在编码层能够进行高清的编码,然后在服务器端就能够提供多种码流的支持,用户可以通过自己的带宽情况适配不同的码流来进行拉流的动作;第三点就是服务器端需要做一些混频和混音的服务,这样就会使整个系统的消耗减少很多;第四点就是对高分辨率的支持,因为未来对于高清码流的需求会越来越大;最后一点就是未来我们会开发更多的基于教学的课堂行为分析,比如通过视频人脸识别技术就能够准确的反映课堂里学生听课和老师讲课是否专心的情况。