Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

房间IM

qinwei edited this page Dec 15, 2022 · 7 revisions

房间IM介绍

房间IM是SONA平台中非常重要的一部分,可以说是整个房间系统的骨架,大部分业务功能都是基于消息来实现的。除了常见的房间内用户的聊天互动,还包含进场、礼物打赏、连麦、各种业务指令等.

SONA一共支持 4 种消息:

  • 聊天室消息

当前房间是聊天室模式,比如直播间这种,一条消息发送给房间内的所有在线用户

  • 群组消息

当前房间是群组模式,比如游戏房这种,一条消息发送给房间内的所有用户

  • 点对点消息

不论是群组模式还是聊天室模式,都可以使用点对点消息,一条消息指定发送给房间内的某个或者某些用户,未被指定的用户是收不到的

  • 广播消息

只支持聊天室模式,一条消息同时发送给多个房间,比如给所有聊天室都发送横幅等场景,就可以使用

消息发送

房间IM的消息发送入口统一在 cn.bixin.sona.api.im.RouterRoomMessageService

不论是sona自动发送的,还是业务方服务端主动发送的消息,最终都是使用这个接口发送

消息发送有2种方式:

  • 业务方服务端直接通过dubbo接口发送
  • 通过 sona-sdk 发送

服务端直接dubbo调用比较简单,这里主要介绍 通过 sona-sdk 发送的方式

1.在 t_product_config 和 t_room_config 的房间配置中,有一个 im_send_type 字段,可以用来指定客户端通过 http 还是 长连接 发送

1=短连 2=长连

2.不管是短链还是长连,最终都会将 客户端发送的消息 回调给业务方,由业务方告知 sona,此条消息是否可以发送,业务方可以在回调中对消息做风控或者消息内容的组装等。

3.业务方需要实现接口 cn.bixin.sona.api.im.callback.MessageCallbackRemoteService,并在实现类上添加注解 @DubboService(group = "sona_${productcode}")

其中 productcode 就是业务申请的产品码,sona 会根据 group 去回调不同的业务方

4.在 sona-service 的apollo 添加配置 spi.msg.productCode,必须配置后 sona 才会去回调,否则默认可以直接发送

多个业务方用 逗号 隔开,例如 "chatroom,living"

sona-service apollo 配置

消息分级

房间IM 和单聊IM 相比有着巨大的差别,最主要的也是最有技术难点的就是 消息的高并发压力,举个例子: 单聊场景下,如果两个人每 10 秒说一句话,实际上每秒的消息下推数只有 0.1;群聊或者聊天室场景,假设是一个 500 人群,如果群里每个人也是每 10 秒说一句话,实际每秒的消息下推数是 500 / 10 * 500 = 25000;那么对于一个10w 人在线的房间互动场景,如果房间里每个人也每 10 秒说一句话,实际每秒可产生的消息下推数就是 100000 / 10 * 100000 = 10 亿,这是一个非常夸张的数字。 实际上,房间一般不会有这么高的发言和互动热度,即使能达到,也会在服务端进行限流和选择性丢弃。一个是考虑服务端的承受能力基本不可能达到这个量级,另一方面,即使消息能全部推下去,客户端也处理不了每秒一万条消息的接收,对客户端来说,一般每秒接收几十条消息就已经是极限了。

  • 单个TCP 连接的处理能力有限,受网络、设备的制约
  • 房间消息使用的写扩散模式推送,容易产生消息风暴
  • 尽可能保证重要消息送达

基于以上几点, sona 对消息进行了分层管控,可以丢弃一部分不重要的消息,尽可能保证重要消息的送达 消息分级

sona 将消息分成了4个等级:

高等级消息 非常重要的消息,比如价值极高的礼物消息
中高等级 比较重要的消息,可接受较大延时送达,价值较高的礼物
中等级 一般的消息,可接受较低延时送达
低等级 不重要的消息,可直接丢弃的,普通文本消息

消息白名单

sona 增加了消息白名单,用来管理消息等级,防止业务方乱用高等级消息

  • 高等级消息需要先配置白名单方可使用
  • 接口参数中传入的消息等级不能高于白名单中配置的等级

比如打赏消息的 msgType 为 333,业务方可以在白名单中配置高等级,然后根据打赏的金额,对333类型的消息设置不同的等级,超过1万设置高等级,小于5块设置低等级

白名单

消息频控

上面也介绍过,当房间消息量太大了,不可能保证每条消息都能送达,也没有必要,比如直播间发言,丢弃一部分是完全可以接收的,所以 sona 做了消息分级和 消息全局频控,不同等级的消息频控策略不同。

频控策略

这里介绍下房间整体的一个频控策略:

  • 单房间每秒最多60条消息
  • 其中高等级消息每秒最多30条
  • 如果在这个1秒的窗口里,前面已经发了60条普通消息,再发30条高优先消息可以发出去;
  • 如果是先发30条高优先级消息,再发40条普通消息,普通消息只能发30条

频控算法

sona 是基于 redis + lua 脚本实现 令牌桶算法,达到精确的全局频控

具体算法实现在 lua 脚本

不同等级的频控配置在sona-service的apollo 上,可以动态修改,message.flow.config 和 message.delay.config sona-service apollo 配置

redis cluster 执行lua脚本时,必须保证 keys 都在同一个slot中

可以通过 Redis 中的 hashtag 来保证同一个roomId 的keys都被哈希到同一个slot中

lua 脚本执行耗时基本在 几毫秒,并且redis 故障或者超时,消息默认可以正常发送

  • h_token 高等级令牌 30/s max 30
  • token 普通令牌 60/s max 60

当前房间发送一条消息,token减1

高等级

高等级

中高等级

中高等级

中等级

中等级

低等级

低等级

群组消息、点对点消息、广播消息不走频控

消息隔离

  • 消息优先级隔离,不同等级的消息互不影响

  • 可以在 sona-service 中配置 自定义的线程池,实现不同的 productCode 之间的消息互不影响

配置 Executor 的bean, bean name 为 ${productCode}-EXECUTOR

消息必达

sona 对重要消息做了 消息必达:

  • 消息必达主要通过 业务ack+重试 实现
  • 只支持高等级

对高等级消息增加 ack 机制,发送高等级消息时,会从当前房间在线列表内选取一批高价值用户作为 ack 用户,这些用户收到需要 ack 的消息,会立即回复服务端已收到。服务端会定时检测,对没有回复ack 的客户端进行消息重发。每条消息都有唯一的消息id 标识,客户端基于消息id 做幂等。

mercury 长连针对房间消息ack 做了特殊优化,在协议层支持了房间ack。消息发到 Mercury 网关,给需要 ack 的 user 推送消息时,会在协议的header 中添加 ack标识,mercury sdk 解析到这个标识会立即回复一条 ack 消息。

消息全链路查询

为了做到每条消息都可追踪,sona 实现了房间消息的全链路日志查询,可以根据 messageId 查询出消息的发送状态,包括 发送成功、被频控、被丢弃等状态

可以通过sona 后台管理系统上查询 sona-admin

Clone this wiki locally

AltStyle によって変換されたページ (->オリジナル) /