本文编写时援用了“聊聊IM零碎的即时性和可靠性”一文的局部内容和图片,感激原作者。

1、引言

上一篇《零根底IM开发入门(二):什么是IM零碎的实时性?》讲到了IM零碎的“立足”之本——“实时性”这个技术特色,本篇次要解说IM零碎中的“可靠性”这个话题,内容尽量做到只讲原理不深刻开展,避开深层次的技术性探讨,确保通俗易懂。


浏览对象:本系列文章次要浏览对象为零IM根底的开发者或产品经理,指标是通知你“IM零碎是什么?”,尽量不深入探讨具体的技术实现,确保通俗易懂,老少皆宜。

如您想从技术维度零碎学习IM技术并着手自已的IM开发(即解决“IM零碎要怎么做?”这个疑难),请从此文开始:《新手入门一篇就够:从零开发挪动端IM》。

学习交换:

  • 即时通讯/推送技术开发交换5群:215477170[举荐]
  • 挪动端IM开发入门文章:《新手入门一篇就够:从零开发挪动端IM》
  • 开源IM框架源码:https://github.com/JackJiang2011/MobileIMSDK

(本文同步公布于:http://www.52im.net/thread-3182-1-1.html)

2、系列文章

《零根底IM开发入门(一):什么是IM零碎?》
《零根底IM开发入门(二):什么是IM零碎的实时性?》
《零根底IM开发入门(三):什么是IM零碎的可靠性?》(* 本文)
《零根底IM开发入门(四):什么是IM零碎的音讯时序一致性?》
《零根底IM开发入门(五):什么是IM零碎的安全性? (稍后公布)》
《零根底IM开发入门(六):什么是IM零碎的的心跳机制? (稍后公布)》
《零根底IM开发入门(七):如何了解并实现IM零碎音讯未读数? (稍后公布)》
《零根底IM开发入门(八):如何了解并实现IM零碎的多端音讯漫游? (稍后公布)》

3、注释概述

一般来说,IM零碎的音讯“可靠性”,通常就是指聊天音讯投递的可靠性(精确的说,这个“音讯”是狭义的,因为还存用户看不见的各种指令,为了艰深,统称“音讯”)。

从用户行为来讲,音讯“可靠性”应该分为两种类型:

  • 1)在线音讯的可靠性:即发送音讯时,接管方以后处于“在线”状态;
  • 2)离线音讯的可靠性:即发送音讯时,接管方以后处于“离线”状态。

从具体的技术体现来讲,音讯“可靠性”蕴含两层含意:

  • 1)音讯不丢:这很直白,收回去的音讯不能像进了黑洞一样,一脸懵逼可不行;
  • 2)音讯不重:这是丢音讯的背面,音讯反复了也不能容忍。

对于“音讯不丢”这个特色来说,细化下来,它又蕴含两重含意:

  • 1)已明确被对方收到;
  • 2)已明确未被对方收到。

是的,对于第1)重含意好了解,第2)重含意的意思是:当对方没有胜利收到时,你的im零碎也必须要感知到,否则,它同样属于被“丢”领域。

总之,一个成型的im零碎,必须蕴含这两种音讯“可靠性”逻辑,能力堪用,缺一不可。

音讯的可靠性(不失落、不反复)无疑是IM零碎的重要指标,也是IM零碎实现中的难点之一。本文以下文字,将从在线音讯的可靠性和离线音讯的可靠性进行探讨。

4、典型的在线音讯收发流程

先看上面这张典型的im音讯收发流程:

是的,这是一个典型的服务端中转型IM架构。

所谓“服务端中转型IM架构”是指:一条音讯从客户端A收回后,须要先通过 IM 服务器来进行直达,而后再由 IM 服务器推送给客户端B,这种模式也是目前最常见的 IM 零碎的音讯散发架构。

你可能会说,IM不能够是P2P模式的吗?是的,目前来说支流IM根本都是服务器直达这种形式,P2P模式在IM零碎中用的很少。

起因是以下两个很显著的弊病:

  • 1)P2P模式下,IM运营者很容易被用户架空(无奈监管到用户行为,用户涉黄了怕不怕?);
  • 2)P2P模式下,群聊这种业务状态,很难实现(我要在千人群中发消息给,不可能我自已来散发1000次吧)。

话题有点跑偏,咱们回到正题:在下面这张图里,客户A发送音讯到服务端、服务端直达音讯给客户B,假如这两条数据链接中应用的通信协议是TCP,你认为在TCP所谓牢靠传输协定加持下,真的能保障IM聊天音讯的可靠性吗?

答案是否定的。咱们持续看下节。

5、TCP并不能保障在线音讯的“可靠性”

接上节,在一个典型的服务端中转型IM架构中,即便应用“牢靠的传输协定”TCP,也不能保障聊天音讯的可靠性。为什么这么说?

要答复这个问题,网上的很多文章,都会从服务端的角度举例:比方音讯发送时操作系统解体、网络闪断、存储故障等等,总之很形象,不太容易了解。

这次咱们从客户端角度来了解,为什么应用了牢靠传输协定TCP的状况下IM聊天音讯依然不牢靠的问题。

具体来说:如何确保 IM 音讯的可靠性是个绝对简单的话题,从客户端发送数据到服务器,再从服务器送达指标客户端,最终在 UI 胜利展现,其间波及的环节很多,这里只取其中一环「接收端如何确保音讯不失落」来探讨,粗略聊下我接触过的两种设计思路。

说到牢靠送达:第一反馈会联想到 TCP 的可靠性。数据的牢靠送达是个通用性的问题,无论是网络二进制流数据,还是下层的业务数据,都有可靠性保障问题,TCP 作为网络基础设施协定,其可靠性设计的可靠性是毋庸置疑的,咱们就从 TCP 的可靠性说起。

在 TCP 这一层:所有 Sender 发送的数据,每一个 byte 都有标号(Sequence Number),每个 byte 在到达接收端之后都会被接收端返回一个确认信息(Ack Number), 二者关系为 Ack = Seq + 1。简略来说,如果 Sender 发送一个 Seq = 1,长度为 100 bytes 的包,那么 receiver 会返回一个 Ack = 101 的包,如果 Sender 收到了这个Ack 包,阐明数据的确被 Receiver 收到了,否则 Sender 会采取某种策略重发下面的包。

第一个问题是:既然 TCP 自身是具备可靠性的,为什么还会呈现音讯接收端(Receiver)失落音讯的状况?

看下图高深莫测:

(▲ 上图援用自《从客户端的角度来谈谈挪动端IM的音讯可靠性和送达机制》)

一句话总结上图的含意:网络层的可靠性不等同于业务层的可靠性。

数据牢靠到达网络层之后,还须要一层层往上移交解决,可能的解决有:

  • 1)安全性校验;
  • 2)binary 解析;
  • 3)model 创立;
  • 4)写 db;
  • 5)存入 cache;
  • 6)UI 展现;
  • 7)以及一些边界问题:比方断网、用户忽然退出登陆、磁盘已满、内存溢出、app奔溃、忽然关机等等。

我的项目的性能个性越多,网络层往上的解决出错的可能性就越大。

举个最简略的场景为例子:音讯牢靠到达网络层之后,写 db 之前 IM APP 解体(不稀奇,是 App 都有解体的可能),尽管数据在网络层牢靠到达了,但没存进 db,下次用户关上 App 音讯天然就失落了,如果不在业务层再减少可靠性保障(比方:前面要提到的网络层面的音讯重发保障),那么意味着这条音讯对于接收端来说就永远失落了,也就天然不存在“可靠性”了。

从客户端角度了解IM的可能性以及解决办法,能够具体浏览:《从客户端的角度来谈谈挪动端IM的音讯可靠性和送达机制》,本节援用的是该文中“4、TCP协定的可靠性之外还会呈现音讯失落?”一节的文字。

6、为在线音讯减少“可靠性”保障

那么怎么在应用层减少可靠性保障呢?

有一个现成的机制可供咱们借鉴:TCP协定的超时、重传、确认机制。

具体来说就是:

  • 1)在应用层结构一种ACK音讯,当接管方正确处理完音讯后,向发送方发送ACK;
  • 2)如果发送方在超时工夫内没有收到ACK,则认为音讯发送失败,须要进行重传或其余解决。

减少了确认机制的音讯收发过程如下:

咱们能够把整个过程分为两个阶段。

阶段1:clientA -> server

  • 1-1:clientA向server发送音讯(msg-Req);
  • 1-2:server收取音讯,回复ACK(msg-Ack)给clientA;
  • 1-3:一旦clientA收到ACK即可认为音讯已胜利投递,第一阶段完结。

无论msg-A或ack-A失落,clientA均无奈在超时工夫内收到ACK,此时能够提醒用户发送失败,手动进行重发。

阶段2:server -> clientB

  • 2-1:server向clientB发送音讯(Notify-Req);
  • 2-2:clientB收取音讯,回复ACK(Notify-ACk)给server;
  • 2-3:server收到ACK之后将该音讯标记为已发送,第二阶段完结。

无论msg-B或ack-B失落,server均无奈在超时工夫内收到ACK,此时须要重发msg-B,直到clientB返回ACK为止。

对于IM聊天音讯的可靠性保障问的深刻探讨,能够详读:《IM音讯送达保障机制实现(一):保障在线实时音讯的牢靠投递》,该文会深刻探讨这个话题。

7、典型的离线音讯收发流程

说完在线音讯的“可靠性”问题,咱们该理解一下离线音讯了。

7.1 离线音讯的收发也存在“不可靠性”

下图是一张典型的IM离线音讯流程图:

如上图所示,和在线音讯收发流程相似。

离线音讯收发流程也可划分为两个阶段:

阶段1:clientA -> server

  • 1-1:clientA向server发送音讯(msg-Req) ;
  • 1-2:server发现clientB离线,将音讯存入offline-DB。

阶段2:server -> clientB

  • 2-1:clientB上线后向server拉取离线音讯(pull-Req) ;
  • 2-2:server从offline-DB检索相应的离线音讯推送给clientB(pull-res),并从offline-DB中删除。

显然:离线音讯收发过程同样存在音讯失落的可能性。

举例来说:假如pull-res没有胜利送达clientB,而offline-DB中已删除,这部分离线音讯就彻底失落了。

7.2 离线音讯的“可靠性”保障

与在线音讯收发流程相似,咱们同样须要在应用层减少可靠性保障机制。

下图是减少了可靠性保障后的离线音讯收发流程:

与初始的离线音讯收发流程相比,上图减少了1-3、2-4、2-5步骤:

  • 1-3:server将音讯存入offline-DB后,回复ACK(msg-Ack)给clientA,clientA收到ACK即可认为音讯投递胜利;
  • 2-4:clientB收到推送的离线音讯,回复ACK(res-Ack)给server;
  • 2-5:server收到res-ACk后确定离线音讯已被clientB胜利收取,此时能力从offline-DB中删除。

当然,上述的保障机制,还存在性能优化空间。

当离线音讯的量较大时:如果对每条音讯都回复ACK,无疑会大大增加客户端与服务器的通信次数。这种状况咱们通常应用批量ACK的形式,对多条音讯仅回复一个ACK。在某尔后IM的实现中是将所有的离线音讯按会话进行分组,每组回复一个ACK,如果某个ACK失落,则只须要重传该会话的所有离线音讯。

无关离线音讯的可靠性保障机制的具体探讨,能够详读:《IM音讯送达保障机制实现(二):保障离线音讯的牢靠投递》、《IM开发干货分享:如何优雅的实现大量离线音讯的牢靠投递》,这两篇文章能够给你更深刻具体的答案。

8、聊天音讯反复的问题

下面章节中,通过在应用层退出重传、确认机制后,咱们的确是杜绝了音讯失落的可能性。

但因为重试机制的存在,咱们会遇到一个新的问题:那就是同一条音讯可能被反复发送。

举一个最简略的例子:假如client胜利收到了server推送的音讯,但其后续发送的ACK失落了,那么server将会在超时后再次推送该音讯,如果业务层不对反复音讯进行解决,那么用户就会看到两条齐全一样的音讯。

音讯去重的形式其实非常简单,个别是依据音讯的惟一标记(id)进行过滤。

具体过程在服务端和客户端可能有所不同:

  • 1)客户端 :咱们能够通过结构一个map来保护已接管音讯的id,当收到id反复的音讯时间接抛弃;
  • 2)服务端 :收到音讯时依据id去数据库查问,若库中已存在则不进行解决,但依然须要向客户端回复Ack(因为这条音讯很可能来自用户的手动重发)。

对于音讯的去重问题,在一对一聊天的状况下,逻辑并不简单,但在群聊模式下,会将问题复杂化,无关群聊音讯不丢和去重的具体探讨,能够深刻浏览:《IM群聊音讯如此简单,如何保障不丢不重?》。

9、本文小结

保障音讯的可靠性是IM零碎设计中很重要的一环,能不能做到“音讯不丢”、“音讯不重”,对用户的体验影响极大。

所谓“牢靠的传输协定”TCP也并不能保障音讯在应用层的可靠性。

咱们个别通过在应用层的ACK应答和重传机制,来实现IM音讯的可靠性保障。但由此带来的音讯反复问题,须要咱们额定进行解决,最简略的办法就是通过音讯ID进行幂等去重。

对于IM零碎音讯可靠性的实践根底,咱们就探讨到这里,有疑难的读者,能够在本文开端注意,欢送踊跃探讨。

10、参考资料

[1] IM音讯送达保障机制实现(一):保障在线实时音讯的牢靠投递

[2] IM音讯送达保障机制实现(二):保障离线音讯的牢靠投递

[3] IM开发干货分享:如何优雅的实现大量离线音讯的牢靠投递

[4] 从客户端的角度来谈谈挪动端IM的音讯可靠性和送达机制

[5] 聊聊IM零碎的即时性和可靠性

[6] 学习笔记4——IM零碎如何保障音讯的可靠性

[7] IM群聊音讯如此简单,如何保障不丢不重?

附录:更多IM开发热门技术点

《挪动端IM开发者必读(一):通俗易懂,了解挪动网络的“弱”和“慢”》
《挪动端IM开发者必读(二):史上最全挪动弱网络优化办法总结》
《古代挪动端网络短连贯的优化伎俩总结:申请速度、弱网适应、平安保障》
《挪动端IM中大规模群音讯的推送如何保障效率、实时性?》
《挪动端IM开发须要面对的技术问题》
《开发IM是本人设计协议用字节流好还是字符流好?》
《请问有人晓得语音留言聊天的支流实现形式吗?》
《如何保障IM实时音讯的“时序性”与“一致性”?》
《一个低成本确保IM音讯时序的办法探讨》
《IM单聊和群聊中的在线状态同步应该用“推”还是“拉”?》
《IM群聊音讯如此简单,如何保障不丢不重?》
《谈谈挪动端 IM 开发中登录申请的优化》
《挪动端IM登录时拉取数据如何作到省流量?》
《浅谈挪动端IM的多点登录和音讯漫游原理》
《齐全自已开发的IM该如何设计“失败重试”机制?》
《微信对网络影响的技术试验及剖析(论文全文)》
《IM开发基础知识补课(五):通俗易懂,正确理解并用好MQ音讯队列》
《微信技术分享:微信的海量IM聊天音讯序列号生成实际(算法原理篇)》
《IM开发基础知识补课(六):数据库用NoSQL还是SQL?读这篇就够了!》
《IM里“左近的人”性能实现原理是什么?如何高效率地实现它?》
《IM的扫码登录性能如何实现?一文搞懂支流利用的扫码登录技术原理》
《IM音讯ID技术专题(一):微信的海量IM聊天音讯序列号生成实际(算法原理篇)》
《IM音讯ID技术专题(二):微信的海量IM聊天音讯序列号生成实际(容灾计划篇)》
《IM音讯ID技术专题(三):解密融云IM产品的聊天音讯ID生成策略》
《IM音讯ID技术专题(四):深度解密美团的分布式ID生成算法》
《IM音讯ID技术专题(五):开源分布式ID生成器UidGenerator的技术实现》
《IM音讯ID技术专题(六):深度解密滴滴的高性能ID生成器(Tinyid)》
《IM开发宝典:史上最全,微信各种性能参数和逻辑规定材料汇总》

本文已同步公布于“即时通讯技术圈”公众号。

▲ 本文在公众号上的链接是:点此进入,原文链接是:http://www.52im.net/thread-3182-1-1.html