玩转直播系列之消息模块演进(3)

1、背景

即时消息(IM)系统是直播系统重要的组成部分,一个稳定的,有容错的,灵活的,支持高并发的消息模块是影响直播系统用户体验的重要因素。IM长链接服务在直播系统有发挥着举足轻重的做用。算法

本篇文章针对秀场直播,简单地描述一下消息模型,说明一下咱们消息模型的架构,并结合咱们一年以来,经过处理不一样的业务线上问题,来进行演进式的消息模型架构的升级与调整,将此整理成文,并分享給你们。json

在目前大部分主流的直播业务中,推拉流是实现直播业务最基本的技术点,消息技术则是实现观看直播的全部用户和主播实现互动的关键技术点,经过直播IM系统模块,咱们能够完成公屏互动,彩色弹幕,全网送礼广播,私信,PK等核心秀场直播的功能开发。"IM消息"做为用户和用户,用户和主播之间"沟通"的信息桥梁,如何保证"信息桥梁"的在高并发场景下保持稳定可靠,是直播系统演进过程当中一个重要的话题。安全

2、直播消息业务

在直播业务中,有几个核心的关于消息模型的概念,咱们先简单地介绍一下,方便你们对直播相关的消息模型有一个总体上的理解。服务器

2.1 主播与用户

主播和观众,对于IM系统来讲,都是一个普通用户,都会有一个惟一用户标识,也是IM分发到点对点消息的重要标识。微信

2.2 房间号

一个主播对应一个房间号(RoomId),主播在开播以前,进行身份信息验证以后,就会绑定惟一的房间号,房间号是IM系统进行直播间消息分发的重要标识。网络

2.3 消息类型划分

按照直播业务特性,IM消息划分的方式有不少方式,例如按照接收方维度进行划分,按照直播间消息业务类型进行划分,按照消息的优先级,存储方式均可以进行不一样的划分等等方式。数据结构

一般,咱们按照接收方维度进行划分有以下几个类型的消息:架构

  • 点对点消息(单播消息)
  • 直播间消息(群播消息)
  • 广播消息

按照具体的业务场景有以下几个类型的消息:并发

  • 礼物消息
  • 公屏消息
  • PK消息
  • 业务通知类消息

消息可以实时准确地分发到对应的群体或者单个用户终端都是很是必要的。固然好一点的IM消息模型也可以赋能业务一些新的能力,例如以下的能力:svg

  • 统计每一个直播间的实时在线人数
  • 捕获用户进出直播间的事件
  • 统计每一个用户实时进入直播间的时间

2.4 消息优先级

直播的消息是有优先级的,这一点是很重要的,与微信,QQ等聊天IM产品不同的地方是直播间消息是分优先级的。

微信等聊天消息产品,无论是私聊仍是群聊,每一个人发送消息的优先级基本上是同样的,不存在谁的消息优先级高,谁的消息优先级低,都须要将消息准确实时地分发到各个业务终端,可是直播由于业务场景的不一样,消息分发的优先级也是不同的。

举例来讲,若是一个直播间每秒只能渲染15~20个消息,若是一个热点直播间一秒钟产生的消息量大于20条或者更多,若是不作消息优先级的控制,直接实时分发消息,那么致使的结果就是直播间公屏客户端渲染卡顿,礼物弹框渲染过快,用户观看体验大幅降低,因此咱们要针对不一样业务类型的消息,给出不一样的消息优先级。

举例来讲,礼物消息大于公屏消息,同等业务类型的消息,大额礼物的消息优先级又大于小额礼物的消息,高等级用户的公屏消息优先级高于低等级用户或者匿名用户的公屏消息,在作业务消息分发的时候,须要根据实际的消息优先级,选择性地进行消息准确地分发。

3、消息技术点

3.1 消息架构模型

3.2 短轮询 VS 长连接

3.2.1 短轮询

3.2.1.1 短轮询的业务模型

首先咱们先简单地描述一下短轮询时间的过程和基本设计思想:

  • 客户端每隔2s轮询服务器接口,参数是roomId和timestamp,timestamp第一次传递0或者null。
  • 服务器根据roomId和timestamp查询该房间在timestamp时间戳后产生的消息事件,返回限定条数的消息例如(例如返回10~15条,固然在这个timestamp以后产生的消息数远远大于15条,不过由于客户端渲染能力有限和过多的消息展现,会影响用户体验,因此限制返回的条数),而且同时返回这些消息中最后一条消息产生的时间戳timestamp,做为客户端下次请求服务器的基准请求时间戳。
  • 以此反复,这样就能够每隔2s按照各个终端要求,更新每一个直播间的最新消息了

总体的主体思想如上图所示,不过具体的时间能够再作精细化处理,后续再作具体的说明和细节说明。

3.2.1.2 短轮询的存储模型

短轮询的消息存储与正常的长链接的消息存储有必定的区别,不存在消息扩散的问题,咱们须要作的消息存储须要达到以下的业务目标:

  • 消息插入时间复杂度要相对比较低;
  • 消息查询的复杂度要相对比较低;
  • 消息的存储的结构体要相对比较小,不能占用太大的内存空间或者磁盘空间;
  • 历史消息可以按照业务须要作磁盘持久化存储;

结合上述4点的技术要求,毕竟通过小组成员的讨论,咱们决定使用Redis的SortedSet数据结构进行存储,具体实现思路:按照直播间产品业务类型,将业务消息划分为以下四大类型:礼物,公屏,PK,通知。

一个直播间的消息使用四个Redis的SortedSet数据结构进行存储,SortedSet的key分别是"live::roomId::gift","live::roomId::chat","live::roomId::notify","live::roomId::pk",score分别是消息真实产生的时间戳,value就是序列化好的json字符串,以下图所示:

客户端轮询的时候,服务端查询的逻辑以下所示:

不少同窗会疑问,为何不适用Redis的list的数据结构呢?以下图会进行详细的说明:

最后咱们再对比一下Redis的SortedSet和Redis的List这两个数据结构在直播消息存储的时候,时间复杂度的相关分析。

以上,就是咱们使用Redis的SortedSet数据结构进行消息存储的一些简单的设计思考,后续咱们也会提到端轮询的编码时候,须要的注意点。

3.2.1.3 短轮询的时间控制

短轮询的时间控制及其重要,咱们须要在直播观众观看体验QoE和服务器压力之间找到一个很好的平衡点。

轮询的间隔时间太长,用户体验就会降低不少,直播观看体验就会变差,会有"一顿一顿"的感受。短轮询的频率太高,会致使服务器的压力过大,也会出现不少次"空轮询",所谓的"空轮询"就是无效轮询,也就是在上一秒有效轮询返回有效消息以后,间隔期直播间没有产生新的消息,就会出现无效的轮询。

vivo直播目前每日的轮询次数是10+亿次,晚观看直播高峰期的时候,服务器和Redis的CPU负载都会上升,dubbo的服务提供方的线程池一直处于高水位线上,这块须要根据机器的和Redis的实时负载的压力,作服务器的水平扩容和Redis Cluster的节点扩容,甚至让一些超高热度值的直播间负载到指定的Redis Cluster集群上,作到物理隔离,享受到"VIP"服务,确保各个直播间的消息相互不影响。

直播人数不同的直播间,轮询的时间也是能够配置的,例如人数比较少的直播,百人如下的直播间,能够设置比较高频的轮询频率,例如1.5s左右,超过300人以上的,1000人如下能够2s左右,万人直播间能够设置2.5s左右,这些配置应该均可以经过配置中心实时下发,客户端可以实时更新轮询的时间,调整的频率能够根据实际直播间用户体验的效果,而且结合服务器的负载,找到一个轮询间隔的相对最佳值。

3.2.1.4 短轮询的注意点

1)服务端须要校验客户端传递过来的时间戳:这一点很是重要,试想一下,若是观众在观看直播的时候,将直播退出后台,客户端轮询进程暂停,当用户恢复直播观看画面进程的时候,客户端传递过来的时间就会是很是老旧甚至过时的时间,这个时间会致使服务器查询Redis时出现慢查,若是出现大量的服务器慢查的话,会致使服务器链接Redis的链接没法快速释放,也会拖慢整个服务器的性能,会出现一瞬间大量的轮询接口超时,服务质量和QoE会降低不少。

2)客户端须要校验重复消息:在极端状况下,客户端有可能收到重复的消息,产生的缘由可能以下,在某一个时刻客户端发出roomId=888888&timestamp=t1的请求,由于网络不稳定或者服务器GC的缘由,致使该请求处理比较慢,耗时超过2s,可是由于轮询时间到了,客户端又发出了roomId=888888&timestamp=t1的请求,服务器返回相同的数据,就会出现客户端重复渲染相同的消息进行展现,这样也会影响用户体验,因此每个客户端有必要对重复消息进行校验。

3)海量数据没法实时返回渲染的问题:设想一下,若是一个热度极大的直播间,每秒钟产生的消息量是数千或者上万的时候,按照上面的存储和查询思路是有漏洞的,由于咱们每次由于各个因素的限制,每次只返回10~20条消息,那么咱们须要很长的时间才能把这热度不少的一秒钟的数据所有返回,这样就会形成最新的消息没法快速优先返回,因此轮询返回的消息也能够按照消息优先级进行选择性丢弃。

客户端轮询服务服务器查询直播间的消息的好处是显而易见的,消息的分发是很是实时和准确的,很难出现由于网络颤抖致使消息没法到达的场景,不过坏处也是很是明显的,服务器在业务高峰期的负载压力很大,若是直播间的全部消息都是经过轮询分发的,长期以往,服务器是很难经过水平扩容的方式来达到线性增加的。

3.2.2 长链接

3.2.2.1 长链接的架构模型

从流程上来讲,如上图所示,总体直播长链接的流程:

手机客户端首先经过http请求长链接服务器,获取TCP长链接的IP地址,长链接服务器根据路由和负载策略,返回最优的可链接的IP列表。

手机客户端根据长链接服务器返回的IP列表,进行长链接的客户端的链接请求接入,长链接服务器收到链接请求,进而创建链接。

手机客户端发送鉴权信息,进行通讯信息的鉴权和身份信息确认,最后长链接创建完成,长连服务器须要对链接进行管理,心跳监测,断线重连等操做。

长链接服务器集群的基本架构图以下所示,按照地域进行业务划分,不一样地域的终端机器按需接入;

3.2.2.2 长链接创建和管理

为了使消息即时、高效、安全地触达用户,直播客户端和IM系统创建了一条加密的全双工数据通路,收发消息均使用该通道,当大量用户在线的时候,维护这些链接、保持会话,须要用到大量内存和CPU资源。

IM接入层尽可能保持功能简洁,业务逻辑下沉到后面逻辑服务中进行处理,为了防止发布的时候,重启进程会致使大量的外网设备从新创建链接,影响用户体验。接入层提供热更新的发布方案:链接维护,帐号管理等不常常改动的基础逻辑放入主程序中,业务逻辑采用so插件的方式嵌入到程序的,修改业务逻辑时只须要从新加载一次插件便可,能够保证与设备的长链接不受影响。

3.2.2.3长链接保活

长链接创建后,若是中间网络断开,服务端和客户端都没法感知,形成假在线的状况。所以维护好这个“长链接”一个关键的问题在于可以让这个“长链接”可以在中间链路出现问题时,让链接的两端可以快速获得通知,而后经过重连来创建新的可用链接,从而让咱们这个长链接一直保持高可用状态。IM在服务端开启了keeplive保活探测机制和在客户端启用了智能心跳。

  • 利用keeplive保活探测功能,能够探知客户端崩溃、中间网络端开和中间设备因超时删除链接相关的链接表等意外状况,从而保证在乎外发生时,服务端能够释放半打开的TCP链接。
  • 客户端启动智能心跳不只能在消耗极少的电和网络流量条件下,通知服务器客户端存活状态、定时的刷新NAT内外网IP映射表,还能在网络变动时自动重连长链接。

3.2.3 直播间IM消息分发

IM长链接分发消息的总体流程图

在整合客户端,IM长链接服务器模块和直播业务服务器模块这三个模块的时候,总体消息的分发逻辑遵循以下的基本原则:

  • 单播,群播,广播消息全部的消息都是由直播业务服务器调用IM长链接服务器的接口,将须要分发的消息分发到各个业务直播间。
  • 业务服务器对直播间产生的事件进行对应的业务类型作响应的处理,例如送礼扣减虚拟货币,发送公屏进行文本健康校验等。
  • 客户端接受直播业务服务器的信令控制,消息是经过长链接通道分发仍是http短轮询分发,都是由直播业务服务器控制,客户端屏蔽底层消息获取的方式细节,客户端上层接受统一的消息数据格式,进行对应的业务类型消息处理渲染。

3.2.3.1直播间成员管理和消息分发

直播间成员是直播间最重要的基础元数据,单个直播间的用户量其实是无上限的,且呈现大直播若干个(大于30W同时在线)、中直播间几百个、小直播几万个这样分布,如何管理直播间成员是一个直播间系统架构中核心功能之一,常见的方式有以下两种:

1.为直播间分配固定分片,用户与具体的分片存在映射关系,每一个分片中保存用户相对随机。

采用固定分片的方式算法实现简单,可是对于用户少的直播间有可能分片承载的用户数量少,对于用户大的直播间有可能分片承载用户量又比较大,固定分片存在自然伸缩性差的特色。

2.动态分片,规定分片用户数,当用户数超过阈值时,增长一个新的分片,分片数量能够随着用户数增长而变化。

动态分片能够根据直播间人数自动生成分片,满了就开辟新片,尽可能使每一个分片的用户数达到阈值,但已有分片的用户数量随着用户进出直播间变化,维护复杂度比较高。

3.2.3.2 直播间消息分发

直播间中有进出场消息、文本消息、礼物消息和公屏消息等多种多样消息,消息的重要程度不同,可为每一个消息设定相应的优先级。

不一样优先级的消息放在不一样的消息队列中,高优先级的消息优先发送给客户端,消息堆积超过限制时,丢弃最先、低优先级的消息。另外,直播间消息属于实时性消息,用户获取历史消息、离线消息的意义不大,消息采用读扩散的方式存储组织。直播间消息发送时,根据直播间成员分片通知对应的消息发送服务,再把消息分别下发给分片中对应的每个用户,为了实时、高效地把直播间消息下发给用户,当用户有多条未接收消息时,下发服务采用批量下发的方式将多条消息发送给用户。

3.2.3.3 长链接的消息压缩

在使用TCP长链接分发直播间消息的时候,也须要注意消息体的大小,若是某一个时刻,分发消息的数量比较大,或者同一个消息在作群播场景的时候,群播的用户比较多,IM链接层的机房的出口带宽就会成为消息分发的瓶颈。因此如何有效的控制每个消息的大小,压缩每个消息的大小,是咱们也须要思考的问题,咱们目前经过两种方式来来作相关消息体结构的优化:

使用protobuf协议数据交换格式

相同类型的消息进行合并发送

通过咱们线上测试,使用protobuf数据交换格式,平均每个消息节省43%的字节大小,能够大大帮助咱们节省机房出口带宽。

3.2.3.4 块消息

所谓块消息,也是咱们借鉴其余直播平台的技术方案,也就是多个消息进行合并发送,直播业务服务器不是产生一个消息就立马调用IM长链接服务器集群直接进行消息的分发。主要思想,就是以直播间为维度,每隔1s或者2s,以匀速的时间间隔将在这个时间段业务系统产生的消息进行分发。

每秒分发10~20个消息,若是每秒中,业务服务器积累到的消息大于10~20个,那就按照消息的优先级进行丢弃,若是这10~20个消息的优先级都比较高,例如都是礼物类型的消息,则将消息放在后一个消息块进行发送,这样作的好处有以下三个;

合并消息,能够减小传输多余的消息头,多个消息一块儿发送,在自定义的TCP传输协议中,能够共用消息头,进一步减小消息字节数大小;

防止出现消息风暴,直播业务服务器能够很方便的控制消息分发的速度,不会无限制的分发消息到直播客户端,客户端没法处理如此多的消息;

友好的用户体验,直播间的消息由于流速正常,渲染的节奏比较均匀,会带来很好的用户直播体验,整个直播效果会很流畅

3.3 消息丢弃

无论是http短轮询仍是长链接,在高热度值直播间出现的时候,都会存在消息丢弃的状况,例如在游戏直播中,有出现比较精彩瞬间的时候,评论公屏数会瞬间增长,同时送低价值的礼物的消息也会瞬间增长不少,用来表示对本身选手精彩操做的支持,那么服务器经过IM长链接或者http短轮询每秒分发的消息数就会数千或者上万,一瞬间的消息突增,会致使客户端出现以下几个问题;

客户端经过长链接获取的消息突增,下行带宽压力突增,其余业务可能会受到影响(例如礼物的svga没法及时下载播放);

客户端没法快速处理渲染如此多的礼物和公屏消息,CPU压力突增,音视频处理也会受到影响;

因消息存在积压,致使会展现过时已久消息的可能,用户体验(QoE)指标会降低。

因此,由于这些缘由,消息是存在丢弃的必要的,举一个简单的例子,礼物的优先级必定是高于公屏消息的,PK进度条的消息必定是高于全网广播类消息的,高价值礼物的消息又高于低价值礼物的消息。

根据这些业务理论,咱们在真实代码开发中,能够作以下的控制:

结合具体业务特色,给各个业务类型的消息划分出不一样等级,在消息分发触发流控的时候,根据消息优先级选择性丢弃低优先级消息。

消息结构体新增建立时间和发送时间两个字段,在实际调用长链接通道的时候,须要判断当前时间与消息的建立时间是够间隔过大,若是过大,则直接丢弃消息。

增益消息(纠正消息),在业务开发中,消息的设计中,尽可能地去设计增益消息,增益消息指的是后续到达的消息可以包含前续到达的消息,举例来讲,9点10的消息,主播A和主播B的PK值是20比10,那么9点11分分发的PK消息值就是22比10,而不能分发增量消息2:0,但愿客户端作PK条的累加(20+2 :10+0),可是存在消息由于网络颤抖或者前置消息丢弃,致使消息丢弃,因此分发增益消息或者纠正消息会可以帮助业务从新恢复正常。

4、写在最后

任何一个直播系统,随着业务的发展和直播间人气不断的增长,消息系统遇到的问题和挑战也会随之而来,无论是长链接的消息风暴,仍是海量http短轮询的请求,都会致使服务器压力的剧增,都是咱们须要不断解决和优化的。咱们要针对每个时期的业务特色,作直播消息的持续升级,作可演进的消息模块,确保消息分发的能力可以确保业务的持续发展。

vivo直播消息模块也是逐步演进的,演进的动力主要来自于由于业务的发展,随着业务形态的多样化,观看的用户数愈来愈多,系统的功能也会逐步增多,也会遇到各类性能瓶颈,为了解决实际遇到的性能问题,会逐一进行代码分析,接口性能瓶颈的分析,而后给出对应的解决方案或者解耦方案,消息模块也不例外,但愿这篇文章可以给你们带来相关直播消息模块的设计启发。

做者:vivo互联网技术-LinDu、Li Guolin
相关文章
相关标签/搜索