📨消息可靠性(不丢消息)核心
消息发送后对方一定要收到。网络可能断、用户可能离线。怎么保证?
| 环节 | 问题 | 解决 |
| 发送→服务器 | 网络断了发不出去 | 客户端本地存+重试,ACK确认 |
| 服务器存储 | 消息要持久化 | 存DB后才返回发送成功 |
| 服务器→接收方 | 对方不在线/连接断了 | 离线消息表,上线后拉取 |
| 接收方确认 | 收到了但展示前崩溃 | 客户端ACK,服务器标记已送达 |
// 消息表设计
// message 表:id, from_id, to_id, content, create_time
// message_ack 表:message_id, user_id, status(SENT/DELIVERED/READ)
// 每条消息对应每个接收方一条 ack 记录
// 发送消息
public Message sendMessage(Long fromId, Long toId, String content) {
Message msg = new Message(fromId, toId, content);
messageMapper.insert(msg); // 1. 先存DB(持久化)
boolean online = wsService.isOnline(toId);
if (online) {
wsService.send(toId, msg); // 2. 在线→WebSocket推送
} // 3. 不在线→等对方上线后拉取离线消息
return msg; // 返回给发送方=发送成功
}
📖已读未读
微信那种"双蓝勾"怎么实现。
// 客户端打开聊天页面时,调接口标记已读
POST /api/messages/read
{ "peerId": 123 }
// 服务器更新 ack 状态
public void markAsRead(Long userId, Long peerId) {
ackMapper.updateStatus(userId, peerId, "READ");
// UPDATE message_ack SET status='READ'
// WHERE user_id=? AND message_id IN (对方发给我的消息)
// 通知发送方:对方已读
wsService.send(peerId, new ReadReceipt(peerId, userId));
}
📱离线消息
用户不在线时,消息存在哪?上线后怎么拉?
// 上线时拉取离线消息
public List<Message> getOfflineMessages(Long userId) {
// 查发给这个用户但还没 DELIVERED 的消息
return messageMapper.findUndelivered(userId);
// 推送后批量更新为 DELIVERED
}
核心:消息先存DB再推送(不丢),ACK确认(不重复),离线消息拉取(不漏)。