单节点发送
最基本的通讯方式
存在问题
- server单点
- 并发受限
- 不支持扩容
架构图
协议制定
from_uid 发送者id
to_uid 接收者id:集合,1v1和选中聊天
room_id 群聊时房间号
msg_type 消息类型:ping/pong/ack/登录信息/聊点信息
chat_type 聊天类型:群聊,1V1,选中聊天
timestamp 发送时间
content 内容
发送消息流程
- client启动成功后,连接注册中心,获取netty-server列表地址。(参考hadoop的namenode和datanode,nacos,eureka,rocketmq)
- https://blog.csdn.net/Nuan_Feng/article/details/108237385
- 用户发送消息后,client通过策略【如负载均衡】获取挑选netty-server。
- 这里可参考【路由策略】 https://blog.csdn.net/Nuan_Feng/article/details/115619448
- client初次连接,netty-server将client和channel等信息封装成session,并存储sessionMap,以及redis中。
- sessionMap,key = uid,value => session
- netty-server发送消息
- 1v1,从sessionMap查询出to_uid,查询出则发送接受者。查询不出来,则从redis中通过to_uid作为key拉取用户信息。通过消息队列推送。可通过redis发布订阅,以及mq推送。
- 群聊,通过房间号作为key,拉取房间所有uid信息(这里可能一个房间号有几百个人,通过client传输就比较耗时)。通过uid集合,从sessionMap查询,查询的到则直接发送。查询不到则通过uid作为key拉取用户信息。在进行推送。
- 选中聊天,同群聊。
- netty-server通知客户端发送成功。
注册中心
服务剔除
服务注册
心跳检测
Netty-server
心跳检测:间歇发送ping给client。netty中IdeaStateHandler支持。
超时剔除:清除会话以及redis信息
用户连接:存储会话到进程和redis
用户退出:清除会话以及redis信息。channelInactive方法实现
server退出:注册钩子函数清除redis
client
用户连接
断线重连
心跳发送
用户退出
其他情况
离线消息
接收者不在线,处理流程如下
- 当用户B上线时,拉取A发送的消息。
- server从db拉取
- 服务器返回给用户B离线消息
- 用户B接收到后,向服务端发送ack
- server从db删除
存在问题:用户B存在很多好友,登录时需要拉取所有好友消息。
优化一:一次性拉取各个好友的离线消息数量,然后界面显示每个好友的消息数量。用户B进入某个好友的时,在按需拉取信息。
优化二:只拉取当前页面的好友的发送消息数量。