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

简介: 本文主要聚焦分布式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消息时序的方法探讨

即时通讯技术学习:

- 移动端IM开发入门文章:《新手入门一篇就够:从零开发移动端IM

- 开源IM框架源码:https://github.com/JackJiang2011/MobileIMSDK备用地址点此

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

目录
相关文章
|
1月前
|
安全 数据可视化 算法
基于蓝牙信标的医院人员定位从特点、技术实现到应用功能详解
蓝牙信标以低功耗、低成本、易部署优势,实现医院患者/医护/婴儿的米级精准定位,支持电子围栏、SOS求助、轨迹追踪与热力分析,全面提升安全管控与就医效率。如果您想进一步了解医院蓝牙信标定位系统的其他案例,欢迎关注、评论留言~也可搜索维构lbs智能定位。
|
4月前
|
缓存 Java Maven
六、Docker 核心技术:Dockerfile 指令详解
想亲手给你的应用程序打造一个专属的“集装箱”吗?Dockerfile就是你的说明书!它其实就是一个简单的文本文件,你可以在里面像搭积木一样,用FROM、COPY、RUN这些指令,一步步告诉Docker如何打包你的应用。最后,通过多阶段构建的小技巧,还能给镜像“减肥”,让它变得轻巧又高效。快来学习用Dockerfile变身打包达人吧!
508 5
|
安全 网络协议 算法
HTTPS网络通信协议揭秘:WEB网站安全的关键技术
HTTPS网络通信协议揭秘:WEB网站安全的关键技术
1085 4
HTTPS网络通信协议揭秘:WEB网站安全的关键技术
|
监控 Java C#
Spring Event 的介绍
Spring Event 是 Spring 框架中的事件驱动机制,允许组件间进行同步或异步消息传递,无需直接依赖。它包括事件(Event)、事件发布者(Publisher)和事件监听器(Listener),通过 `ApplicationEventPublisher` 广播事件,实现松耦合通信,增强模块化和可维护性。Spring 还提供了多种内置事件,如 `ContextRefreshedEvent` 和 `ContextClosedEvent`,支持同步及异步处理,并具备良好的扩展性。
405 3
|
11月前
|
存储 安全 算法
鸿蒙NEXT如何保证应用安全:详解鸿蒙NEXT数字签名和证书机制
本文对鸿蒙NEXT公开资料进行了深入分析和解读,梳理了鸿蒙单框架应用的签名机制,拆解每一步的实操过程和背后的实现原理,并对源码分析整理签名的校验机制。从中管中窥豹,探究鸿蒙系统的安全设计思路,给从事鸿蒙研发的同学提供一些借鉴。
1188 3
|
算法 前端开发 API
开源轻量级IM框架MobileIMSDK的鸿蒙NEXT客户端库已发布
MobileIMSDK-鸿蒙端是一套基于鸿蒙Next(纯血鸿蒙)系统的IM即时通讯客户端库: 1)超轻量级(编译后库文件仅50KB)、无任何第3方库依赖(开箱即用); 2)纯ArkTS编写、无Native代码、高度提炼、简单易用; 3)基于鸿蒙Next标准WebSocket API,简洁优雅; 4)可运行于任何支持鸿蒙Next的平台; 5)能与 MobileIMSDK的各种客户端完美互通; 6)可应用于鸿蒙Next中的消息推送、客服聊天、企业OA、IM等场景。
426 45
|
12月前
|
存储 消息中间件 缓存
支持百万人超大群聊的Web端IM架构设计与实践
本文将回顾实现一个支持百万人超大群聊的Web端IM架构时遇到的技术挑战和解决思路,内容包括:通信方案选型、消息存储、消息有序性、消息可靠性、未读数统计。希望能带给你启发。
493 0
支持百万人超大群聊的Web端IM架构设计与实践
poi.openxml4j.exceptions.InvalidFormatException: Package should contain a content type part [M1.13]
poi.openxml4j.exceptions.InvalidFormatException: Package should contain a content type part [M1.13]
911 0
|
缓存 Java 数据库连接
Spring框架中的事件机制:深入理解与实践
Spring框架是一个广泛使用的Java企业级应用框架,提供了依赖注入、面向切面编程(AOP)、事务管理、Web应用程序开发等一系列功能。在Spring框架中,事件机制是一种重要的通信方式,它允许不同组件之间进行松耦合的通信,提高了应用程序的可维护性和可扩展性。本文将深入探讨Spring框架中的事件机制,包括不同类型的事件、底层原理、应用实践以及优缺点。
394 8
|
域名解析 网络协议 安全
DNS查询工具简介
DNS查询工具简介
8437 4