一条消息从发送到送达, 中间到底经历了什么。 表面上用户只是点了一下"发送", 实际上后端已经开始了一场分布式协作。 个人项目:github.com/w3777/xigua...

1. 消息从客户端出发
用户在客户端输入内容后,消息会先通过 WebSocket 长连接发送到接入节点。
这里的接入层,本质上更像一个"网关"。
它不负责真正处理消息,只负责:
- 维护连接
- 心跳检测
- 用户在线状态
- 转发消息
所以接入节点收到消息后,会通过 Dubbo RPC 把消息转发到 Center 消息中枢。
这时候,真正的消息链路才刚开始。
2. Center 开始接管消息
Center 服务收到消息后,
这里会完成几件事情:
- 生成 messageId
- 开启事务
- 消息持久化
- 更新会话状态
消息会先进入 MySQL。
因为只有真正落库成功,这条消息才算存在。
否则客户端看到"发送成功",结果服务重启后消息没了,这其实是最严重的问题。
3. ACK 为什么很重要
很多人以为:
客户端点发送后,服务端回个 success 就结束了。
实际上不是。
IM 里的 ACK,更像一种"确认机制"。
只有消息真正完成持久化后,服务端才会把 ACK 返回给发送者。
客户端收到 ACK 后:
- 消息状态变成"发送成功"
- 本地 loading 消失
- 消息进入正常展示状态
如果 ACK 超时没回来,客户端就会认为发送失败。
这里有个细节:
ACK 不能回太早。
因为:
如果 ACK 已经返回,但事务实际上失败了,客户端会误认为消息发送成功。
这也是为什么很多 IM 系统,会把 ACK 放在事务后面。
4. Redis 开始发挥作用
消息落库后,系统会开始查接收者在线状态。这里不会查数据库。
因为数据库扛不住高频在线查询。
在线状态、会话状态、未读数量这些数据, 都会放进 Redis。
Redis 在 IM 里,其实更像一个"实时状态中心"。
系统会根据 Redis 中记录的:
- 用户在线节点
- 当前连接机器
- 会话状态
去定位:
"这个人现在到底连在哪台机器上。"
5. 定向投递开始
找到接收者所在节点后,Center 会再次通过 Dubbo RPC:
把消息定向投递到对应机器。
这里有个关键点:
发送者和接收者,可能根本不在同一台服务器。
比如:
发送者连接的是节点 A, 接收者连接的是节点 B。
所以消息一定会经过:
接入层 → Center → 另一台接入层
这也是 IM 分布式架构里,最核心的一层:
消息中枢。
6. 消息真正送达
接收节点收到消息后,
会通过 WebSocket:
把消息推送给接收者客户端。
这时候用户终于看到消息。
但其实流程还没结束。
因为:
"收到消息"
不等于
"已读消息"。
7. 已读回执才是最后一步
当用户真正打开聊天窗口后,
客户端会发送已读回执。
服务端收到后:
- 更新消息已读状态
- 同步会话状态
- 更新未读数量
- 通知发送方"对方已读"
至此, 一条消息的生命周期才真正结束。
8. 消息系统真正难的地方
真正复杂的是:
- ACK 一致性
- 在线状态同步
- 分布式路由
- 消息可靠性
- 会话状态维护
- 未读数量计算
- 已读同步
- 多端登录
- 节点故障恢复
很多时候,
聊天窗口只是表象。
底层其实是一套实时分布式系统。