导读:直播业务的核心功能有两个,一个是实时音视频推拉流,另一个是直播间消息流的收发。本文主要介绍百度直播服务内的消息服务系统的设计实践和演化。 第三期百度架构师「周一见」活动火热进行中,文末有惊喜福利~ 一、背景 直播间内用户聊天互动,形式上是常见的IM消息流;但直播消息流不仅仅是用户聊天。除用户聊天外,直播间内常见的用户送礼物、进场、点赞、去购买、主播推荐商品、申请连麦等互动行为的实时提醒,也是通过消息流下发的。此外,直播间关闭、直播流切换等特殊场景,也依赖消息流的实时下发。消息流可以认为是直播间内主播与用户间实时互动和直播间实时控制的基础能力。 二、直播消息1.0 - 奠定基础 如何构建直播的消息系统,又有哪些挑战需要解决,我们来梳理一下。 2.1 直播消息场景分析 直播间内聊天消息,经常被类比于群聊。群聊是大家比较熟悉的即时通讯场景,直播间内聊天和群聊,二者有相似性,但也有本质的区别。 对比二者的特点: 1 . 同时参与人数不同:群聊的参与人数上千人就是很大的群了;但对于高热度的大型直播场景,例如国庆、阅兵、春晚等,单直播间累计用户是百万甚至千万量级的集合,同时在线人数可达数百万人。 2 . 用户与群和直播间的关系不同:用户进群退群,是相对低频的操作,用户集合相对固定,用户进出的变更频度不会特别高;而用户进出直播间,是非常频繁的,高热度直播的单直播间每秒面临上万用户的进出变更。 3 . 持续时间不同:群聊建立后,聊天持续时间可能比较长,几天到数月都有;而直播间大部分持续不超过几个小时。
根据以上分析,提炼出两个核心问题: 问题一:直播间内用户的维护 1 . 单直播间每秒上万用户的进出变更;实际进入直播间峰值不超过2万QPS,退出也不超过2万QPS。 2 . 单直播间同时数百万用户在线 3 . 单直播间累计用户达千万量级
支持在线百万、累积千万两个集合,每秒4万QPS更新,有一定压力,但有支持高读写性能的存储应该可以解决,例如redis。
问题二:百万在线用户的消息下发 面对百万在线用户,上下行都有大量的消息,从直播用户端视角分析:
由于问题一不难解决,以下主要讨论问题二。 2.2 直播消息设计目标 综合考虑直播业务场景,对于消息服务的需求目标如下:
现在,问题的核心是,如何做到把不超过N条的消息,在S秒内,下发到直播间内的百万用户,假设N<=20,S<=2。 2.3 普通群聊压力分析 2.3.1 普通群聊消息收发分析 △图1:群聊数据流及压力点 首先,具体分析一下普通群聊的消息收发流程:
2.3.2 普通群聊主要压力 如果完全重用普通群聊消息的下发通知到端拉取的全过程,对于user-1发的一条消息msg-1,如果需要支持一个实时百万量级的群消息,大概有以下几个每秒百万量级的挑战: 首先,秒级拆分出用户列表groupUserList-1,需要秒级读出百万的用户列表数据,对于存储和服务是第一个百万级挑战。 第二,对于拆分出群中的所有独立用户user-i,需要秒级查询出百万量级的device-i-j,对于存储和服务是第二个百万级挑战。 第三,对于所有device-i-j,通过动态路由服务route,需要秒级查询出百万量级的connect-j,对于存储和服务是第三个百万级挑战。 第四,对于通过长连接connect-j下发时,需要支持秒级下发百万量级的群消息通知groupmsg-notify-1到对应的connect-j上,对于长连接服务是个百万级的挑战。 第五,对于收到消息通知的所有端APP-1,需要支持百万QPS端从服务端拉取消息请求fetchMsg,对于消息信箱服务,这是也是一个百万量级的挑战;考虑到实际各端latestMsgID可能不同,可能的优化方式会更复杂一些,带来的性能影响会更大。 第六,如果在绝大多数用户是在线聊天的场景,设置已读状态也会有百万量级QPS对服务端的压力。 显然,完全重用群聊的消息流程,对消息服务和长连接服务带来的压力是巨大的。 2.3.3 普通群聊优化方案 △图2:群聊数据流优化后压力点
现在,我们来分析以上每个百万量级的挑战,是否有优化的空间。
如上优化后,减少了②⑤⑥三个百万量级压力请求,但还有①拆分用户列表③动态路由查询④长连接下发,这三个百万量级步骤需要处理。 对于①拆分用户列表,支持百万量级用户列表查询,比较常规的思路是支持基于群groupID的批量查询,例如一次可以查出100个用户,1万QPS查询就可以支持到百万;基于群groupID把用户数据的存储,分散到多个主从实例和分片上,控制好打散粒度不出现热点,基本能做到,只是存储资源可能消耗较多。 对于③动态路由查询,表面上看,面临的问题与①类似,但却有些不同。因为群的用户列表,是基于群groupID做key,建立一个表或多个打散的表;而device-i-j的查询是完全分散的,也是需要批量查询能力,但是完全分散的设备信息查询,不能只针对特定key做优化,需要动态路由服务支持整体上达到百万QPS的查询性能。 对于④长连接服务下发,由于长连接服务不依赖外部的存储服务,如果整体要支持百万量级的下发能力,若长连接单实例能支持1万的下发能力,整体上100个实例就能支持到百万量级下发。 基于以上分析,支持百万量级的消息下发,初见曙光。似乎只要优化好用户列表、动态路由的存储/查询和长连接的容量扩容,但所有的前提是需要消耗大量存储和机器资源。考虑到直播业务的实际情况,现实不容乐观: 一方面,平时没有热点直播时,可能单场直播并发在线用户数峰值不超过1万人,甚至不到1000;在业务初期,整体直播在线用户峰值可能也不超过10万。这就意味着,为了支持百万量级的峰值,资源整体上有几十倍的冗余。 另一方面,如果突然来了一场热度非常高的直播,可能需要支持的不只是100万量级消息下发,可能是500万以上的量级(例如国庆阅兵、春晚等)。这样的话,每次大型直播得提前预估可能的在线用户峰值,如果超过当前设计容量,需要对①用户列表③动态路由查询④长连接服务,分别扩容和压测;或者在可接受的情况下,做服务降级或拒绝服务。 而实际上,在线用户峰值量级很难估计准确,这样会造成实际资源利用率很低,扩缩容的操作频繁,运维成本高。是否选择这个方案,也是很令人纠结。 2.3.4 普通群聊多群组方案 也有人提过拆分多个群组的方案,例如,如果一个群组最多支持1万用户,开100个群就可以支持一百万用户;再建立一个虚拟群,将这100个群关联起来,似乎可行。 但如果仔细分析,会发现以上提到的几个问题①拆分用户列表③动态路由查询④长连接下发,高压力依然存在,还是不可避免。 除此之外,多群组还会引入其他问题: 问题一:多群组消息不同步。如果两个用户在一起看直播,而所属群不同,看到的消息会完全不同。 问题二:直播场景用户是动态进出的,也就是说群组成员非常不稳定,在线用户峰值波动也比较大。如果是根据在线人数增长,动态新开群组,可能第一个群用户已经很多了,第二个群刚开始用户比较少;或者,在峰值期间开了比较多的群,随着热度降低用户离开,用户变得分散,一些群的用户可能较稀少,聊天互动较少,这时需要缩容合并群。如何平衡多个群的用户,达到好的业务效果,也是比较难做的。 基于以上分析,我们并没有选择多群组方案。 2.4 组播mcast方案 支持实时高并发百万量级同时在线用户的直播消息架构,组播mcast方案的提出及演化。 2.4.1 跳出原有框架思考 是否要采用以上基于群聊的优化方案,还是可以另辟蹊径? 先暂时抛开群收发消息流程,对于消息下发来说,如果一定要说一个步骤是必不可少的,那一定是长连接下发这步了。没有通过长连接下发,消息就无法最终到达用户;当然有人说轮询拉取也可以替代长连接下发,来获取消息,但显然轮询拉取的性能压力和实时性与长连接下发相比差很多,故不在讨论范围。 如果能简化为,给长连接服务下发消息时指定一个类似的groupID,长连接服务能直接拆分到所有群组用户相关的长连接connect-j,就可以省略掉用户列表拆分和动态路由查询的百万量级查询。 这样的话,消息下发的压力将主要由长连接服务来承受,服务端也不需要对多个系统扩容,直播消息的优化可能会大为简化。 根据这个思路,相当于在长连接服务中,对连接connect也建立群组的概念。基于连接组的设想,我们设计了一套长连接的组播mcast机制。 2.4.2 长连接组播mcast基本概念
2.4.3 长连接组播mcast的路由概念 组播mcast-m的路由route-m,是一个长连接服务实例的集合LcsList,记录了所有加入mcast-m的长连接connect-i所在长连接服务实例lcs-j。 2.4.4 长连接组播mcast路由的记录维护 加入组播mcast:
离开组播mcast,与加入组播mcast基本类似,由客户端调用消息sdk离开mcast-m,发出上行请求mcastLeave(mcast-m),长连接服务端更新路由和mcastConnectList-m信息。 2.4.5 组播mcast消息推送 △图3:组播mcast数据流及压力点 基于组播mcast的长连接消息推送过程,是一个1:M * 1:N的扩散放大过程,具体过程描述如下:
2.4.6 组播mcast机制的性能评估 现在分析一下以上的组播mcast机制的性能压力:
容量评估(实例数) | 单实例1万qps | 单实例5万qps | 单实例8万qps |
---|---|---|---|
100万长连接 | 100 | 20 | 12.5 |
500万长连接 | 500 | 100 | 62.5 |
1000万长连接 | 1000 | 200 | 125 |
看上去,基于以上组播mcast机制,我们建立了一套高效的支持百万量级QPS的长连接下发机制,当前长连接服务的容量就可以支持,基本不用扩容。但是否能完全满足直播业务场景需求,还需要进一步讨论。 2.4.7 消息峰值问题 对于每秒1条消息,扩散到100W用户,甚至500W用户,以上组播mcast机制似乎都能应对。 但直播间内消息的实际情况是,热门的直播每秒用户上行聊天消息会有很多,除聊天消息外,直播间还有人数、进场、点赞、分享等定期和不定期发送的很多种类系统消息。 如果假设每秒峰值有100条各类消息,100W*100=1亿,简单按单实例5Wqps算,需要2000个实例才能支持,虽然比老的群聊系统应该好很多,但系统还是遇到大量资源冗余或应对峰值需要大量扩容的老问题。是否能有更好的解决方式?
这里我们考虑常见的一个优化思路,是通过批量聚合的模式来提高系统性能。如果将这100条消息,每秒聚合打包一次来统一下发,QPS还是100W,长连接系统的下发QPS不变,但每秒下发消息量级可以达到1亿,这个聚合方案实测是可行的。聚合模式,我们付出的成本是消息时延的上升,1秒的聚合平均时延增加500ms,用户体验损失不算大,但系统下发消息量级可以提升百倍,综合评估成本收益来看是合理的。考虑到直播的实际场景,大多数场景下秒级的聚合和时延是可以接受的。 2.4.8 消息带宽问题 聚合延时下发,长连接单实例QPS问题解决了,随之而来的是,长连接单实例下发的带宽压力问题。例如,长连接单实例需要下发10000长连接时,每秒100消息,消息平均2K字节,实际带宽为2K*100*10000*8=15625Mbps,这已经超过单物理机的万兆网卡的带宽容量。 另一方面,从全局带宽来看,也高达1.5Tbps,带宽资源对于机房出口也会带来压力,这样的带宽成本过高,需要削减带宽使用或有更好的替代方案。
面对下发数据量带宽消耗过大的问题,在不改动业务数据的前提下,我们采用了数据压缩的解决方案。而压缩是CPU密集型的操作,由于直播业务的实时性,不能简单考虑压缩比,在综合平衡压缩比、压缩时延和压缩CPU消耗后,调优压缩库后实测的平均压缩比达到6.7 : 1,数据量压缩到原来的15%左右,这样15625Mbps*15%=2344Mbps=2.29Gbps;单机万兆网卡的带宽容量,最多承载4.27万的长连接下发,虽然没有达到5万,基本也可以接受。 从全局带宽来看,峰值也削减到不超过230Gbps,收益很明显。
带宽对比 | 压缩前 | 压缩后 |
---|---|---|
单机10000长连接 | 15.26 Gbps | 2.29 Gbps |
全局100万长连接 | 1.49 Tbps | 229 Gbps |
全局500万长连接 | 7.45 Tbps | 1.12 Tbps |
2.4.9 客户端性能问题 进一步考虑,直播场景下,不仅是有较高的峰值消息量级,而是在直播过程中有持续的高消息量级压力;这不仅对于服务端是压力,对于客户端来说也是个挑战。持续的高消息量级,一方面,客户端在接收、展示等方面有明显的压力;另一方面,直播界面上过多过快的消息刷新,对于用户体验也是有害无益的。
所以,在综合平衡用户体验和客户端性能的基础上,消息服务端增加了结合消息优先级的分级频控限速机制,单用户客户端并不需要承受每秒100条的压力,削减每秒下发消息后,长连接单实例每秒下发5-8万长连接,CPU和带宽都是可以稳定支持的。 2.4.10 实时消息问题
我们提供了基于消息优先级的实时下发机制,对于高优消息可以立即触发聚合下发,不会增加聚合延时;而对于普通中低优消息,还是做延时聚合下发。 2.4.11 用户在线问题 组播mcast机制的出发点,在百万量级高并发在线的场景下,保障在线用户的消息到达,允许不在线用户接收消息的部分折损,付出合理的技术复杂度和成本,取得服务质量和性能平衡。 而针对在线用户的消息到达,还有个关键问题是如何保障用户的长连接在线。 为了提升长连接服务的接入稳定性和可达性,我们在以下几个方面做了优化。
长连接服务在国内三大运营商的华北华东华南区域均部署了接入点入口;针对有部分国外用户的直播场景,增加了香港机房的独立接入点入口。
针对部分用户的DNS劫持问题和解析错误问题,消息SDK接入了HTTPDNS服务并优化本地缓存,形成多级域名解析保障体系,提升了域名解析的可靠性,减少了DNS劫持和错误率。
长连接的心跳是保活探活的重要手段,针对直播场景实时性高的特点,为了尽快发现长连接断链,在组播mcastJoin后,长连接心跳也调整为间隔更短、服务端动态可控的智能心跳。这样在及时发现连接异常后,消息SDK可以快速主动重新建连。
在直播间用户已加入组播mcast的情况下,如果长连接断链,长连接服务端会主动或被动的触发清除组播mcast成员。而长连接重建连恢复时,直播业务层也需要监听连接恢复信号,重新加入组播mcast,以恢复组播mcast的消息通路。 2.4.12 组播mcast小结 综上所述,组播mcast机制,有效的解决了百万量级同时在线用户的消息实时下发问题;对于短时断链和消息过多,允许部分消息的丢弃;满足了直播场景消息的设计目标。 组播mcast机制特点是:
三、直播消息2.0 - 持续发展 在组播mcast机制解决了百万量级的在线用户实时消息下发后,直播消息的场景不断扩大,不断有直播创新业务提出新的消息需求。相应的,组播mcast的服务机制也需要与时俱进,不断在深度和广度上拓展优化。以下重点介绍一下历史消息和礼物消息。 3.1 历史消息 对于刚进入直播间的用户来说,需要看到一些最近的聊天记录,以增强聊天互动氛围并帮助了解直播的进展;对历史聊天记录感兴趣额用户,还可以追溯更多的消息历史。这就产生了聊天历史的需求。 为了支持这类历史消息的需求,解决方案是对于每个组播mcast申请开通一个组播公共消息信箱mcast-mbox服务。
补充说明一下消息信息的概念和应用。 3.1.1 消息信箱服务概念
实际上,最常用的就是基于msgid范围的消息拉取;这里的消息信箱服务是时间线timeline模型,有兴趣的同学可以进一步参考时间线timeline模型的相关信息。 3.2 礼物消息 △图4:礼物消息 礼物消息场景分析:
基于以上分析,直播消息提出以下技术方案: 1 . 增加一个独立的可靠消息组播mcast通道(如图4中组播mcast-2),专供高优可靠消息的收发;与其他普通消息、系统消息在数据流层面隔离,减少相互干扰; 2 . 对于普通用户侧的端消息SDK,礼物消息组播mcast通道虽然是新增独立通道,消息收发逻辑与普通消息组播mcast通道保持一致; 3 . 对于主播侧,端消息SDK对于礼物消息组播mcast通道,需要支持推拉结合模式,以保障礼物消息的全部到达;即使有短暂的掉线,也需要取到全部礼物消息; 4 . 对于主播侧,在极端情况下,如果长连接建连有异常,消息SDK可以通过短连接接口轮询,来拉取礼物组播mcast信箱消息来兜底。
基于以上独立的可靠消息组播mcast通道方案,在未剔除一些异常场景的情况下,如主播下线未关播、数据偶发打点丢失等,礼物消息的触达率已达到99.9%以上。 3.3 直播消息其他方面的发展 在百度直播的发展历程中,直播消息服务还面临着许多其他基础性问题和创新业务带来的其他挑战。现在这些问题都有了较好的解决方案,以下列举一些,供大家学习参考:
限于篇幅,以上问题在此不再做具体讨论,有兴趣同学欢迎直接联系探讨。 四、回顾展望 自百度直播上线以来几年间,直播消息服务迎难而上,一路披荆斩棘为百度直播保驾护航,为百度直播提供了坚实的技术支撑和保障。 未来,在支持直播创新业务、更细粒度的消息分级服务、直播消息基础服务的稳定性和性能等方面,直播消息服务会继续努力,夯实基础,持续创新,以支持直播业务更好更快的发展。
Copyright© 2013-2020
All Rights Reserved 京ICP备2023019179号-8