本文引用了45岁老架构师尼恩的技术分享,有修订和重新排版。
1、引言
分布式IM聊天系统中,IM消息怎么做到不丢、不重、还按顺序到达?
这个问题,涉及到IM系统的两个核心:
***1)***消息不能丢(可靠性):比如用户点了发送,不能因为服务宕机或网络抖动,消息石沉大海。比如地铁隧道、电梯间,网络断了又连,消息不能卡住不动(要确保弱网也能用)。
***2)***顺序不能乱(有序性):比如"在吗?" 回成 "吗在?",群聊时间线错乱,体验直接崩盘。
这二大痛点,是IM聊天系统架构的命门所在。
下面是一张IM消息从发出到接收的关键路径:

2、系列文章
为了更好以进行内容呈现,本文拆分两了上下两篇。
本文是2篇文章中的第 1 篇:
- 《如何保障分布式IM聊天系统的消息有序性(即消息不乱)》(☜ 本文)
- 《如何保障分布式IM聊天系统的消息可靠性(即消息不丢)》(稍后发布..)
本篇主要总结和分享分布式IM聊天系统架构中关于消息有序性的设计和实践。
3、传统技术方案的瓶颈,怎么破?
早期做消息有序,很多人第一反应是搞个"全局发号器"------所有消息排一队,挨个编号再发。
理想很丰满,现实很骨感:高并发下一拥而上抢号,发号器直接被打满;更致命的是,它一旦宕机,全链路雪崩。
这就像春运火车站只开一个售票窗------再快也撑不过三分钟。
所以,我们必须换思路:不搞大一统,而是分片独立发号,让每个"窗口"自给自足,互不干扰。
4、痛点拆解:为什么消息会乱?
我们先还原一个真实场景: 想象一下你和朋友聊天:
你说:"1 吃饭了吗?"
他回:"2 刚吃完。"
你又说:"3 吃啥呢?"
结果对方手机上显示成:
"3 吃啥呢?" → "1 吃饭了吗?" → "2 刚吃完。"
这不是 bug,是分布式系统的常态。
三条消息走不同服务节点、经不同网络路径,到达时间完全不可控,最终呈现顺序错乱。
会乱 问题本质是什么?一个要"串行等",一个想"并发冲",天然冲突。
**这时候有人会说:**那我加个全局排序服务不就行了?
可以,但代价太大------一个中心节点最多撑几万 QPS,面对百万群聊、亿级用户,还没上线就已过载。
所以,全局有序不是解,而是枷锁。我们要的不是"天下大同",而是"各聊各的别乱就行"。
5、最终方案:分而治之 + 局部有序
**真正的突破口在于:**我们根本不需要全局有序,只需要"会话内有序"。
你和张三的聊天记录不能乱,但你和李四的聊天跟王五的完全无关------何必放一起排序?
**这就引出了经典策略:**分而治之 + 局部有序。
具体怎么做?两步走稳:
**** 第一步 - 业务分区:***哈希分片,锁定归属
用 sessionId 做一致性哈希,确保同一个会话的所有消息始终路由到同一个处理节点。按"会话ID"做哈希,算出该消息该由哪个节点处理。同一会话 → 哈希值一样 → 路由到同一台机器 → 所有消息串行处理,天然避免跨节点乱序。
这样一来,单个会话内的消息在服务端就是串行处理的,天然不会乱。
**** 第二步 - 局部序号:***独立发号,局部递增
每个会话独立维护一个计数器,每来一条消息就+1,作为它的"官方序号"。每个会话,可以配一个独立计数器(比如 Redis 的 INCR),每来一条消息就+1,生成唯一 SEQ。客户端不管什么时候收到消息,只认这个序号,按序号从小到大排列展示。
这个 SEQ 就是这条消息的"官方身份证号",客户端只认这个,不看接收时间。这就像电影院检票------你可以早到晚到,但座位按票号定。哪怕后排观众先进场,也不会坐到前排去。
**PS:IM消息ID生成相关的文章可详细阅读以下资料:
- 《IM消息ID技术专题(一):微信的海量IM聊天消息序列号生成实践(算法原理篇)》
- 《IM消息ID技术专题(二):微信的海量IM聊天消息序列号生成实践(容灾方案篇)》
- 《IM消息ID技术专题(三):解密融云IM产品的聊天消息ID生成策略》
- 《IM消息ID技术专题(四):深度解密美团的分布式ID生成算法》
- 《IM消息ID技术专题(五):开源分布式ID生成器UidGenerator的技术实现》
- 《IM消息ID技术专题(六):深度解密滴滴的高性能ID生成器(Tinyid)》
- 《IM消息ID技术专题(七):深度解密vivo的自研分布式ID服务(鲁班)》
6、实践落地(核心片段伪代码)
1)服务端分片路由逻辑:
**来看关键实现:**如何把消息精准投递给"对的人"。
String sessionId = msg.getSessionId();
//这里是伪代码,实际代码以mq 的负载均衡机制为准
int nodeIndex = Math.abs(sessionId.hashCode()) % clusterNodeCount;
//这里写个伪代码,代表mq 主从复制
ClusterNode targetNode = clusterNodes.get(nodeIndex);
targetNode.sendMsg(msg);
**核心就一句:**基于会话 ID 哈希取模,固定路由。
从此,每个会话都有了自己的"专属服务通道",不再受其他会话影响。
2)服务端序号分配逻辑:
接下来,给每条消息发"通行证":
long msgSeq = redis.incr("msg_seq_" + sessionId);
msg.setSeq(msgSeq);
msg.setUniqueKey(sessionId + "_" + msgSeq);
这里用了 Redis 的 INCR,保证同一个会话下的 SEQ 绝对递增,且线程安全。同时用 sessionId_seq 作为唯一键,既能幂等去重,也能防止重试导致消息重复入库。
实战提示:
如果你的 Redis 是集群模式,记得确保同一个会话的 key 落在同一 slot,否则 INCR 可能跨节点失效。
3)客户端排序逻辑:
**最后一步,客户端收尾:**别急着渲染,先排好队。
//这里是伪代码, 先排序
List<Msg> sortedMsgs = msgList.stream()
.sorted(Comparator.comparingLong(Msg::getSeq))
.collect(Collectors.toList());
//这里是伪代码, 再渲染
renderMsgList(sortedMsgs);
无论消息以什么顺序到达,统统按 seq 升序排列后再上屏。哪怕第100条先到,第1条后到,也能正确归位。这也是为什么我们强调"客户端必须信任服务端 SEQ"------它是唯一真相源。
7、方案总结:放弃全局有序,换高可用与高性能
总结一下,这套方案的核心思想就一句话:
不要为"假需求"买单------我们不需要全局有序,只需要业务上有意义的有序。
你看微信、钉钉、飞书,哪一个是把全平台消息排成一条队列的?没有。
它们都选择了"会话级隔离 + 局部有序"的设计,这才是工业级系统的通用解法。
背后的分布式哲学也很清晰:

最终换来的是:
- *1)*高并发支持(水平扩展);
- *2)*高可用(无单点);
- *3)*强一致体验(用户无感知)。
这正是中高级开发者必须掌握的权衡思维:
不是技术做不到,而是要不要做。
有时候,"不做全局有序",反而是最正确的选择。
8、 IM消息有序性架构的核心流程总结
最后,一张图串起全流程:

从发起到渲染,全程围绕"会话隔离"和"局部发号"展开。每一个环节都在为同一个目标服务:在分布式环境下,低成本实现用户可感知的"顺序正确"。
------ 下篇 《如何保障分布式IM聊天系统的消息可靠性(即消息不丢)》稍后发布,敬请期待 ------
9、参考资料
1\] [什么是IM聊天系统的可靠性?](http://www.52im.net/thread-3182-1-1.html "什么是IM聊天系统的可靠性?") \[2\] [什么是IM聊天系统的消息时序一致性?](http://www.52im.net/thread-3189-1-1.html "什么是IM聊天系统的消息时序一致性?") \[3\] [微信技术分享:微信的海量IM聊天消息序列号生成实践(算法原理篇)](http://www.52im.net/thread-1998-1-1.html "微信技术分享:微信的海量IM聊天消息序列号生成实践(算法原理篇)") \[4\] [马蜂窝旅游网的IM系统架构演进之路](http://www.52im.net/thread-2675-1-1.html "马蜂窝旅游网的IM系统架构演进之路") \[5\] [一套亿级用户的IM架构技术干货(下篇):可靠性、有序性、弱网优化等](http://www.52im.net/thread-3445-1-1.html "一套亿级用户的IM架构技术干货(下篇):可靠性、有序性、弱网优化等") \[6\] [从新手到专家:如何设计一套亿级消息量的分布式IM系统](http://www.52im.net/thread-3472-1-1.html "从新手到专家:如何设计一套亿级消息量的分布式IM系统") \[7\] [企业微信的IM架构设计揭秘:消息模型、万人群、已读回执、消息撤回等](http://www.52im.net/thread-3631-1-1.html "企业微信的IM架构设计揭秘:消息模型、万人群、已读回执、消息撤回等") \[8\] [融云技术分享:全面揭秘亿级IM消息的可靠投递机制](http://www.52im.net/thread-3638-1-1.html "融云技术分享:全面揭秘亿级IM消息的可靠投递机制") \[9\] [阿里IM技术分享(四):闲鱼亿级IM消息系统的可靠投递优化实践](http://www.52im.net/thread-3706-1-1.html "阿里IM技术分享(四):闲鱼亿级IM消息系统的可靠投递优化实践") \[10\] [阿里IM技术分享(八):深度解密钉钉即时消息服务DTIM的技术设计](http://www.52im.net/thread-4012-1-1.html "阿里IM技术分享(八):深度解密钉钉即时消息服务DTIM的技术设计") \[11\] [基于实践:一套百万消息量小规模IM系统技术要点总结](http://www.52im.net/thread-3752-1-1.html "基于实践:一套百万消息量小规模IM系统技术要点总结") \[12\] [一套分布式IM即时通讯系统的技术选型和架构设计](http://www.52im.net/thread-4564-1-1.html "一套分布式IM即时通讯系统的技术选型和架构设计") \[13\] [转转平台IM系统架构设计与实践(一):整体架构设计](http://www.52im.net/thread-4764-1-1.html "转转平台IM系统架构设计与实践(一):整体架构设计") \[14\] [移动端弱网优化专题(一):通俗易懂,理解移动网络的"弱"和"慢"](http://www.52im.net/thread-1587-1-1.html "移动端弱网优化专题(一):通俗易懂,理解移动网络的“弱”和“慢”") \[15\] [移动端弱网优化专题(二):史上最全移动弱网络优化方法总结](http://www.52im.net/thread-1588-1-1.html "移动端弱网优化专题(二):史上最全移动弱网络优化方法总结") \[16\] [Web端即时通讯实践干货:如何让你的WebSocket断网重连更快速?](http://www.52im.net/thread-3098-1-1.html "Web端即时通讯实践干货:如何让你的WebSocket断网重连更快速?") \[17\] [从客户端的角度来谈谈移动端IM的消息可靠性和送达机制](http://www.52im.net/thread-1470-1-1.html "从客户端的角度来谈谈移动端IM的消息可靠性和送达机制") \[18\] [IM消息送达保证机制实现(一):保证在线实时消息的可靠投递](http://www.52im.net/thread-294-1-1.html "IM消息送达保证机制实现(一):保证在线实时消息的可靠投递") \[19\] [移动端IM中大规模群消息的推送如何保证效率、实时性?](http://www.52im.net/thread-1221-1-1.html "移动端IM中大规模群消息的推送如何保证效率、实时性?") \[20\] [如何保证IM实时消息的"时序性"与"一致性"?](http://www.52im.net/thread-714-1-1.html "如何保证IM实时消息的“时序性”与“一致性”?") \[21\] [一个低成本确保IM消息时序的方法探讨](http://www.52im.net/thread-866-1-1.html "一个低成本确保IM消息时序的方法探讨") > **即时通讯技术学习:** > > - 移动端IM开发入门文章:《[新手入门一篇就够:从零开发移动端IM](http://www.52im.net/thread-464-1-1.html "新手入门一篇就够:从零开发移动端IM")》 > > - 开源IM框架源码:[https://github.com/JackJiang2011/MobileIMSDK](https://github.com/JackJiang2011/MobileIMSDK "https://github.com/JackJiang2011/MobileIMSDK")([备用地址点此](https://gitee.com/jackjiang/MobileIMSDK "备用地址点此")) (**本文已同步发布于:** [http://www.52im.net/thread-4887-1-1.html](http://www.52im.net/thread-4887-1-1.html "http://www.52im.net/thread-4887-1-1.html"))