IM系统设计

即时通讯(Instant Messaging,简称IM)是一个实时通信系统,允许两人或多人使用网络实时的传递文字消息、文件、语音与视频交流。实现方式有两种。第一种基于Server转发的,Client双方通信会经过Server转发来完成消息传递。例如QQ、微信。

第二种是基于P2P(点对点)的。P2P的实现依赖于客户端之间的互联,但由于NAT与防火墙的存在,客户端无法直接互联,需要coturn服务器用来穿越NAT网络。

本文主要讲述基于Server转发实现的即时通讯原理和实现过程,包括私聊和群聊两类。私聊和群聊,原理基本类似,但私聊是单次转发,群聊属于遍历转发。不同点为,群聊以群ID(多人)划分,私聊以会话ID(两人)划分,其次消息存储上也有较大差异。

总体设计

实现方式

  1. 协议:WebSocket
  2. 框架:Socket IO/Swoole
  3. 关键点:可扩展,支持分布式部署。短链接负责业务逻辑,长链接负责Websocket。

总体架构图

总体架构图

时序图

这里写图片描述

详细实现

接入层实现

接入层的目的:1保证IM服务的可靠性,避免用户同时集中在同一聊天服务器中。2当聊天服务器压力过大时,能实现扩容。接入层可采用ngx_lua实现,保证性能。

  1. 客户端请求接入IM,调用接入API。参数为:接入类型(群聊,私聊),接入uid,群聊ID
  2. 群聊:保证隶属同群ID的用户落在同一Socket实例上。如果用户ABC,都属于群ID=123中,那么一定要保证ABC都分配在同一实例上。具体分配策略自定,一般按照群ID取余分配在不同机器实例上。
  3. 私聊:A和B私聊,也要保证A和B落在同一实例上。一般分配策略为,(A+B)对机器取余。
  4. 根据群聊类型,得到接入实例的IP和端口,返回给客户端。

连接逻辑

连接逻辑比较简单,Socket connect的过程。

  1. 客户端得到接入层提供的Socket Ip和端口,发起Websocket请求。
  2. Socket服务,根据请求参数,判断客户端合法性(签名验证)和登录验证。
  3. 记录请求日志,保存Socket连接句柄(当前实例数组中)。

订阅消息逻辑

上一步连接成功后,用户触发进群(聊天窗口)操作,订阅该群消息。订阅消息用来保证,用户通过socket实时接收到该群其他用户发送的消息。否则,服务端会采用推送完成消息转发。

  1. 订阅事件。emit(‘sub’),群聊:发送当前群ID和用户的uid。私聊:发送对方uid和用户自身uid(会话ID)。
  2. 服务端判断该uid是否已经在线,如果已经在线,则主动关闭旧的Socket,保证只有当前窗口在线。并将当前socket句柄push到数组中保存。
  3. 业务逻辑判断。群聊:判断用户是否属于该群,判断群合法性。私聊:判断是否为好友。
  4. 客户端在线,接收Socket消息,对消息解码,区分类型并展示。编码协议自定。
  5. 客户端不在线,收到Push推送。客户端根据接收到的推送,拉取历史消息。更新未读数。

发布消息逻辑

用户完成订阅群ID(群聊)或者订阅会话ID(私聊)后,可主动发送消息到该订阅ID(群ID和会话ID)中。

  1. 发布事件。emit(‘pub’),图片(缩略图)、视频、语音,完成上传(静态文件服务)后,编码消息内容并提交。编码协议自定。
  2. 服务端收到消息后,解码,识别过滤色情、政治内容。写入队列,完成其他操作(判断图片色情内容,消息统计,落地入库,更新未读数)。
  3. 群聊:遍历当前群存在的socket句柄,如果在线则转发内容,不在线推送通知。
  4. 私聊:判断对方是否在线,如果在线转发,不在线push推送。

用户下线

用户退出聊天窗口,触发disconnect事件。服务端关闭socket句柄,标示用户下线。

广播逻辑

广播逻辑,主要用在运营管理上。是对所有在线用户或者特定群ID(会话ID),发送消息(系统消息)。实现方法为:遍历所有在线socket句柄,发送内容。

监控服务

在运营管理或者监控上,需要对聊天服务性能加以判定。

  1. 统计在线人数,得到每个实例的Socket句柄长度相加

希望转载的朋友能够尊重作者的劳动成果,加上转载地址。谢谢!
http://gglinux.com/2017/04/15/IM_design/

坚持原创技术分享,您的支持将鼓励我继续创作!