【融云分析】聊天室海量消息分发之消息丢弃策略
1 背景
随着直播、聊天室等APP的广泛普及应用,聊天室功能越来越被重视。比如今年非常火、下载量飙升的『直播带货』类APP,在其直播中的用户聊天、弹幕、礼物、点赞、禁言、系统通知等场景都基于聊天室实现;如果将聊天室中产生的海量消息全量分发至客户端,那么客户端可能会出现卡顿,且此类刷屏消息人眼无法查看也会影响用户体验,因此聊天室消息分发的丢弃策略应运而生。
2 海量消息分发的挑战
我们以一个百万人观看的直播聊天室模型进行举例:
1、在直播中会有一波一波的消息高峰,比如直播中的“刷屏”消息,即大量用户在同一时段发送的海量消息,一般情况下此类“刷屏”消息的消息内容基本相同。如果将所有消息全部展示在客户端,则客户端很可能出现卡顿、消息延迟等问题,严重影响用户体验。
2、海量消息的情况下,如果服务端每条消息都长期存储将导致服务缓存使用量激增,使得内存、存储成为性能瓶颈。
3、在另外一些场景下,比如聊天室的房间管理员进行操作后的通知消息或者系统通知,一般情况下这类消息是较为重要的,需要优先保障它的到达率。
基于这些挑战,我们的服务需要做一个基于业务场景的优化来应对。
3 聊天室架构模型
融云聊天室服务经过多年迭代更新,目前已非常稳定。以下简要介绍融云的聊天室架构,架构示意图如下:
图3-1
简要说明:
聊天室服务-缓存聊天室的基本信息,包括用户列表,禁言封禁关系,白名单用户等
消息服务-缓存本节点需要处理的用户关系信息、消息队列信息等
*聊天室用户关系同步:
成员主动加入退出时:聊天室服务同步至==> 消息服务
分发消息发现用户已离线时:消息服务同步至==> 聊天室服务
*发送消息:
聊天室服务经过必要校验通过后将消息广播至消息服务
聊天室服务不缓存消息内容
Zk-各服务实例均注册到Zk,数据用于服务间流转时的落点计算
聊天室服务:按照聊天室ID落点
消息服务:按照用户ID落点
Redis-主要作为二级缓存,以及服务更新(重启)时内存数据的备份
4 聊天室消息分发解决方案
聊天室服务的消息分发、拉取方案:
图4-1
分发(图4-1):
1. 用户A在聊天室中发送一条消息,首先由聊天室服务处理
2. 聊天室服务将消息同步到各消息服务节点
3. 消息服务向本节点缓存的所有成员下发通知拉取
4. 如图『消息服务-1』将向用户B下发通知
另外,在分发过程中,具有通知合并机制;上述步骤3详细流程为:
a. 将所有成员加入到待通知队列中(如已存在则更新通知消息时间)
b. 下发线程,轮训获取待通知队列
c. 向队列中用户下发通知拉取
通过次流程可保障下发线程一轮只会向同一用户发送一个通知拉取,即多个消息会合并为一个通知拉取,有效提升了服务端性能且降低了客户端与服务端的网络消耗。
图4-2
拉取(图4-2):
1. 用户B收到通知后将向服务端发送拉取消息请求
2. 该请求将由『消息服务-1』节点处理
3. 『消息服务-1』将根据客户端传递的最后一条消息时间戳,从消息队列中返回消息列表,参考图4-3示意
4. 用户B获取到新的消息
图4-3
5 聊天室消息分发之消息丢弃策略
图5-1
5.1 上行限速控制(丢弃)策略:
针对上行的限速控制,默认200条/秒,根据业务需要可调整。达到限速后发送的消息将在聊天室服务丢弃,不再向各消息服务节点同步。
5.2 下行限速控制(丢弃)策略:
针对下行的限速控制,即对消息环形队列(图4.3所描述拉取消息过程)长度的控制,达到最大值后最“老”的消息将被淘汰丢弃。
每次下发通知拉取后服务端将该用户标记为拉取中,用户实际拉取消息后移除该标记。拉取中标记作用:例如产生新消息时用户具有拉取中标记,如果距设置标记时间在2秒内则不会下发通知(降低客户端压力,丢弃通知未丢弃消息),超过2秒则继续下发通知(连续多次通知未拉取则触发用户踢出策略,不在此赘述)。因此消息是否被丢弃取决于客户端拉取速度(受客户端性能、网络影响),客户端及时拉取消息则没有被丢弃的消息。
通过以上两个策略保障了客户端不会因为海量消息出现卡顿、延迟等问题;避免出现消息刷屏,肉眼无法查看的情况;同时降低了服务端存储压力,不会因为海量消息出现内存瓶颈从而影响服务。
5.3 重要消息防丢弃策略:
如前文所述,在聊天室场景下对某些消息应具有较高优先级,不应丢弃;例如聊天室的房间管理员进行操作后的通知消息或者系统通知,针对此场景,我们设置了消息白名单、消息优先级的概念,保障不丢弃。如图5-1所示,消息环形队列可以为多个,与普通聊天室消息分开则保障了重要消息不丢弃。
6 结束语
随着移动互联网的发展,聊天室的业务模型和压力也在不停地扩展变化,后续可能还会遇到更多的挑战,我们的服务会与时俱进、跟进更优的方案策略进行应对。