06-消息推送底层分析

06-消息推送底层分析

1. 前言

上一章节我们已经通过im-client的sendPrivateMessage接口轻松实现了聊天功能,但是现在这个接口的内部实现对我们来说还是一个未知的”黑盒”。

在这一章节中,我们就来打开这个”黑盒”,看看调用sendPrivateMessage之后,具体干了什么事情。

本章节案例以私聊为主,但群聊的流程与私聊基本一致,主要的区别是群聊是发送给多个用户

2. 消息推送

2.1. 交互流程

用户发送私聊消息完整流程:

d2b5ca33bd20250831143600

当消息的发送者和接收者连的不是同一个im-server时,消息是无法直接推送的,所以我们需要设计出能够支持跨节点推送的方案:

  • 利用了redis的list类型实现消息推送,其中key为im:message:private:${serverid},每个key的数据可以看做一个队列,每个im-server根据自身的id只消费属于自己的消息队列
  • 用户发起ws连接时,redis会记录每个用户所连接的serverid
  • 用户发送消息时,im-client将根据接收方的serverid,决定将消息推向响应的队列

图中2~5步都是调用sendPrivateMessage之后的流程,整个流程涉及3个组件:

  • im-client:客户端部分,负责将消息推送至redis,对应图中橙色部分
  • im-server: 服务器部分,负责从redis拉取消息,并通过ws推送给用户,对应图中红色部分
  • redis: 中间角色,充当消息中间件,负责将消息从im-client转发到im-server

2.2. 客户端部分(im-client)

核心流程逻辑说明:

  • 通过redis获取接收方id所绑定的serverid(每个im-server都会生成一个唯一的id)
  • 当serverid存在表示用户在线,反之则说明用户离线
  • 如果用户在线,则将消息上传到redis,等待im-server拉取。注意上传消息的key是通过serverid生成的,也就是说,不同的im-server使用不同的消息队列,这么做是为了支持im-server集群化部署。
  • 如果用户离线,则向应用层回复消息发送失败状态(消息推送结果监听机制下面有介绍)

相关代码

类名

方法

作用

IMSender

sendPrivateMessage

推送私聊消息

IMSender

sendGroupMessage

推送群聊消息

2.3. 服务器部分(im-server)

核心流程逻辑:

  • 循环从redis中拉取私聊消息,然后将消息交由PrivateMessageProcessor处理
  • 通过接收方id取出对方的channel,通过channel将消息推送给用户
  • 无论推送成功还是失败,都会回推消息推送结果

相关代码:

类名

方法

作用

PullPrivateMessageTask

pullMessage

循环从redis中拉取私聊消息

PrivateMessageProcessor

process

处理拉取到的私聊消息

GroupMessageProcessor

process

处理拉取到的群聊消息

3. 消息接收

上一小节中,im-server已经通过ws向前端推送消息了,那么前端是如何跟im-server交互,并接收到消息呢?

3.1. ws交互流程

d2b5ca33bd20250831143637

相关代码(后端):

步骤

方法

说明

1

IMChannelHandler

handlerAdded

用户连接事件,这里只打印日志

2

LoginProcessor

process

登录处理,将用户id和serverId绑定

3

HeartbeatProcessor

process

心跳处理,绑定关系续命

3

IMChannelHandler

userEventTriggered

心跳超时事件,关闭channel

4,5

PrivateMessageProcessor

process

私聊消息发送处理,前面已讲解

4,5

GroupMessageProcessor

process

群聊消息发送处理,前面已讲解

6

IMChannelHandler

handlerRemoved

连接断开事件,移除channel,解除绑定

相关代码(前端):

文件

方法

说明

wssocket.js

全部

前端ws封装

home.vue

init

ws应用初始化

4. 推送结果监听机制

4.1. 方案探讨

我们思考一个问题,在im-platform调用了sendPrivateMessage之后,是否一定能将成功消息推送给用户呢?

答案是否定的,例如对方当前不在线,消息即使投递到了im-server也会由于缺少对方的channel而无法推送。

作为一个相对完善的消息推送组件,应该能够提供一种机制:不管消息是否成功发送,都应该告知调用消息推送是否成功。

一般来说,实现有两种方案:

实现方式

同步方式

异步方式

实现思路

接口同步返回结果,发送成功返回true,否则返回false

消息发送后,回推结果消息,将发送结果异步投递到业务层的监听器。

优点

调用简单

调用复杂

缺点

性能较低,实现比较复杂

性能好,同时也易于实现

4.2. 实现流程

盒子IM目前采用的的是异步方式,回推消息结果流程其实与推送消息类似,只是流程”反”过来:

d2b5ca33bd20250831143656

相关代码:

步骤

方法

说明

1

PrivateMessageProcessor

sendResult

向redis回推私聊消息送结果

1

GroupMessageProcessor

sendResult

向redis回推群聊消息送结果

2

PrivateMessageResultResultTask

pullMessage

从redis中定时拉取私聊消息送结果

2

GroupMessageResultResultTask

pullMessage

从redis中定时拉取群聊消息送结果

3

MessageListenerMulticaster

multicast

向业务层的监听器广播消息送结果

© 版权声明
THE END
喜欢就支持一下吧
点赞6 分享
评论 抢沙发

请登录后发表评论

    暂无评论内容