2026/4/17 1:14:29
网站建设
项目流程
品牌营销网站,wordpress侧边栏,行政助手网站开发,微信小程序注册流程本文引用了45岁老架构师尼恩的技术分享#xff0c;有修订和重新排版。
1、引言
分布式IM聊天系统中#xff0c;IM消息怎么做到不丢、不重、还按顺序到达#xff1f;
这个问题#xff0c;涉及到IM系统的两个核心#xff1a;
1#xff09;消息不能丢#xff08;可靠性…本文引用了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 就是这条消息的“官方身份证号”客户端只认这个不看接收时间。这就像电影院检票——你可以早到晚到但座位按票号定。哪怕后排观众先进场也不会坐到前排去。PSIM消息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客户端排序逻辑最后一步客户端收尾别急着渲染先排好队。//这里是伪代码 先排序ListMsg 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聊天系统的可靠性[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-4887-1-1.html