本文由网易云信资深服务器开发工程师曹佳俊分享,原题“深度剖析“圈组”消息系统设计 | “圈组”技术系列文章”,为了提升内容品质,本文有修订和删节。
鉴于实时社群产品Discord在IM垂直应用领域的爆火,类似的需求越来越多,云信的“圈组”就是针对这种应用场景的技术产品。
“圈组”产品发布后获得了很大的关注,很多云信用户在接入SDK的同时对于“圈组”的底层技术细节和原理也非常关注,为此我们决定推出“圈组”相关的技术文章,分享云信在“圈组”技术设计上的一些思考和实践。
本文是序列文章的第2篇,将要分享的是云信的实时社群产品“圈组”(“圈组”云信的类Discord产品实现方案)的消息系统技术设计实践。
本文是系列文章中的第 2 篇:
曹佳俊:网易云信资深服务器开发工程师,毕业于中国科学院,硕士毕业后加入网易,负责云信 IM/RTC 信令等业务的服务器开发。专注于即时通讯、RTC 信令以及相关中间件等技术,是云信开源项目 Camellia 的作者。
在介绍“圈组”的技术细节之前,我们先了解一下圈组的技术特点。
“圈组”产品最大的特点是什么?
那么对于这样一个新颖的 IM 系统,在技术上应该如何实现呢?
一种简单的思路是改造已有的 IM 系统,对于“圈组”这样的类 Discord 社群,第一个思路是拓展我们的群组功能,猛一看在很多方面确实挺像的。
我们做了个简单的对比:
从上面的表格可以看到,“圈组”和群组最大的不同:
其他的诸如身份组、个性化推送策略,似乎只要适配的做一下就可以了。
那么是不是只要想办法提升一下群组的容量,再在业务层封装一下二级结构就可以了呢?
答案显然是否定的,或者至少说基于群组去扩展不是一个很好的想法。
首先是二级结构。
在类 Discord 的二级结构中,成员的管理在 server 层,而 channel 成员是继承自 server 的,而且在 channel 之上还有很多可见性的配置(我们的“圈组”提供了黑白名单机制,而Discord 则提供了查看频道权限)。
在这种机制之下,任何 server 层面的成员变动,都可能影响全部或者部分频道的成员列表。
面对这种复杂的结构,群组有两种思路去实现:
不管哪种方式,先不说消息投递这块的逻辑,仅成员管理上逻辑的耦合和交织的复杂性,足以劝退任何人。
常规IM群组的容量一般只有数百,最多可以扩展到数千。
对于IM群组成员的管理,我们一般采取全量+增量同步相结合的方案,客户端和服务器映射到相同的群组镜像(群信息+群成员等)。此时很多操作,例如群成员的展示、检索,消息的艾特等,都可以基于纯客户端进行。
而“圈组”要求几十万甚至上百万的容量,显然客户端无法一次性获取到所有成员,如果你一次性加入多个 server,那成员的数量将更加膨胀。
因此在“圈组”这种大规模社群的设计中,很多逻辑都会转向云端,此时不管是 SDK 还是服务器,均需要修改原有的设计逻辑。
此外,大规模社群带来的是消息爆炸。
在原有的IM群组设计中,假设一个人同时加入了 1000 个群,那么这 1000 个群内的所有消息均会在第一时间下发给给客户端。
但是在一般的业务场景中,不会所有的群都同时活跃,假设这 1000 个群变成了 1000 个服务器/频道,作为一种社群组织,同时活跃的可能性将大大增加,而且每个服务器/频道的人数远远超过普通的群组,叠加之后带来的消息爆炸现象在原有的群组体系中将带来极大的压力。
压力包括多方面:
除了容量、二级结构、消息规模,包括身份组、成员管理、个性化推送策略等等都存在巨大差异。
是否真的适合在群组中添加这些复杂逻辑呢,强行绑定在一起会不会既没有一个好用的类 Discord 平台,也使得原始的群组功能繁杂,反而降低了易用性呢?
经过上面的一些分析,我们基本可以得出一个结论:在已有的群组基础上扩展来实现一个类 Discord 功能的社群,显然不是一个很好的思路。
那么还有其他“捷径”吗?
IM聊天室也是一个潜在的选项,聊天室的一大特点就是支持超大规模同时在线(参见《千万级实时直播弹幕的技术实践》),容量似乎已经不是问题,但是当考虑添加其他一些强社交关系的特性时(如成员、身份组等)就显得有点为难了,聊天室本身就是来去自如的一个开放空间,这个和圈组的产品本身定位互相冲突的。
因此基于聊天室扩展的方案也基本 pass 掉了。
基于上述种种的思考和讨论,最终选择脱离已有 IM 体系,从零研发一套全新的社群方案“圈组”,“圈组”不是一个简单的 IM 功能,而是一套可以独立运行的 IM 系统。
经过上面的讨论,相信大家对“圈组”本身的技术特点和难点也有所理解。
可以归纳为以下几点:
“圈组”整体架构:
上面展示了“圈组”服务整体的架构。
可以看到整个“圈组”服务是一个分层的架构:
这其中和消息系统相关联的包括接入层、网络层、以及后端的登录/订阅/消息/检索等模块。
基本架构如下:
消息系统中第一个要讨论的点就是消息的存储和分发方式,包括在线广播、离线推送、历史消息三个维度。
下面几节我们将对消息系统中各模块分别展开介绍。
对于一般的IM群组来说,在线广播的一般过程是这样的:依次查询群组里的所有人的在线状态,如果在线,则将消息发送给对应的长链接服务器。
显然这种机制无法复制到“圈组”,因为在“圈组”的一个服务器里可能存在超过 100w 的人。
此外:IM聊天室的广播模式也不能直接复用,因为在聊天室架构中,每个长链接映射到一个聊天室,因此当你登录到某个聊天室的时候,你只会收到该聊天室的消息。而对于“圈组”来说,每个用户会同时加入多个服务器/频道,而且会同时收到多个服务器/频道的消息。
针对“圈组”的上述特点:我们设计了消息订阅模式,也就是用户登录之后,需要订阅感兴趣的相关服务器/频道,服务器会记录下这个订阅信息。当有新消息的时候,服务器通过订阅关系(而不是在线状态)查询到需要广播的列表,通过这种方式就不再需要遍历服务器/频道里的所有用户。
但是当一个服务器/频道里在线人数非常多的时候,这个订阅关系仍然是巨大的。
为此:我们设计了一种两层订阅模型,即所有的订阅关系会保存在长链接服务器上(QChatLink/QChatWebLink),同时长链接服务器会定时发送心跳给后端的订阅服务器,心跳信息相比原始的订阅信息会大大简化,比如长链接服务器上会记录账号 A 订阅了某个频道 A 的消息,如果有 1w 个账号,则有 1w 条订阅记录,而心跳信息里只会上报有 1w 个人订阅了某个频道 A 的消息,具体的账号列表则被精简掉了。当一条消息需要广播时,消息服务会访问订阅服务,获取到该服务器/频道被订阅的长链接服务器列表,并依次给该列表中的长链接服务器发送消息下发通知,长链接服务器收到通知后会根据订阅详情再广播给所有客户端。
此外:我们还提供了多种订阅类型,当你非常关心某个频道消息时(比如页面正停留在该频道),此时你可以订阅该频道的消息。对于其他频道,如果你仅仅需要知道该频道有多少条未读消息(或者有无未读消息),则可以选择订阅该频道的未读计数(或者未读状态),此时服务下发时仅会广播精简的消息体用于维护客户端未读计数,并且当未读计数达到一定阈值之后(比如 99+),服务器可以选择不再下发任何通知消息而不影响用户体验。
通过上文介绍的消息订阅模型,极大地提高了超大型的圈组频道/服务器消息在线广播的效率,降低了服务器压力。
除此之外:我们还设计了针对小型频道的特殊策略,对于小型频道,即使不订阅,服务器也会下发消息通知给频道里所有人,从而减轻端侧消息订阅模型的维护成本。针对消息订阅机制本身,后续我们也会根据不同的业务场景,提供更多一站式的策略来帮助降低接入成本,提升整体的易用性。
在强社交的场景下,离线消息推送对于维持用户粘性+提升产品体验有很大的作用。
从技术角度看的话,主要解决2个问题:
1)第一个是超大型服务器/频道的消息推送的效率问题;
2)另一个是提供足够丰富的推送策略来帮助 C 端用户,避免被过量的推送消息给打扰。
针对第一个问题,我们针对不同规模的服务器/频道采取了不同的策略:
此外:分片会采用一致性策略,保证单个用户固定为某些节点,从而提高缓存命中效率。
针对第二个问题,推送策略可以用以下几句话来描述:
并且:未来用户还可以自定义消息的高低优先级,并搭配不同的推送配置(如不同的免打扰配置等),如下图所示。
历史消息的存储在“圈组”的场景中也需要一些特别的设计。
同样以传统IM群组为例,一般来说消息的存储方式有两种,写扩散和读扩散。在小型的IM群组或者多人会话中,写扩散模式可以简化设计,但是当群组规模扩大到一定程度(如万人群),读扩散就成了选择。
而对于“圈组”这种单个服务器可能上百万人的“群组”中,除了常规的读扩散之外,我们还设计了多级缓存的结构来应对海量的读请求。
基本的存储架构大致如下:
消息的存储主要包括两部分:
首先是写入:对于上述两者,我们都会使用中心化的缓存服务器来存储最近的数据,并使用异步+批量+聚合等手段,通过 MQ 异步落库,从而平衡写入效率(单条写入性能低)和写入读取延迟(异步写入有延迟)的问题,并且针对不同数据类型的特点,我们也选择了不同的存储方案(历史消息使用分布式时间序列数据库,未读计数使用分布式 k-v 数据库),最大化地提升消息存储和查询的性能和效率。
有写就有读,针对读取操作:
上述针对“圈组”的特别设计,消息存储系统可以应对几十数百人的小型圈组频道,也可以从容应对上百万的超大型频道。
[1] IM群聊消息究竟是存1份(即扩散读)还是存多份(即扩散写)?
[3] 企业微信的IM架构设计揭秘:消息模型、万人群、已读回执、消息撤回等
[5] 微信直播聊天室单房间1500万在线的消息架构演进之路
[7] 千万级实时直播弹幕的技术实践
[10] 一套亿级用户的IM架构技术干货(上篇):整体架构、服务拆分等
[11] 一套亿级用户的IM架构技术干货(下篇):可靠性、有序性、弱网优化等
[12] 从新手到专家:如何设计一套亿级消息量的分布式IM系统
技术交流:
- 移动端IM开发入门文章:《新手入门一篇就够:从零开发移动端IM》
- 开源IM框架源码:https://github.com/JackJiang2011/MobileIMSDK(备用地址点此)
(本文已同步发布于:http://www.52im.net/thread-4321-1-1.html)
本文系转载,前往查看
如有侵权,请联系 cloudcommunity@tencent.com 删除。
本文系转载,前往查看
如有侵权,请联系 cloudcommunity@tencent.com 删除。