1、前言互联网发展至今,IM(即时通讯聊天应用)一直是互联网上最为成功也是最为平常的应用类型。尤其现今的移动互联网时代,因即时通讯技术的发展和普及,IM这种即时通讯应用已乎达成了各即时通讯应用运营者梦寐已求的所谓“全时在线”,而这种“全时在线”及其应用体验的背后,回归到技术本质就是各种行为消息(或者说信息)的实时性、必达性。 本文将要讨论的是即时IM应用中极其重要但也不被用户感知的消息送达保证机制(即QoS机制),文中将给出目前主流的参考实现思路。 另外,开源轻量级移动端即时通讯框架 MobileIMSDK 的消息送达保证(QoS机制)的大致原理请见:http://www.52im.net/thread-129-1-2.html,3楼Jack Jiang的回复已指出技术原理,跟本文中所描术的思路异曲同工,需要的可以看看。也可以从《为什么说基于TCP的移动端IM仍然需要心跳保活?》一文中了解到TCP协议下的移动端IM网络状况分析。 2、IM开发干货系列文章本文是系列文章中的第1篇,总目录如下:
另外,如果您正在查阅移动端IM开发资料,推荐阅读《新手入门一篇就够:从零开发移动端IM》。 3、本文概述消息的可靠性,即消息的不丢失和不重复,是IM系统中的一个难点。当初QQ在技术上(当时叫OICQ)因为以下两点原因才打败了ICQ:
今天,本文将用十分通俗的语言,来讲述IM系统中消息可靠性的问题。 4、报文类型IM的客户端与服务器通过发送报文(也就是请求包)来完成消息的传递。 报文分为三种:
这三种报文的解释如下: <ignore_js_op>
5、普通消息投递流程用户A给用户B发送一个“你好”,很容易想到,流程如下: <ignore_js_op>
6、上述消息投递流程出现的问题从流程图中容易看到,发送方client-A收到msg:A后,只能说明im-server成功接收到了消息,并不能说明client-B接收到了消息。在若干场景下,可能出现msg:N包丢失,且发送方client-A完全不知道,例如:
结论是悲观的:接收方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-A,在收到了ack:N报文后,才能确认client-B真正接收到了“你好”。 你会发现,一条消息的发送,分别包含(上)(下)两个半场,即msg的R/A/N三个报文,ack的R/A/N三个报文。一个应用层即时通讯消息的可靠投递,共涉及6个报文,这就是im系统中消息投递的最核心技术(如果某个im系统不包含这6个报文,不要谈什么消息的可靠性)。 8、可靠消息投递存在什么问题期望六个报文完成消息的可靠投递,但实际情况下:
那怎么办呢? 9、消息的超时与重传client-A发出了msg:R,收到了msg:A之后,在一个期待的时间内,如果没有收到ack:N,client-A会尝试将msg:R重发。可能client-A同时发出了很多消息,故client-A需要在本地维护一个等待ack队列,并配合timer超时机制,来记录哪些消息没有收到ack:N,以定时重发。 <ignore_js_op> 一旦收到了ack:N,说明client-B收到了“你好”消息,对应的消息将从“等待ack队列”中移除。 10、消息的重传存在什么问题第五节提到过,msg:N报文,ack:N报文都有可能丢失:
启示: 平时使用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群消息的可靠投递问题感兴趣的可查阅相关资料。 |