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

本文引用了45岁老架构师尼恩的技术分享,有修订和重新排版。

1、引言

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

2、系列文章

为了更好以进行内容呈现,本文拆分两了上下两篇。

本文是2篇文章中的第 1 篇:

本篇主要聚焦的是分布式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消息可靠性架构的核心流程总结

上面三层如何联动?一张图讲清楚全链路生命周期:

**整条链路形成闭环:**任何环节出问题,都有对应兜底机制接管。

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消息时序的方法探讨

即时通讯技术学习:

本文已同步发布于: http://www.52im.net/thread-4889-1-1.html

相关推荐
hz567895 分钟前
国产化视频会议系统怎么做?鲲鹏+麒麟+国密的完整国产化路径
音视频·实时音视频·信息与通信
逻极1 小时前
Hermes Agent深度探索:一个会自我沉淀经验的终端智能体
架构·llm·agent·rag·多智能体系统·hermes agent·hermes
数智顾问2 小时前
(151页PPT)XX集团信息化整体架构规划及ERP方案建议书(附下载方式)
大数据·架构
caimouse2 小时前
Reactos 第1章 概述
c语言·开发语言·架构
namexingyun2 小时前
拆解Fable 5三重安全护栏:模型路由、蒸馏防护与生物安全分类器的技术原理 - 微元算力(weytoken)
java·人工智能·python·安全·架构·ai编程
小短腿的代码世界3 小时前
行情快照与增量更新引擎:Qt在高频交易数据分发中的核心架构——你的行情推送为什么延迟了500ms?
开发语言·qt·架构
上海云盾第一敬业销售3 小时前
高效阻止网站攻击的WAF防护架构解析
web安全·架构·ddos
初中就开始混世的大魔王3 小时前
6 Fast DDS-传输层
开发语言·c++·中间件·信息与通信
意图共鸣3 小时前
意图共鸣科技《AI记忆链商业化白皮书3.0》假设场景解析:从母亲到消防员,专属AI如何重塑记忆与传承
人工智能·科技·架构
FPGA小徐3 小时前
Xilinx zynq-7000系列FPGA移植Linux操作系统详细教程
fpga开发·架构