找回密码
 立即注册
首页 业界区 安全 如何保障分布式IM聊天系统的消息可靠性(即消息不丢) ...

如何保障分布式IM聊天系统的消息可靠性(即消息不丢)

公西颖初 昨天 16:50
本文引用了45岁老架构师尼恩的技术分享,有修订和重新排版。
1、引言

接上篇《如何保障分布式IM聊天系统的消息有序性(即消息不乱)》,本文主要聚焦分布式IM聊天系统消息可靠性问题,即如何保证消息不丢失。
1.png
2、系列文章

为了更好以进行内容呈现,本文拆分两了上下两篇。
本文是2篇文章中的第 1 篇:

  • 《如何保障分布式IM聊天系统的消息有序性(即消息不乱)》
  • 《如何保障分布式IM聊天系统的消息可靠性(即消息不丢)》(☜ 本文)
本篇主要聚焦的是分布式IM聊天系统消息可靠性问题。
3、痛点拆解:聊天消息总是丢?不是网络差,是设计没兜底

产品做着做着,用户开始投诉:“我明明发了消息,对方怎么没收到?”。你查日志发现——消息真丢了。但更可怕的是:你也不知道它什么时候丢的。
这背后,其实是移动场景下的经典三连击:

  • 1)地铁进隧道,网络闪断;
  • 2)App 被系统杀掉,进程没了;
  • 3)对方服务器刚好在发布,接口500……
你以为只是“发一下”,其实要穿越重重险境才能抵达。
结果就是:
- 消息发不出去 → 用户以为被无视;
- 或者重试太多 → 对方收到一堆重复“在吗?”;
- 最后用户体验崩了,客服工单爆了。
所以问题本质不是“快不快”,而是:
“宁可慢点,也不能丢;就算重发,也不能重复。”
这就是我们常说的可靠消息投递 ——一个看似简单的需求,却是高可用系统的分水岭。
4、解决方案:三层兜底,像保险一样层层防

光靠“发一次”肯定不行。
我们要学保险公司,给关键消息上三重保险:

  • 1)自己先复印一份存档 → 客户端本地存
  • 2)邮局签收后锁进保险柜,并异地备份 → 服务端落盘 + 副本
  • 3)如果没收到回执,隔段时间再寄,但对方只认一次 → 超时重试 + 幂等去重
每一层都不贵,合起来却能扛住99%的异常。下面看每层怎么落地。
5、第一层:客户端兜底 —— 消息先存本地,解决网络不稳定问题

记住一句话:只要没收到 ACK,就当没发成功。
所以第一步不是联网,而是先把消息塞进手机本地数据库(比如 SQLite)。
就像下面这样:
db.saveLocalMsg(msg); // 先落库,保命
boolean sendOk = network.send(msg);
if (!sendOk) {
    scheduleRetry(msg, 1000); // 发失败?排队重试
}
再加上客户端scheduleRetry  采用阶梯式重试策略:

  • 1)第1次失败 → 1秒后重试
  • 2)第2次失败 → 3秒后重试
  • 3)第3次失败 → 5秒后重试
避免雪崩式刷屏,既保障可靠性,又不压垮服务。只有等到服务端明确说“我收到了”,才把这条消息从本地删掉。
就像快递发货单:客户签收了,你才能撕票。
这样哪怕 App 崩溃、手机重启,下次打开照样继续发——用户体验无缝衔接。而如果不做这一步?一旦断网或崩溃,消息直接蒸发,用户永远不知道。
6、第二层:服务端兜底 —— 实现 服务端持久化的高可靠

客户端发来了,服务端能不能直接处理完就返回?绝对不行!
如果此时机器宕机,消息还在内存里没来得及持久化,那就真的丢了。
正确做法是两步走:

  • 1)收到消息立刻写入 RocketMQ(支持刷盘、集群同步);
  • 2)同步复制到至少3个副本节点,确保单点故障不丢数据。
伪代码如下:
rocketMQ.send(msg); // 必须落盘,断电也不怕
replicaService.syncTo3Replicas(msg); // 多副本容灾
response.sendAck(msg.getUniqueKey()); // 此时才能回 ACK
这一步的关键是:ACK 必须在落盘之后发!否则就是“虚假确认”,等于骗客户端“我收到了”,其实自己也没保住。
这一层扛住了服务端单机崩溃的风险,是整个链路的数据基石。
7、第三层:幂等性设计 —— 保障exact one

前面两层解决了“存得住”的问题,但这还不够。现实是:网络可能超时、包可能丢失、ACK 可能没传回来。
于是客户端必须重试。但重试带来新问题:
“我已经处理过了,再来一遍怎么办?”
解决办法是:用唯一键 + 幂等控制。
每个消息生成全局唯一的 key(如 sessionID:msgID),服务端通过 Redis 的原子操作判断是否已处理。
就像下面的代码这样:
String uniqueKey = msg.getUniqueKey();
if (redis.setNx(uniqueKey, "processed", 86400)) {
    processMsg(msg); // 第一次来,正常处理
} else {
    log.info("重复消息,忽略:{}", uniqueKey);
}
setNx 是关键:只有 key 不存在时才设置成功,保证多实例并发下也不会重复消费。
8、IM消息可靠性架构的核心流程总结

上面三层如何联动?一张图讲清楚全链路生命周期:
2.png
整条链路形成闭环:任何环节出问题,都有对应兜底机制接管。
9、本文小结

至此,《如何保障分布式IM聊天系统的消息有序性和可靠性》这期文章的上下两篇就完结了(上篇点此查看),上篇涉及到的分布式IM聊天系统架构中关于消息有序性问题,下篇则主要聚焦的是消息可靠性问题。
如果你是IM开发新人,想要系统地学习移动端IM开发的话,建议从我整理的这篇《新手入门一篇就够:从零开发移动端IM》开始,这样能保证IM开发知识能从网络到应用层、再从局部设计到整体架构,都有一个系统的学习脉络而不是在信息碎片中苦苦总结。
10、参考资料

[1] 什么是IM聊天系统的可靠性?
[2] 什么是IM聊天系统的消息时序一致性?
[3] 微信技术分享:微信的海量IM聊天消息序列号生成实践(算法原理篇)
[4] 马蜂窝旅游网的IM系统架构演进之路
[5] 一套亿级用户的IM架构技术干货(下篇):可靠性、有序性、弱网优化等
[6] 从新手到专家:如何设计一套亿级消息量的分布式IM系统
[7] 企业微信的IM架构设计揭秘:消息模型、万人群、已读回执、消息撤回等
[8] 融云技术分享:全面揭秘亿级IM消息的可靠投递机制
[9] 阿里IM技术分享(四):闲鱼亿级IM消息系统的可靠投递优化实践
[10] 阿里IM技术分享(八):深度解密钉钉即时消息服务DTIM的技术设计
[11] 基于实践:一套百万消息量小规模IM系统技术要点总结
[12] 一套分布式IM即时通讯系统的技术选型和架构设计
[13] 转转平台IM系统架构设计与实践(一):整体架构设计
[14] 移动端弱网优化专题(一):通俗易懂,理解移动网络的“弱”和“慢”
[15] 移动端弱网优化专题(二):史上最全移动弱网络优化方法总结
[16] Web端即时通讯实践干货:如何让你的WebSocket断网重连更快速?
[17] 从客户端的角度来谈谈移动端IM的消息可靠性和送达机制
[18] IM消息送达保证机制实现(一):保证在线实时消息的可靠投递
[19] 移动端IM中大规模群消息的推送如何保证效率、实时性?
[20] 如何保证IM实时消息的“时序性”与“一致性”?
[21] 一个低成本确保IM消息时序的方法探讨
即时通讯技术学习:
- 移动端IM开发入门文章:《新手入门一篇就够:从零开发移动端IM》
- 开源IM框架源码:https://github.com/JackJiang2011/MobileIMSDK(备用地址点此)
本文已同步发布于:http://www.52im.net/thread-4889-1-1.html)

来源:程序园用户自行投稿发布,如果侵权,请联系站长删除
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!

相关推荐

您需要登录后才可以回帖 登录 | 立即注册