IO及网络通信我爱编程

一套简洁的即时通信(IM)系统

2018-03-03  本文已影响0人  卡门001

原文:https://weibo.com/p/1001603950681689090105

 无论是IM消息通信系统还是客户消息系统,其本质都是一套消息发送与投递系统,或者说是一套网络通信系统,其本质两个词:存储与转发。由于看到最近一个月内业界有些公司的相关人员介绍了自己的IM系统,如

图二
  上两图显示了携程家的消息系统的初期架构,图一中的架构充分体现我朝IT开发人员"快糙猛"的开发风格,直接用mongodb作为消息队列,然后就把系统开发出来了,至图片2中才稍微见到一个常见IT系统的接口层。

  上图揭示了京东家的消息系统的初期架构,作者云下山巅揭示了其特点是“为了业务的快速上线,1.0 版本的技术架构实现是非常直接且简单粗暴的”,而且后台系统使用.net基于Redis就把一个IM系统开发出来了。
  两家系统的初期架构说明,一套消息系统对提升自家的服务质量是多么的重要,可以认为现代的服务型的互联网公司成长过程就是一套IM系统的进化史。
  也说明,一家的稍微初具实力的IT公司都可以自己很easy地搭建一套简单易用的即时通信系统。
  本文结合鄙人对IM系统的了解,也给出一套初具IM系统系统特点的消息系统模型

1 在线消息系统

  本文只考虑IM系统的在线消息模型,不考虑其离线消息系统(能够存储IM消息的系统)。根据个人理解其应有的feature如下:

根据以上系统特点,先给出一套稍微完备的IM系统的框架图:


IM系统框架图

  针对上图,我先给出各个模块的中文说明,然后分章节介绍这套系统的各个模块的职能,以及相关的系统流程。

1 PC: 单机型客户端,如windows端和mac端等等;
2 Web/h5: 网页客户端;
3 Android: 手机移动端,取其典型Android端,当然也有ios端[但是考虑到各家开发App都是安卓客户端最先上系统新版本,故用Android代表之];
4 broker:文本消息的有线或者无线接口端,考虑到携程采用了这个词,我也姑且先用之,它提供了消息的接收与投递功能;
5 Relay:图片/语音/视频 转发接口端,其后端可以是自家的服务也可以是第三方服务(如提供图片存储服务的七牛、提供云视频解决方案的腾讯云等);
6 msg chat server:消息逻辑处理端;
7 Router: 在线状态服务端,存储在线的用户以及其登录的broker接口机;
8 Counter: 消息计数器,为每个text等类型的消息分配MSG id;
9 Msg Queue: 未读消息的msg id队列,存储每个client未接收的且未超时的且未超出队列大小的msg id集合;
10 Mysql/mongodb: 消息存储服务、用户资料数据、以及channel成员列表服务数据库,因为二者比较典型,所以取用了这个名字,当然你可以在其上部署一层cache服务;
11 Client:客户端层;
12 Interface/If(下文简称If):服务接口层;
13 Logic:消息逻辑处理层,[这层其实应该有系统最多的模块];
14 DB:存储层,存储了在线状态、消息id以及msg id队列和消息内容等;
15 http: 消息发送和接收协议,IM协议中一般理解为long polling消息处理方式,在web端多采用这种协议;
16 Websocket: 另一种消息发送和接收协议,在移动环境或者采用html5开发的系统多采用这种协议;
17 TCP: 另一种消息发送和接收协议,在环境或者采用html5开发的系统多采用这种协议;
18 UDP: 另一种消息发送和接收协议,某个不保证提供稳定消息传输服务的厂家采用的协议,也许也是用户最多使用的协议,它的优点是无论是无线还是有限环境下都非常快,又由于http/Websocket的基础都是tcp协议,UDP协议在环境拥塞情况下由于不提供拥塞控制等退让算法,反而会去争用网络通道,所以在网络复杂的特别是发生网络风暴的情况下它会显得更快^ _ ^ & ^ _ ^【呵呵哒】;
19 RPC: 一种远程过程调用协议,提供分布式环境下的函数调用能力;
20 Restful: 一种远程服务提供的架构风格,跟RPC比起来貌似更高级点;
* A client以阻塞方式发出msg req,req = {producer uin, channel name, msg device id, msg time, msg content};
* B broker收到消息后,以uin为hash或者通过其他hash方式把消息转发给某个msg chat server;
* C msg chat server收到消息后以 {producer uin【发送者id】 + msg device id【设备id】+ msg time【消息发送时间,精确到秒】}到本地消息缓存中查询消息是否已经存在,如果存在则终止消息流程,给broker返回"duplicate msg"这个msg ack,否则继续;
* D msg chat server到Counter模块以channel name为key查询其最新的msg id,把msg id自增一后作为这条消息的id;
* E msg chat server把分配好id的消息插入本地msg cache和msg DB[mysql/mongoDB]中;
* F msg chat server给broker返回msg ack, ack = {producer uin, channel name, msg device id, msg time, msg id};
* G broker把ack下发给producer;
* H producer收到ack包后终止消息流程,如果在发送流程超时后仍未收到消息则转到步骤1进行重试,并计算重试次数;
* I 如果重试次数超过两次依然失败则提示“系统繁忙” or “网络环境不佳,请主人稍后再尝试发送”等,终止消息发送流程。

  上面设计到了一个模块图中没有的概念:msg cache,之所以没有绘制出来,是因为msg cache的大小是可预估的,假设msg chat server的服务人数是40 000人,消息发送频率是1条/s,消息的生命周期是4 hour,消息长度是1kB。,那么这个cache大小 = 1k * 4 * 3600 * 40000 = 576 000 000kB,这个数字可能有点恐怖,如果是真实商业环境这个数字只会更小。其本质是一个hashtable。
  这个流程牵涉到一个比较重要的模块:Counter,这个模块其实都可以用Redis充当,怎么做你自己想^ _ ^。
  上面还有一个概念未叙述到:发送箱,它存储了所有本地发送出去的消息,其中没有服务端分配的msg id的消息都被认为是发送失败的消息,待用户主动尝试发送或者网络环境重新稳定后可以有客户端尝试重新发送流程。
  用户查看本地历史消息的时候,就要依据msg id把消息排序好展现给用户。至于用户发送过程中看到的消息可以认为是本地消息的一个cache,每个channel最多给他展现100条,这100条消息的排序要依照每条消息的发出时间或者是消息的接收时间[这个接收到的消息时间以消息到达本机时的本地时钟为依据]。当用户要查看超出数目如100条消息之外的消息,客户端要引导用户去走历史消息查看流程。

+ 2.3 消息状态部分流程
https://kb.cnblogs.com/page/541190/
上一篇下一篇

猜你喜欢

热点阅读