典型的即时通讯架构可能如下所示

 

 无论是即时消息系统还是客户消息系统,其本质都是一套消息传递系统,或者一套网络通信系统。它的本质是两个词:存储和转发。

 

 建议:如果您感兴趣,作者的另一篇文章“高可用性、可扩展性和高并发性的即时消息群聊架构的设计实践”适合在即时消息群聊架构设计中参考。

 

 此外,以下关于即时消息实际开发的文章也值得一读。如果你感兴趣,你可以看看:

 

 适合初学者:从头开始开发即时消息服务器(基于Netty,带有完整的源代码)

 "拿起键盘是干的:和我一起开发一个分布式即时通讯系统. "

 自己开发即时通讯有这么难吗?手工教你一个Andriod版本的简单即时消息(带源代码)

 安卓即时通讯智能心跳算法的设计与实现探讨(含示例代码)

 教你用Netty实现网络通讯程序的心跳机制和断线重连机制

 “WebSocket实时聊天室演示:基于node . js+Socket . io[附件下载]”

 

 1携程异步消息系统的初始架构

 

 一套原始的分布式系统理论框架方案

 

 一套原始的分布式系统理论框架方案

 

 上图显示了携程的消息系统的初始架构。图中的体系结构直接使用mongodb作为消息队列,然后开发了系统。在图中可以看到通用信息技术系统的接口层。

 

 2京东的初始结构

 

 一套原始的分布式即时通讯(IM)系统理论框架方案_im_ jd.png。

 

 上图展示了京东家园消息系统的初步架构,其特点是“1.0版的技术架构实现非常直接、简单,对于快速的网上交易来说很粗鲁”,后台系统使用。net开发一个基于Redis的即时消息系统。

 

 这两个系统的初始架构显示了消息系统对提高服务质量的重要性。可以认为,现代面向服务的互联网公司的成长过程是一个即时通讯系统的进化史。

 二、本方案的总体思路

 

 基于对即时通信系统的理解,本文还给出了一套具有即时通信系统特点的消息系统体系结构模型。本文只考虑即时消息系统的在线消息模型,不考虑其离线消息系统。

 

 1根据个人理解,其应有的特征如下

 

 A.整个系统的服务器端提供存储和转发功能,而不管整体架构是B/S还是c/S;

 消息的发送者可以成功地发送后端的消息,并从后端获得确认。

 C、接收端可以接收服务器端转发的消息,而不超过消息生命周期和系统承载能力;

 整个系统只考虑短信(即限制短信长度);

 e .每条消息都有一个生命周期,如一天,并有一个长度限制,如1440 B[尽量不要超过一帧的大小],仅考虑在线消息的处理,无论是超过系统承载能力的消息(如键盘狂人或键盘狂人机器人发送的消息),都被认为是“垃圾消息”;

 为了简单起见,没有给出许多类型的消息,例如人对人消息、群消息、讨论组消息等。,所有这些都被认为是一组消息[由下面的通道代替,有些人使用“房间”这个词];

 g .为简单起见,本文不讨论建立和销毁该组的过程,即当消息处理开始时,所有消息组都已建立,并且在该过程中没有成员的增加或减少;

 账户申请、用户认证和中国独特的反文字检查和其他即时通讯安全层暂时不予考虑。

 

 根据上述系统特点,首先给出了一个稍完整的即时消息系统框架图

 

 一套原始的分布式系统理论框架方案

 

 3系统名词解释

 

 1台电脑:独立的客户端,如视窗和苹果电脑;

 2网络/h5:网络客户端;

 3安卓:手机移动终端,以其典型的安卓终端为例,当然也有ios终端[但考虑到每个开发应用都是安卓客户端的第一个新版本,它是以安卓为代表的];

 4 broker:文本消息的有线或无线接口。考虑到携程采用这个词,我将首先使用它。它提供接收和传递消息的功能;

 5中继:图片/语音/视频转发接口,后端可以是自己的服务或第三方服务(如七牛提供图片存储服务,腾讯云提供云视频解决方案等)。);

 6消息聊天服务器:消息逻辑处理端;

 7路由器:在线状态服务器,它存储在线用户、他们登录的代理接口机器的id和一些数据,例如心跳分组时间;

 8计数器:消息计数器,为每条文本类型的消息分配msgid

 9消息队列:每个通道消息的消息标识队列,存储每个客户端没有接收到的消息标识集,消息标识集没有超时,也没有超过队列大小;

 10 Mysql/mongodb:消息存储服务、用户配置文件数据和频道成员列表服务数据库。因为他们是典型的,他们取这个名字。当然,您可以在其上部署一层缓存服务;

 11客户端:客户端层;

 12接口/中频(以下简称中频):业务接口层;

 13逻辑:消息逻辑处理层,[该层实际上应该拥有系统中最多的模块];

 14 DB:存储层,存储在线状态、消息id、消息id队列和消息内容;

 15 http:消息发送和接收协议,一般理解为即时消息协议中的长轮询消息处理模式,主要用于网络端;

 16 Websocket:另一种消息发送和接收协议,通常用于移动环境或使用html5开发的系统中;

 17传输控制协议:另一种消息发送和接收协议,通常用于使用html5开发的环境或系统中;

 18 UDP:制造商采用的另一种消息发送和接收协议,不能保证提供稳定的消息传输服务,也可能是用户使用最多的协议。它的优点是在无线和有线环境下都非常快,并且因为http/Websocket是基于tcp协议的,所以UDP协议将竞争网络信道,而不是提供诸如拥塞环境中的拥塞控制之类的让步算法,所以它在复杂的网络中出现得更快,尤其是在网络风暴的情况下。

 19远程过程调用协议,在分布式环境中提供函数调用能力;

 20 Restful:一种远程服务提供的架构风格,似乎比RPC更先进。

 三个具体的消息发送过程

 

 在介绍消息发送过程之前,先介绍一些基本概念。

 

 1 sub/sub,UIN和届会

 

 从宏观上讲,消息系统是一个PUB/SUB系统,它有一个消息生产者发布者[或生产者],一个消息代理,一个消息处理器消息服务器,和一个消息消费者订户[或消费者]。消息消费者可以是一个人或一群人。在pub/sub系统中,生产者和消费者一起形成一个渠道、房间或团体。

 

 无论是生产者还是消费者,每个特定的单位都应该由系统分配一个id,这个id叫做UIN[。

 

 后端if层中的代理机器可以分布在全球或某个区域。UIN可以根据dns系统获得if层中所有机器的列表。如果dns层由于机器故障或攻击而无法服务,客户端应该根据内存知道一些机器的ip和端口地址[是上次成功登录的机器还是制造商内置的机器列表],然后根据速度测量结果选择最近的代理。

 

 UIN是一种会话服务,在代理之间的一段时间内有效。此会话在长连接中存在,或者它可以跨越几个长连接或短连接,也就是说,会话所依赖的网络链路不稳定。在会话有效期内,服务器认为UIN在线,客户端应该在会话有效期内定期向代理发送心跳数据包。在本文中,会话可能是不稳定的,也就是说,发送给客户端的消息可能在会话有效期内丢失,但是消息可以通过其他方式传递给客户端。

 

 2四个发送过程

 

 消息[生产者]通常是即时消息系统最基本的单位,UIN[(即自然人)。由于它是自然人,它的发送能力被认为是有限的,不可能在一秒钟内发送一条以上的消息,也就是说,它的消息频率高达一个消息/秒。在这个频率之上,它们被认为是键盘狂热者或狂躁的机器人,并且客户端或服务器应该有能力拒绝向这些人提供服务或因为疯狂而丢弃他们发送的消息。

 

 基于上述假设,生产者发送的消息请求称为消息请求,服务器返回给客户端的消息响应称为消息确认。整个消息流是:

 

 客户端以阻止模式发送消息请求,req = {生产者uin,频道名称,msgdevice id,msgtim e,msgcontent}。

 B代理收到消息后,将消息转发给msgchat服务器,并使用uin作为哈希或其他哈希方法;

 收到消息后,消息聊天服务器查询消息是否已经存在于本地消息缓存中,关键字=哈希{生产者uin[发送者id]+消息设备id[设备id]+消息time[消息发送时间,精确到秒]}。如果它存在,消息流被终止,并且“重复消息”的消息ack被返回给代理,否则,继续;

 从消息聊天服务器到计数器的模块以频道名称为关键字查询其最新的消息id,并以消息id为该消息的id,增加1后;

 聊天服务器将带有指定id的消息插入本地消息缓存和消息数据库;

 消息聊天服务器向代理返回消息确认,确认= {生产者uin,频道名称,消息设备id,消息tim e,消息id };

 经纪人发送消息给生产商;;

 在收到确认包后,H生产者终止消息流。如果在发送过程超时后仍未收到消息,则转到步骤1重试并计算重试次数。

 如果重试次数超过两次,仍然失败,则通过提示“系统忙”或“网络环境不好,请稍后再试发送”来终止消息发送过程。

 

 上面设计了一个模块图中没有的概念:消息缓存,因为消息缓存的大小是可以预测的,所以没有画出来,它只用于消息去重判断,所以只需要保存重复的消息键。假设消息聊天服务器的服务号是40 000,消息发送频率是1消息/秒,消息生命周期是24小时,消息密钥长度是64B,那么缓存大小= 64B *(24 * 3600)s * 40000 = 221 184 000 000 b,这可能有点可怕。如果是一个真实的商业环境,这个数字只会更小,因为没有。它的本质是一个散列集(对应于C++中的无序集),而物理存储介质当然是共享内存。

 

 [2016/03/10:经过思考,msg cache只需要在设备上存储UIN的最新消息时间,msg cache的结构应该是hashtable,以{ UIN+设备id}为关键字,其最新消息发送时间(客户端发送消息的时间)为值,而不考虑消息的生命周期。每当消息聊天服务器接收到新消息时,它可以比较新消息中记录的发送时间和缓存中记录的消息时间。如果新消息的时间小于这个消息池中记录的时间,则它是一个重复的消息,如果时间更长,则它是一个新消息。新消息的msg time用作msg缓存中相应kv的最新值。假设UIN为4B,设备id为4B,时间为4B,消息缓存的数据大小(不包括哈希表数据结构本身占用的内存大小)为12B * 40000 = 480 000B,新的消息池与每条消息的提升im e无关,大大减少了其内存占用。

 

 还有一个问题。如果用户改变了手机的当地时间会怎么样?然后换成另一个参数:本地手机时钟的累计运行时间,手机出厂后的累计运行时间只会增加而不会减少。

 

 这个过程涉及到一个重要的模块:计数器,它实际上可以用作Redis,你可以自己做。这个模块本身的实现是一个分布式计数器,直接使用Redis没有问题,但是最好的方法是使用消息id批发商。消息聊天服务器一次将一批身份信息批发回柜台,然后将它们分发给每条消息。完成后,它会去柜台申请一批身份证,以减轻柜台的压力。具体设计请参考专利“即时消息处理方法和装置”[参考文献9]。

 

 还有一个上面没有提到的概念:发件人的消息邮箱{有人称之为消息框,或者一家大公司称之为客户消息数据库}存储所有本地发送的消息,其中没有服务器分配的msg id的消息被认为是发送失败消息,在用户主动尝试发送或网络环境再次稳定后,客户端可以尝试再次发送该过程。

 

 当用户查看消息邮箱中的本地历史消息时,他们应该根据消息id对消息进行排序,并显示给用户。对于用户在发送过程中看到的消息,可以将其视为本地消息的缓存,每个通道最多可以向用户显示100条消息,这些消息应该根据每个消息的发送时间或接收时间进行排序(该接收消息时间基于消息到达本地机器时的本地时钟)。当用户想要查看超过100条消息的消息时,客户端应该指导用户完成历史消息查看过程。

 

 3消息状态部分流程

 

 在发送消息的过程中,消息聊天服务器充当消息处理程序。事实上,消息发送过程可以被看作是客户机和服务器之间简单的“心跳逻辑”过程。在此过程中,消息聊天服务器[实际上是下面提到的心跳服务器]必须完成以下消息状态处理逻辑:

 

 1心跳服务器在路由器中将生产者的状态直接修改为在线;

 2心跳服务器应更新客户端连接的代理的id及其最近登录路由器的时间;

 

 至于路由器的具体结构,将在下一章中介绍。

 

 4关于长短信

 

 另一个问题,如果消息超过了服务器指定的短信最大长度怎么办?

 

 一种方法是简单地丢弃它,并拒绝将其发送给客户端,这看起来用户体验不是很好。

 

 还有另一种方法,分裂。通过切分分成几条短消息。每条短消息都由客户端或服务器本身分配一个序列号,然后在用户收到时进行组合。它的本质与tcp层处理大数据包时拆分几个子数据包是一样的。

 

 如果长文本可以用第二种方法处理,它能通过发送图片来完成吗?它的本质是数据,语音和视频数据的处理也是如此。

 4.消息处理和消息传递过程

 

 在上述消息发送过程中,消息聊天服务器将指定消息id的消息返回给生产者后,将继续发送消息。消息传递涉及一系列技巧,包括消息的订户是否能够保持消息的活力。这些技能其实并不神秘,下面的过程将详细描述。

 

 1消息传递过程

 

 顾名思义,消息传递就是消息的分发。有人称之为信息推送过程。

 

 如果消息被发送=消息请求+消息确认,那么消息传递就简单得多:

 

 消息聊天服务器从频道成员列表服务数据库中提取成员列表;

 聊天服务器循环到路由器,检查每个成员是否在线,如果在线,获取成员连接的代理接口机器地址;

 聊天服务器发送消息给经纪人;;

 代理在收到消息后向客户端发送消息;

 电子消息聊天服务器在向在线成员循环发送消息后,将消息id放在消息队列中其频道的消息id列表的末尾;

 f .如果消息队列的消息id列表超过长度限制,则删除链表头部的一些id,以确保列表的长度不超过系统指定的参数;

 过程结束。

 

 消息传递要容易得多。至于“考虑在线”的客户是否收到消息,消息聊天服务器根本不在乎!

 

 这个过程涉及另一个重要的模块:路由器,它也可以用作Redis。Redis位图用于记录所有用户的状态,0表示离线,1表示在线,哈希表用于存储代理的id和每个用户的最新登录时间。

 

 消息队列模块实际上是一个哈希表,关键字是通道的名称或id,值是一个消息id列表。

 

 我听说Redis最近要添加布隆过滤器,这更有趣。关键取决于它是否能处理删除操作。如果有一个删除界面,把它作为位图播放也没什么坏处。

 五心跳过程

 

 如果客户端希望与服务器的会话保持有效,它必须与其代理保持心跳流,才能被视为在线。然后,基本问题是:心脏跳动多长时间。

 

 这个问题将在很长一段时间内让许多移动开发人员头疼。最基本的是根据网络环境设计不同的心跳持续时间:例如,频率在有线环境下设置为10秒,在wifi环境下设置为30秒,在3G或4G环境下设置为1.5分钟,在2G环境下设置为4分钟。简而言之,原则是网络环境越差,心跳间隔越长。

 

 如果心跳间隔较长,心跳频率将较低,消息发送和接收速度将较慢。

 

 此外,在无线环境中心跳时间长度不是固定的,具体时间长度应该由服务器来判断。如果在无线环境中假设初始心跳间隔为4分钟,并且客户端在最后3次连续心跳中失败一次,则时间长度将修改为2分钟,如果有两次失败,则修改为1分钟。如果连续3次超时都没有报告心跳,客户端将被视为离线!

 (2016/03/10):经过今天的思考,我认为上一段例子中的参数是错误的,违反了上一段描述的原则。当心跳超时发生时,意味着网络环境已经改变,但是仅仅一次超时不足以解释网络环境是变好还是变坏。事实上,心跳长度的问题被转换成一个角度来思考:当你知道前三两次的实际心跳间隔时,如何预测下一次心跳间隔?其本质是拉格朗日外推的应用。我不会在这里描述它,只是给出一个方法:如果知道最后两次心跳之间的间隔是iv1和iv2,那么iv3 = k *(iv1+iv2)/2)将被返回给客户端。如果iv1 > iv2,则k = 0.8,否则k = 1.2。这两个值只是经验值,如何评价它们需要系统设计。

 

 如果系统设计者发现这很麻烦,他可以将上述值修改为经验参数值。例如,在无线环境中,假设初始心跳间隔为4分钟,并且客户端在最近3次连续心跳中出现一次故障,时间将修改为4.5分钟,如果出现两次故障,时间将修改为5.5分钟。如果连续三次超时后仍未报告心跳,则认为客户端离线!

 

 

 解决心跳长度的问题,然后看看具体的心跳过程:

 

 a、客户端发送心跳包心跳,心跳= {uin,设备id,网络类型,列表{通道名称:最新通道msgid},其他信息},即心跳包上报uin的所有通道以及本地历史消息记录中每个通道的最新消息id;

 b代理将心跳数据包传输到专门研究心跳逻辑的msgchat服务器(以下简称心跳服务器);

 心跳服务器更新客户端的在线状态以及从路由器登录的代理的id和最新登录时间;

 D心跳服务器从计数器服务器循环查询每个通道的最新消息id。如果客户端报告的id不等于此id,它将发送一条消息通知消息聊天服务器,消息= {uin,频道名称,客户端新的消息频道的ID };

 当接收到该消息时,电子消息聊天服务器重新启动消息发布逻辑,并从消息队列中取出所有大于{客户端新消息频道id }的id列表;

 聊天服务器根据列表中的标识依次从消息存储服务器中取出每条消息(如果不能取出,则表示该消息已被消息存储服务器超时删除);

 消息聊天服务器将这些消息作为“未读消息”发送给客户端;

 心跳服务器根据路由器存储的客户端最近三次登录时间调整会话的心跳时间间隔,并将心跳确认包作为心跳返回包参数值的一部分发送给客户端,其他数据包含消息标识;每个频道的最新消息;

 心跳服务器定期到路由器检查所有客户端的最新登录时间,如果超过会话有效时间,将状态设置为“脱机”,并删除其登录服务id和其他数据;

 j .在接收到心跳确认包之后,客户端修改下一次心跳时间,并将每个信道的最新消息id与本地消息邮箱中相应信道的最新消息id进行比较。如果id不相等,客户端可以开始提取消息的过程,或者等待服务器发送这些消息。

 

 上面提到的一个词:信道的最新信道id或客户端最新消息id,这意味着消息接收者所在的信道所拥有的本地消息的最新id。一般来说,如果服务器上的计数器能够稳定地提供服务,通道中的消息id应该是连续的。如果客户端检测到消息id不连续,它可以将不连续处的id作为新的通道id,并要求服务器在消息id之后重新发送消息,这要求客户端具有消息去重功能。

 

 每次收到服务器发送的消息后,用户必须更新本地最新的频道消息id,并将消息id窗口向前推。不要因为id不连续就一直更新这个值,因为服务器上的服务不一定超级稳定。

 

 我上面写的那段有点太“嗬”了。事实上,它的想法类似于tcp的滑动窗口的想法,所以我会自己比较来理解它。

 

 步骤H要求路由器至少存储客户端最近的四次登录时间,然后根据这三次时间间隔和网络类型修改下一次心跳间隔的有效持续时间。我已经在这里清楚地写下了原则。至于如何取值,我们可以根据上述原则修改相关参数[这需要测试得到一些关键数据,但这个参数值应该与本文中提到的参数值几乎相同]。

 

 步骤J中描述的客户端是否启用消息拉逻辑取决于您的服务类型。具体场景将单独处理,本文将不设计消息拉取过程。

 

 事实上,结合第四章和本章,通俗地说,信息的发送就是微信所谓的“参照动态,同步协议”[参照文件7]的过程,江湖人称之为推与拉的结合过程。

 

 该流程可通过流程图参考:

 

 一套原始的分布式系统理论框架方案

 

 请注意,上图中一些名词的用法与本文不同。其所谓的“离线信息”在本文中被称为“未读信息”。在本章的最后,描述了即时消息的主要过程。

 六种信息存储服务

 

 因为本文描述的消息系统是一个在线消息模型,所以必须删除存储在消息数据库中的超时消息。首先,可以根据服务人员的数量和每条消息的持续时间来估计数据库的大小。

 

 第二,在一个简单的im系统中,如果不考虑用户的等级,可以认为所有的移动服务组都是相等的,并且具有相同的提升im e。但是,如果区分了用户的优先级,其消息lifetime将是不相等的,并且必须有消息db[具有不同服务级别的用户[事实上,优先级越高,消息存储的时间越长,企业支付存储成本,并且一些神秘的力量越容易获得其聊天数据]。

 

 最后,启动定时消息删除模块,定时删除消息数据库中的过期消息。

 七种其他类型的消息

 

 由于本文仅描述了基于文本的短消息服务的相关过程,如果我们也考虑图片、声音和视频流服务,这些消息将被称为富媒体消息。最基本的富媒体消息应该有相应的文本消息,文本消息包含这些富媒体文件的url地址或其他方式定义的地址。通过拉下这种类型的消息,消费者可以根据消息地址拉富媒体文件。

 

 至于如何存储富媒体文件,我个人建议我们可以利用目前成熟的第三方服务平台,比如齐牛的云图片服务(我给你一个栗,不收任何费用,不要怀疑有广告)来存储图片,利用腾讯云的视频服务能力来处理语音和视频信息。

 

 富媒体消息通过您的中继界面提取和上传。因为这个服务接口的逻辑与普通的文本消息有很大的不同,所以建议创建一个独立的接口,称为中继模块,以区别于代理,并为将来替换第三方服务供应商打下良好的基础。

 

 如果你的工厂有钱有势,可以考虑自己储存富媒体文件。此时,在逻辑层中应该有一个相应的模块叫做富文本消息服务器,它的逻辑应该如下:

 

 答:无论是语音还是视频,客户端将其格式化为合适的文件格式,压缩后以片段的形式上传到中继站,每个片段都应该分配一个片段序列号;

 收到这些片段后,brerelay将数据传输到富服务器;;

 富c服务器首先将片段数据存储在缓存中,并在接收到最后一个片段时检查丢失的片段;

 如果D富服务器发现丢失的片段,它将通知客户端丢失的片段列表,并让它重新发送;

 在收集完所有的片段后,富服务器可以再次组装数据并将其放入mongodb或其他数据库。

 

 整个逻辑都完成了,不是很容易吗?

 八个方案概述

 

 该即时消息系统总体上具有以下特征:

 

 1其完整的即时通讯系统设计;

 2.以计数器为系统核心,驱动整个系统流程设计;

 3.涉及客户端的消息流方案;

 4.在保证服务质量的情况下,确保新闻不被重磅或泄露;

 5.详细描述了消息分发的技术流程。

 6.给出了自己设计的智能心跳方案。

 7.它给出了自己的解决方案来处理“长数据”,如长消息、图片、声音和视频;

 8自然分布式能力,确保其多数据中心部署能力;

 9尽最大努力,不断优化......


----------------------------------------------------------------------------------

哇谷im_im即时通讯_私有云_公有云-哇谷云科技官网-JM沟通

IM下载体验 - 哇谷IM-企业云办公IM即时聊天社交系统-JM 沟通下载

IM功能与价格 - 哇谷IM-提供即时通讯IM开发-APP搭建私有化-公有云-私有化云-海外云搭建

新闻动态 - 哇谷IM-即时通讯热门动态博客聊天JM沟通APP

哇谷IM-JM沟通热门动态博客短视频娱乐生活

关于哇谷-哇谷IM-提供企业即时通讯IM开发-语音通话-APP搭建私有化-公有云-私有化云-海外云搭建

联系我们 - 哇谷IM-即时通讯IM私有化搭建提供接口与SDK及哇谷云服务

即时通讯IM融云世界

IM即时通讯钉钉技术:企业IM钉钉在后端架构上的优越之处

新的市场叫板环信、融云、腾讯云!开源版IM即使聊天工具

企业IM即时通讯聊天办公APP钉钉技术分析交流

哇谷云-怎么样正确认识海外云服务器

公有云和私有云之间有什么区别?类似融云、环信云、网易云、哇谷云?