本文做者“商文默”,有修订和改动。php
我整理的大量IM技术文章中(见本文末“参考资料”一节),有关消息可靠性和一致性问题的文章占了很大比重,缘由是IM这类系统抛开各类眼花缭乱的产品功能和技术特性,保证消息的可靠性和一致性几乎是IM产品必需的素质。html
试想若是一个IM连发出的消息都不知道对方到底能不能收到、发出的聊天内容对方看到的究竟是不是“胡言乱语”(严重乱序问题),这样的APP用户确定不会让他在手机上过夜(确定第一时间卸载了),由于最基本的聊天逻辑都没法实现,它已经失去了IM软件自己的意义。前端
不过,另外一个方面来说,IM系统是不标准的(虽然曾经XMPP这种协议试图解决这个问题,但事实证实那根本不现实),各家几乎都是自已的私有协议、不一样的实现逻辑,这也决定了即便同一个技术问题,对于IM来讲很难有固定的实现套路和标准的解决方案。git
因此,对于本文来讲,文中做者虽然提供了有关IM消息“可靠性”与“一致性”问题的解决方案,但方案到底合不合理、适不适合你,这就是仁者见仁、智者见智的事了。用人话说就是:本文内容仅供参考,具体的解决方案请务结合自已的系统构架和实现状况,多阅读几篇有关这个技术话题的文章,取其精华,找到适合自已的技术方案和思路才是最明智的。github
丛所周之,即时通信聊天(IM)系统必须要解决消息可靠性及消息一致性问题(**PS:**若是具体IM系统是什么你都还没弄明白,先读这篇《零基础IM开发入门(一):什么是IM系统?》)。算法
这两个问题,通俗来讲就是:服务器
本文会从典型的IM消息发送逻辑开始,简单易懂地阐明消息可靠性、一致性问题的原理及可参考的技术解决方法,或许技术方案并不完美,但但愿能为你的IM技术问题解决带来启发。微信
IM的消息发送通常的实现过程能够分为两个阶段:markdown
判断消息发送是否成功主要依据第一阶段——即服务器是否接受到消息。架构
对于消息发送者来讲,消息状态能够分为三类:
具体来讲,这三类状态的具体意义是:
对应的消息发送流程以下图所示:
限于篇幅,对于IM消息可靠性的基本概念和详细原理建议阅读《零基础IM开发入门(三):什么是IM系统的可靠性?》,本文着重谈谈解决思路。
保证消息发送第一阶段(见本文“三、典型IM消息发送过程”一节)消息成功发送的方法是设立重发机制:
PS: 具体的完整方案级代码实现,能够参考MobileIMSDK 中有关QoS机制的代码实现。
消息发送第二阶段(见本文“三、典型IM消息发送过程”一节)服务端推送消息到接收方,若是链接断开,会丢失消息。
因此要保证消息完整,就须要在创建链接后,根据上一条消息(已经 ACK)时间戳,获取会话记录,一次返回一段时间内全部消息(PS: 中大型应用中,消息的拉取也不是个简单事情,详情能够阅读《IM开发干货分享:如何优雅的实现大量离线消息的可靠投递》)。
另外一种保证方法是加入定时轮询,检查消息完整性,具体的思路以下图所示。
创建链接流程图:
消息重发、会话记录检查须要考虑两个问题:
举两个例子。
关于消息重发问题:
关于消息顺序问题:
同上节同样,对于IM消息一致性的基本概念和详细原理建议阅读《零基础IM开发入门(四):什么是IM系统的消息时序一致性?》。
对于消息重发问题,能够给每条消息增长属性 uuid 做为消息惟一标识,重发消息 uuid 不变,前端根据 uuid 去重。大体思路就是这样。
PS: 对于IM来讲,消息ID也是个很大的技术话题,有兴趣能够读下面这个系列:
《IM消息ID技术专题(一):微信的海量IM聊天消息序列号生成实践(算法原理篇)》
《IM消息ID技术专题(二):微信的海量IM聊天消息序列号生成实践(容灾方案篇)》
《IM消息ID技术专题(三):解密融云IM产品的聊天消息ID生成策略》
《IM消息ID技术专题(四):深度解密美团的分布式ID生成算法》
对于消息排序问题: 由于在聊天中,消息的顺序对于发送方的表述有重要的影响,消息不完整或顺序颠倒均可能形成语意不连贯,甚至曲解。因此须要保证发送方发送消息顺序,而会话双方消息排序须要考虑实际状况。
在通常的认知里: 状态是正在发送的消息,应该尚未被对方看到,只有发送成功的消息,才会被对方看到。但在实现中,消息发送成功是以服务器接收消息并返回 ACK 成功为判断依据,而不是被对方接收到。
那么就会出现这样一个问题: 若是一条消息状态是正在发送,此时收到一条消息,那么收到的消息是在正在发送的消息以前仍是以后?
这是一个上下文关系,关键问题是:发送方是以哪条所见消息为依据发送消息的。
这里提供一种思路: 借鉴分布式系统中的向量时钟算法(见《分布式系统中的向量时钟算法》)。
先简单描述向量时钟算法:
向量时钟算法用于在分布式系统中生成事件偏序关系,并纠正因果关系。一个系统包含 N 个节点,每一个节点产生的消息体中包含该节点的逻辑时钟,总体系统的向量时钟由 N 维逻辑时钟组成,并在每一个节点产生的消息体中传递。
简单来讲,向量时钟算法的实现原理以下:
针对上述的第5)点:
偏序关系: 若是 A 向量中的每一维都大于等于 B 向量,则 A、B 之间存在偏序关系,不然不存在偏序关系。
对于IM为聊天消息排序来讲,其实就是处理聊天消息的上下文语境,决定消息之间的因果关系。
参考向量时钟算法: 假设有 N 个消息会话方,系统的向量时钟由 N 维时钟组成,向量时钟在各方发送的消息体中传递,并依据向量时钟排序。
具体实现思路以下:
针对上述第4)点:
向量时钟在理论上能够解决大部分消息一致性的问题,但在实现中还须要考虑实际使用时的体验。
这其中最须要关注的问题是: 是否要强制排序,或者说,若是实际显示顺序和向量时钟之间的偏序关系不一致,是否要移动消息之间的顺序。
举个例子: 在一个有多人的会话中,若是有一方网速特别慢,收不到消息,也发不出消息。在他看到的最后的消息以后,其余人已经开始新的话题,这时他关于上一个话题的消息终于发送成功,并被其余人收到。
此时就存在这样一个问题: 这条关于上一个话题的消息是显示在最后,仍是移到较早时间?
IM 的场景不少,也很复杂,更多的时候须要从产品角度考虑问题。
对于消息是否须要排序的问题,这里只提出一个比较通用的方案: 建议会话中不强制排序,会话历史记录中按照向量时钟的偏序关系进行排序。
对于 IM 系统消息可靠性及一致性问题,经过消息重发机制保证消息成功被服务端接收,经过会话记录检查保证收取消息完整,从而保证整个消息发送过程的可靠性。使用 uuid 消息去重,参考向量时钟算法进行消息排序,为保证消息一致性提供一种解决方案。
总之,IM这类系统看似简单,实则水深似海,若是你是IM开发新手,能够从《新手入门一篇就够:从零开发移动端IM》这篇入手系统学习。若是你自认为已经是IM老手,这里整理的 IM中大型架构设计 方面的文章或许能够参考一下。
[2] 零基础IM开发入门(四):什么是IM系统的消息时序一致性?
[3] IM消息送达保证机制实现(一):保证在线实时消息的可靠投递
[4] IM消息送达保证机制实现(二):保证离线消息的可靠投递
[9] IM开发干货分享:如何优雅的实现大量离线消息的可靠投递
[10] 从客户端的角度来谈谈移动端IM的消息可靠性和送达机制
本文已同步发布于**:** www.52im.net/thread-3574…