世界杯 ⚽️ 期间,我与其他的梅西粉丝在某 APP 里建了个梅粉聊天群,群内人数上万人,大家一起讨论赛事热点,可谓热火朝天,此起彼伏,这是四年一度的狂欢,虽值冬季,但热情不减。
“阿根廷 1 比 2 遭沙特逆转!!!!”
“天台的风略凉……”
“支持阿根廷队!!!!”
“球王梅西加油啊”
“想梅西有一个好的落幕”
……
梅西进球的一刻,激动的心颤抖的手,瞬间直达快乐巅峰,万人齐呼梅西威武;遭沙特逆转之时,心情失落无以言表,群内刷满了对梅西带领下的阿根廷队惜败沙特的震惊、恍惚、悲痛、惋惜。
是的,我被梅粉群的万条消息刷屏了。
大型群聊聚集了同一项目爱好者,将单纯的群聊功能转变为线上俱乐部性质的大型发烧友群聊,上述场景中大规模球迷们的即时消息推送,是区别于普通 IM 房间的,类似这种超大规模 IM 通讯场景,如万人观看直播并进行互动评论、万人搭建兴趣群聊等都会面临人数无上限、消息高并发的情况,所以设计一套稳定可靠的架构来保障用户通信的稳定性与可靠性就十分必要。
百万人超大房间即时通讯面临的问题
所谓百万人超大房间即时通讯,就是指支持百万人同时在房间中发送即时消息,进行线上互动。越是这种大规模即时通讯的背后,越是需要进行精心的架构设计,接下来我们以一个百万人超大房间为例,来看看针对这种大规模互动的 IM 通信场景,究竟要在架构设计环节中考虑到哪些问题才能更好的保障用户通信的高质量。
- 高并发进出房间:比如定期做活动时,面对房间内瞬间涌入的大量用户,就需要处理高并发房间的进出,以及推送房间进出变更通知;
- 消息推送:每发送一条消息,都要推送给近一百万人,推送压力极大;
- 客户端性能:同一时段往房间内发送大量消息,如果将所有消息都推送给客户端,很可能导致客户端出现卡顿、消息延迟等问题,进而严重影响用户体验;
- 对外感知:百万级超大房间与普通房间支持功能是否有差别,对外感知是否一样,客户是否有额外的接入成本;
- 消息存储:海量消息的情况下,如果每条消息都长期存储,将导致服务缓存使用量激增,使得内存、存储成为性能瓶颈。
上述列出的这些问题,是我们在设计百万人超大房间架构时需要考虑到的。ZEGO 即构科技在之前推出的 ZIM(ZEGO Instant Messaging) 中,针对大规模百万人房间即时通讯做了特殊的架构设计,以实现安全可靠、有序稳定的线上互动!
房间功能特性对比
首先,我们来对比下普通房间与 ZIM 中超大房间在功能特性上的差异,主要体现在以下两点:
- 对超大规模房间的人数不做限制;
- 收发消息,设置房间 kv 属性等功能要保证可靠性。
下图列出了 ZIM 中超大房间与普通房间的功能特性对比:
从上图中我们可以清晰的看到普通房间与超大房间的差异,接下来我们一起看下即构 ZIM 在超大房间架构模型设计上的思考与实践。
ZIM 超大房间架构模型
ZEGO 实现超大房间的服务架构如下图所示,其中主要的设计理念是:将超大房间拆分成多个子房间,分布到不同的 worker 节点来处理,以解决单点问题。
作为 to B 公司,子房间的概念只在我们的 SDK 与后台服务之间交互时使用,可以平滑的从普通房间切换到超大房间,对客户是完全无感知的,不需要为此增加接入成本。
关于上图中的各服务,我们来详细的介绍下各模块的具体功能:
- 接入服务:负责客户端的接入;
- room-router:路由服务(简称 router),来自客户端的请求会随机选择一台 router 节点,然后 router 根据特定规则路由到 room-worker 节点;
- room-worker:工作进程(简称 worker),负责具体的业务处理,比如进入房间、退出房间、发送房间消息等;
- 推送服务:负责各种信令与消息的推送,比如进出房间通知,消息推送等。推送服务与工作进程之间通过消息队列解耦;
- 缓存数据库:存储房间各种信息,包括房间用户列表、房间 kv 属性、消息列表等。
技术实现的关键逻辑
为实现超大房间,我们对许多逻辑功能做了针对性的优化,主要体现在以下几点:
房间扩展
概念上,我们称最初建立的房间为“主房间”,如果主房间人数满了(比如 500 人),则为其分配“子房间”。当有新用户进入房间时,如果主房间或者子房间还有空位,则进入相应房间;如果人数都满了,则会继续分配新的子房间。
为了合理选择房间(包括主房间与子房间)进入,需要维护每个房间的人数信息,做好房间人数信息同步,以及合理设计房间选择策略。
1.1 房间人数信息同步
对于高并发的进房间请求,每次进房间处理是需要时间的,等进房间成功了再更新房间人数,很可能导致大量请求涌入到同一个房间。
为了解决高并发进房间的问题,即构提出“预分配人数”概念,即分配给某个房间一个新的用户,就将预分配人数加一,预分配人数满了,则不再进入新的用户。
1.2 房间选择策略
router 根据缓存的房间人数信息,选择没有满员的房间进入。如果所有房间都满了,随机选择一个,由 worker 做下一步处理。
由于 router 节点有多台,且缓存的房间人数信息有可能滞后,worker 节点处理进房间请求时,该房间可能已经满了,如果出现这种情况则执行“重定向逻辑”,重新选择或新建一个子房间。
重定向逻辑主要有:
- 选择没有满员的房间进行重定向;
- 如果所有房间都满了,则新建一个子房间;
- 为了防止进入房间时,出现连续多次重定向,对于已经是重定向的请求,可以超出房间人数上限,保证进入房间成功。
房间合并
随着房间用户的进出,如果出现多个子房间人数较少的情况,则主动合并子房间,减少服务端性能消耗。合并中的房间不允许新的用户进入,房间合并后,客户端与新的子房间进行交互。
房间消息的发送与拉取
用户发送房间消息时,由该用户所在子房间进行处理,以避免单点问题。子房间同时会将消息扩散给其它子房间,拉取消息时只需要从子房间进行拉取。消息的发送与拉取过程如下:
这其中包含的主要步骤如下:
发送房间消息:由子房间处理,并扩散给其他子房间节点。
消息扩散:子房间所在节点会在服务缓存内维护一份消息列表,一个节点只需要保存一份,如果一个节点包含多个子房间,则共享这份信息。
消息存储:将全量消息保存在缓存数据库,worker 节点重启或者消息不连续时,可以从缓存数据库中恢复。
消息拉取:消息拉取同样会分散到各 worker 服务,避免单点问题。
房间 kv 属性的修改与房间消息的实现类似,此处不再单独进行介绍。
消息推送
消息推送最大的难点就是推送的扩散,在超大房间场景中,房间成员变更、房间属性变更,聊天消息都会扩散给房间内所有用户,对于百万人数级别的房间,这个扩散量是非常大的,对服务器性能与带宽都是很大的考验。
1 推送扩散问题解决
为解决扩散问题,在接入层直接保存房间的用户列表信息,推送消息时,内部服务只需要推送一个包到接入服务,然后由接入服务进行扩散,可以大幅降低推送扩散程度。
我们以发送房间消息为例,看下推送过程:
2 进一步优化推送包数量
推送到客户端的消息包量过多时,对服务端与客户端都有很大的压力,我们通过合并推送进一步降低推送包的数量。
将多条消息合并后一起推送给客户端,意味着有些消息会产生推送延迟。为了兼容推送速度与推送性能,即构使用分阶梯推送策略:消息量比较小时,不做合并,逐条即时推送;消息量比较大时,再逐步提升合并条数。
总结
在已有实例参考的一起看球、一起看电影的场景中,从数十人的小型同好会变成超大型的线上俱乐部、论坛等,ZEGO 即构科技在 ZIM 中的大规模房间架构设计让更多人实时在线互动成为可能,也让众多不可线下观球的梅粉们远距离嗨翻屋顶、百万人同频交流!
ZIM 免费体验地址:https://www.zego.im/product/InstantMessage
本文为原创稿件,版权归作者所有,如需转载,请注明出处:https://www.nxrte.com/jishu/im/5750.html