06-消息推送底层分析
1. 前言
上一章节我们已经通过im-client的sendPrivateMessage接口轻松实现了聊天功能,但是现在这个接口的内部实现对我们来说还是一个未知的”黑盒”。
在这一章节中,我们就来打开这个”黑盒”,看看调用sendPrivateMessage之后,具体干了什么事情。
本章节案例以私聊为主,但群聊的流程与私聊基本一致,主要的区别是群聊是发送给多个用户
2. 消息推送
2.1. 交互流程
用户发送私聊消息完整流程:
当消息的发送者和接收者连的不是同一个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交互流程
相关代码(后端):
步骤 |
类 |
方法 |
说明 |
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目前采用的的是异步方式,回推消息结果流程其实与推送消息类似,只是流程”反”过来:
相关代码:
步骤 |
类 |
方法 |
说明 |
1 |
PrivateMessageProcessor |
sendResult |
向redis回推私聊消息推送结果 |
1 |
GroupMessageProcessor |
sendResult |
向redis回推群聊消息推送结果 |
2 |
PrivateMessageResultResultTask |
pullMessage |
从redis中定时拉取私聊消息推送结果 |
2 |
GroupMessageResultResultTask |
pullMessage |
从redis中定时拉取群聊消息推送结果 |
3 |
MessageListenerMulticaster |
multicast |
向业务层的监听器广播消息推送结果 |
暂无评论内容