1、前言

互联网发展至今,IM(即时通讯聊天应用)一直是互联网上最为成功也是最为平常的应用类型。尤其现今的移动互联网时代,因即时通讯技术的发展和普及,IM这种即时通讯应用已乎达成了各即时通讯应用运营者梦寐已求的所谓“全时在线”,而这种“全时在线”及其应用体验的背后,回归到技术本质就是各种行为消息(或者说信息)的实时性、必达性。

本文将要讨论的是即时IM应用中极其重要但也不被用户感知的消息送达保证机制(即QoS机制),文中将给出目前主流的参考实现思路。

2、IM开发干货系列文章

  • 《IM消息送达保证机制实现(二):保证离线消息的可靠投递》
  • 《如何保证IM实时消息的“时序性”与“一致性”?》

3、本文概述

消息的可靠性,即消息的不丢失和不重复,是IM系统中的一个难点。当初QQ在技术上(当时叫OICQ)因为以下两点原因才打败了ICQ:

  • QQ的消息投递可靠(消息不丢失,不重复);
  • QQ的垃圾消息少(它antispam做得好,这也是一个难点,但不是本文重点讨论的内容)。

今天,本文将用十分通俗的语言,来讲述IM系统中消息可靠性的问题。

4、报文类型

IM的客户端与服务器通过发送报文(也就是请求包)来完成消息的传递。

报文分为三种:

  • 请求报文(request,后简称为为R);
  • 应答报文(acknowledge,后简称为A);
  • 通知报文(notify,后简称为N)。

这三种报文的解释如下:

  • R:客户端主动发送给服务器的报文
  • A:服务器被动应答客户端的报文,一个A一定对应一个R
  • N:服务器主动发送给客户端的报文

5、普通消息投递流程

用户A给用户B发送一个“你好”,很容易想到,流程如下:

  • client-A向im-server发送一个消息请求包,即msg:R
  • im-server在成功处理后,回复client-A一个消息响应包,即msg:A
  • 如果此时client-B在线,则im-server主动向client-B发送一个消息通知包,即msg:N(当然,如果client-B不在线,则消息会存储离线)

6、上述消息投递流程出现的问题

从流程图中容易看到,发送方client-A收到msg:A后,只能说明im-server成功接收到了消息,并不能说明client-B接收到了消息。在若干场景下,可能出现msg:N包丢失,且发送方client-A完全不知道,例如:

  • 服务器崩溃,msg:N包未发出
  • 网络抖动,msg:N包被网络设备丢弃
  • client-B崩溃,msg:N包未接收

结论是悲观的:接收方client-B是否有收到msg:N,发送方client-A完全不可控,那怎么办呢?

7、应用层确认+im消息可靠投递的六个报文

我们来参考网络传输协议的实现:UDP是一种不可靠的传输层协议,TCP是一种可靠的传输层协议,TCP是如何做到可靠的?答案是:超时、重传、确认。(即时通讯网注:实际上IM中,数据通讯层无论用的是UDP还是TCP协议,都同样需要消息送达保证(即QoS)机制,原因在于IM的通信是A端-Server-B端的3方通信,而非传统C/S或B/S这种2方通信)。

要想实现应用层的消息可靠投递,必须加入应用层的确认机制,即:要想让发送方client-A确保接收方client-B收到了消息,必须让接收方client-B给一个消息的确认,这个应用层的确认的流程,与消息的发送流程类似:

  • client-B向im-server发送一个ack请求包,即ack:R
  • im-server在成功处理后,回复client-B一个ack响应包,即ack:A
  • 则im-server主动向client-A发送一个ack通知包,即ack:N

至此,发送“你好”的client-A,在收到了ack:N报文后,才能确认client-B真正接收到了“你好”。

你会发现,一条消息的发送,分别包含(上)(下)两个半场,即msg的R/A/N三个报文,ack的R/A/N三个报文。一个应用层即时通讯消息的可靠投递,共涉及6个报文,这就是im系统中消息投递的最核心技术(如果某个im系统不包含这6个报文,不要谈什么消息的可靠性)。

8、可靠消息投递存在什么问题

期望六个报文完成消息的可靠投递,但实际情况下:

  • msg:R,msg:A 报文可能丢失:
    此时直接提示“发送失败”即可,问题不大;
  • msg:N,ack:R,ack:A,ack:N这四个报文都可能丢失:
    (原因如第二章所述,可能是服务器奔溃、网络抖动、或者客户端奔溃),此时client-A都收不到期待的ack:N报文,即client-A不能确认client-B是否收到“你好”。

那怎么办呢?

9、消息的超时与重传

client-A发出了msg:R,收到了msg:A之后,在一个期待的时间内,如果没有收到ack:N,client-A会尝试将msg:R重发。可能client-A同时发出了很多消息,故client-A需要在本地维护一个等待ack队列,并配合timer超时机制,来记录哪些消息没有收到ack:N,以定时重发。

一旦收到了ack:N,说明client-B收到了“你好”消息,对应的消息将从“等待ack队列”中移除。

10、消息的重传存在什么问题

第五节提到过,msg:N报文,ack:N报文都有可能丢失:

  • msg:N 报文丢失:说明client-B之前压根没有收到“你好”报文,超时与重传机制十分有效
  • ack:N 报文丢失:说明client-B之前已经收到了“你好”报文(只是client-A不知道而已),超时与重传机制将导致client-B收到重复的消息。

启示:

平时使用qq,或许大伙都有类似的体验,弹出一个对话框“因为网络原因,消息发送失败,是否要重发”,此时,有可能是对方没有收到消息(发送方网络不好,msg:N丢失),也可能已经收到了消息(接收方网络不好,反复重传后,ack:N依然丢失),出现这个提示时,大伙不妨和对端确认一下,看是哪种情况。

11、消息的去重

解决方法也很简单,由发送方client-A生成一个消息去重的msgid,保存在“等待ack队列”里,同一条消息使用相同的msgid来重传,供client-B去重,而不影响用户体验。

12、其他

1)上述设计理念,由客户端重传,可以保证服务端无状态性(架构设计基本准则);
2)如果client-B不在线,im-server保存了离线消息后,要伪造ack:N发送给client-A;
3)离线消息的拉取,为了保证消息的可靠性,也需要有ack机制,但由于拉取离线消息不存在N报文,故实际情况要简单的多,即先发送offline:R报文拉取消息,收到offline:A后,再发送offlineack:R删除离线消息。

13、总结

1)im系统是通过超时、重传、确认、去重的机制来保证消息的可靠投递,不丢不重;
2)切记,一个“你好”的发送,包含上半场msg:R/A/N与下半场ack:R/A/N的6个报文。

个人消息是一个1对1的ack,群消息就没有这么简单了,群消息存在一个扩散系数,im群消息的可靠投递问题感兴趣的可查阅相关资料。

网易云信,你身边的即时通讯和音视频技术专家,了解我们,请戳网易云信官网

想要行业洞察和技术干货,请关注网易云信博客

本文转载自52im,作者:JackJiang

IM消息送达保证机制实现(一):保证在线实时消息的可靠投递相关推荐

  1. IM消息送达保证机制实现(二):保证离线消息的可靠投递

    1.前言 本文的上篇<IM消息送达保证机制实现(一):保证在线实时消息的可靠投递>中,我们讨论了在线实时消息的投递可以通过应用层的确认.发送方的超时重传.接收方的去重等手段来保证业务层面消 ...

  2. IM消息送达保证机制实现

    一.保证在线实时消息的可靠投递 1.报文类型 报文分为三种: 请求报文(request,后简称为为R): 应答报文(acknowledge,后简称为A): 通知报文(notify,后简称为N). 这三 ...

  3. 如何保证IM实时消息的“时序性”与“一致性”?

    1.前言 我们都知道,一个典型的分布式系统中,很多业务场景都需要考虑消息投递的时序,例如: IM中单聊消息投递:保证发送方发送顺序与接收方展现顺序一致: IM中群聊消息投递:保证所有接收方展现顺序一致 ...

  4. IM开发干货分享:如何优雅的实现大量离线消息的可靠投递

    1.点评 IM聊天消息的可靠投递,是每个线上产品都要考虑的IM热点技术问题. IM聊天消息能保证可靠送达,对于用户来说,就好比把钱存在银行不怕被偷一样,是信任的问题.试想,如果用户能明显感知到聊天消息 ...

  5. iOS和Android即时通讯开发时后台实时消息推送的原理和区别

    iOS和Android上的实时消息推送差异很大,往小了说是技术实现的差异,往大了说是系统实现理念的不同.实时消息推送在移动端互联网时代很平常,也很重要,它的存在让智能终端真正成为全时信息传播的工具.本 ...

  6. rabbitmq怎样确认是否已经消费了消息_【朝夕专刊】RabbitMQ生产者/消费者消息确认...

    欢迎大家阅读<朝夕Net社区技术专刊> 我们致力于.NetCore的推广和落地,为更好的帮助大家学习,方便分享干货,特创此刊!很高兴你能成为忠实读者,文末福利不要错过哦! 上篇文章介绍了R ...

  7. IM系统中如何保证消息的可靠投递(即QoS机制)(转)

    消息的可靠性,即消息的不丢失和不重复,是im系统中的一个难点.当初qq在技术上(当时叫oicq)因为以下两点原因才打败了icq: 1)qq的消息投递可靠(消息不丢失,不重复) 2)qq的垃圾消息少(它 ...

  8. 【消息队列】kafka是如何保证消息不被重复消费的

    一.kafka自带的消费机制 kafka有个offset的概念,当每个消息被写进去后,都有一个offset,代表他的序号,然后consumer消费该数据之后,隔一段时间,会把自己消费过的消息的offs ...

  9. rabbitmq如何保证消息不被重复消费_RabbitMQ保证消息可靠投递与消费的正确使用姿势...

    前言 MQ 是什么?MQ 我们可以理解为消息队列. 队列是什么?队列我们可以理解为管道. 即以管道的方式做消息传递. 场景展示: 1.我们在双11的凌晨大量秒杀和抢购商品,然后去结算的时候,发现界面会 ...

最新文章

  1. 替换openjdk的版本时遇到报错Transaction check error
  2. 站长之家html视频播放,HTML5视频发展状况
  3. fpga fft项目整理
  4. SAP日记之一-漫漫自学路
  5. K8S批量scale deploy的副本为0,结合xargs -I使用
  6. 维基百科 百度百科:谁是真正的草根
  7. RTA广告能力的应用场景剖析
  8. java cap是什么_寒冬面试归来总结最新蚂蚁4面(Java):CAP+数据强一致性+分布式等...
  9. BZOJ.3990.[SDOI2015]排序(DFS)
  10. GDKOI 2016
  11. Nginx+Keepalived主备配置
  12. 【机器人】从机械臂示教器导出编码器数据到U盘中的操作步骤
  13. TypeError: ‘RClass‘ object is not callable, TypeError: ‘CClass‘ object is not callable
  14. 用文本文档编写c语言程序,c语言程序设计!怎样用程序修改文件(txt文档)里面的内容!?(需要代码,不仅仅是解说)...
  15. Microsoft Visio 2010 简体中文版官方版
  16. java项目-第37期基于springboot+layui实现的医院His系统【毕业设计】
  17. python pymysql multiprocessing.dummy多线程 读写数据库报错
  18. 华为服务器在哪里看型号,服务器型号怎么看
  19. 安装分区助手,总是显示“分区助手已安装到你的电脑中,怎么办
  20. 【jmeter性能测试】模拟多个IP同时登录

热门文章

  1. [WPF]静态资源(StaticResource)和动态资源(DynamicResource)
  2. 关闭 Sublime Text 3 自动更新
  3. iOS开发基础-九宫格坐标(4)
  4. system函数和fork-exec机制
  5. codevs 1004 四子连棋 BFS、hash判重
  6. WebsiteSpark是免费软件吗?
  7. Python--粒子滤波定位案例程序
  8. colab出现input output error问题
  9. 二级指针读取文件(显示行数、读取、释放内存)
  10. 初中计算机vb知识点,全国计算机等级考试VB知识点总结(全部).docx