IM 内容分享(七): 登录和登出以及消息收发逻辑实现

本文详细阐述了IM系统中的用户登录、重复登录处理、多端登录、登出逻辑以及心跳机制,同时介绍了消息收发的三个关键阶段:生产消息、推送消息和确认消息,强调了及时性和可靠性的重要性。
摘要由CSDN通过智能技术生成

目录

登录和登出逻辑实现

一、用户登录

二、重复登录

三、多端登录

四、用户登出

五、心跳

消息收发逻辑实现

一、 生产消息阶段

二、推送消息阶段

三、确认消息阶段

总结文中关键


登录和登出逻辑实现

通过前面几篇文章,对 IM 系统的分层架构设计,以及入口层 Entry、路由层 Router、数据访问层 Das 等每一层的核心职责和关键逻辑进行了深度剖析。

今天开始分析 IM 的功能逻辑实现,通过落地系统功能,将前面剖析的每一块内容串联在一起,来体会每一模块在整体架构中的设计和实践。

一、用户登录

首先分析用户登录逻辑,见下图。

以用户 uid=101 为例说明,其登录流程为:

  • 客户端在启动时,会通过 http 方式访问专门用于鉴权的 Passport 服务,获取用于登录的  token,token内嵌uid;

  • 客户端在登录 IM 后端系统时,首先与入口层 Entry 通过三次握手建立 TCP 长连接;

  • 基于该长连接,客户端携带  token,向 Entry 发送登录请求;

  • Entry 不处理业务逻辑,将该登录请求转发给业务逻辑层  Logic;

  • Logic  通过访问 Passport 服务,判断 token 的合法性,若 token 非法,Logic 回复 Entry,Entry 直接关闭与客户端之间的长连接;

  • 若 token 合法,Logic 首先向 Router 中写入登录用户与 Entry 节点之间的映射关系  <uid, 192.168.1.1>;

  • 然后 Logic 将用户登录事件封装成消息,发送到 MQ,MQ 下游的  Extlogic 通过访问数据访问层 Das,将用户登录事件写入到登录库;

  • 最后 Logic 回复 Entry 用户登录成功,Entry 将回复包发送到客户端。

二、重复登录

对于已经成功登录的用户,如果通过其他设备再次登录,此时就会发生 “重复登录”,对于重复登录,我们通常的处理方式是将前一次登录的用户进行 “踢人” 操作,见下图。

用户 uid=101 已经成功登录 IP 是 192.168.1.2 的 Entry 节点,如果该用户此时通过其他移动设备重新发起登录,且连接的是 IP 是 192.168.1.1 的 Entry 节点,那么 Logic  在处理登录请求逻辑时,会通过访问 Router 判断出用户 101 已登录的状态,且连接的是另一个  Entry 节点,此时 Logic 会构造 kickout 包,发送到最初登录连接的 Entry 节点,该 Entry 节点向客户端发送 “踢人” 通知后,关闭与客户端之间的长连接;同时,Logic 会将最新的 <101, 192.168.1.1> 记录写入到  Router 中。

对于两次重复登录连接的是同一个 Entry 节点的情况,处理方式类似。

三、多端登录

如何实现用户的多端登录呢?即同一个用户在不同类型的客户端上同时在线。

实现多端登录的核心设计在于中央存储 Router 登录用户设备类型的支持,见下图。

同一个用户,可以在不同类型的客户端上与 Entry 建立连接,路由层 Router 需要存储 <uid、设备类型、Entry 节点> 这样的三元组记录,uid 可以唯一标识用户,uid + 设备类型 可以唯一标识连接的 Entry  节点。

所以 Router 的数据结构是一个嵌套的 key-value 结构:<uid , <type,entryIp>> , uid 表示用户,type 表示设备类型,entryIp 表示 Entry 节点。

四、用户登出

理解用户登录逻辑之后,用户登出逻辑就非常简单了。

  • 客户端基于与 Entry 之间的长连接,发送登出请求到 Entry;

  • Entry 将登出请求转发给 Logic ;

  • Logic 访问 Router ,清理用户在线的相关数据;

  • Logic 将用户登出事件封装成消息,发送到 MQ,MQ 下游的  Extlogic 通过 Das,将用户登出事件写入到登录库;

  • Logic 回复 Entry 后, Entry 关闭与客户端之间的连接。

五、心跳

客户端成功登录后端 IM 系统后,会周期性发送心跳请求到后端,以保持长连接的活性;同时, Entry 也以客户端心跳为依据判断用户是否已经登出,毕竟在移动时代,很少有用户会有 “登出” 操作了。

  • 客户端发送心跳请求到 Entry,Entry 通过心跳算法处理心跳请求;

  • 同时,Entry 将心跳请求转发给  Logic,Logic 将心跳时间写入到  Router;

  • 当 Entry 发现心跳失活的客户端后,会模拟客户端构造登出请求发给 Logic;

  • Logic 访问 Router ,清理用户在线的相关数据;

  • Logic 将用户登出事件封装成消息,发送到 MQ,MQ 下游的  Extlogic 通过 Das,将用户登出事件写入到登录库;

  • Logic 回复 Entry 后, Entry 关闭与客户端之间的连接。

消息收发逻辑实现

“消息收发” 是 IM 系统最最核心的业务逻辑模块,本篇文章是整个【IM专题】的核心!

IM,即 “即时通讯”,要求消息具备 “及时性” 和 “可靠性”:

及时性,要求消息的收发需要很低的延时,在线双方通过消息交流时,没有明显的滞后感。

可靠性,要求消息不能丢失;对于消息发送方来说,只要消息发送成功了,消息就会一直存在服务端,不会丢失(除非因产品策略,删除久远的历史消息);对于服务端来说,只要接收方在线,一定会把消息送达到接收方,如果接收方不在线,会通过其他通道来触达和通知到接收方用户,体现出 “即时通讯” 的优势 。

在 IM 单体架构中,我们分析过客户端不断对服务端进行轮询获取消息的方式,这种方式我们称之为 “信箱模型”;信箱模型虽然实现比较简单,但是消息的及时性不好,因为存在大量的无效请求,会造成服务端的资源浪费和负载,信箱模型比较适合用户规模比较小的场景,不适合分层架构的 IM 系统。

分层架构的 IM 系统,通常基于服务端直接推送的方式实现消息从服务端到客户端的传输,见下图。

客户端 A 发送消息到服务端后,服务端直接将消息推送到客户端 B,就好像服务端知道客户端 B 的电话号码一样,可以直接联系到客户端 B,我们把这样的消息推送模型叫做 “电话模型”,这里的 “电话” 往往就是 长连接。

分层架构 IM 系统的消息收发流程,为了做到 “及时性” 和 “可靠性”,我们划分为三个阶段:生产消息阶段、推送消息阶段,确认消息阶段,在这三个阶段中,通过识别用户假在线的三重保障加强了消息的及时可靠性。IM 分层架构消息收发流程见下图。

以用户 uid=101 发送消息给用户uid=102 为例,描述详细流程。

一、 生产消息阶段

生产消息阶段完成客户端成功发送消息到服务端的过程。

  • 1-1:客户端 101 成功登录后,基于与入口层 Entry 之间的长连接,发送消息请求到 Entry;

  • 1-2:Entry 是入口层组件,不负责处理业务请求,收到客户端的消息请求后,基于与业务逻辑层 Logic 之间的  RPC 连接,将消息请求转发到 Logic;

  • 1-3:Logic 接收消息后的第一件事情,是对消息内容进行 “反作弊” 过滤,通过调用 Spam 服务识别出违反平台规则的消息;若消息违反平台规则,则整个流程止步于此;

  • 1-4:对于正常的消息,Logic 做的第二件事情是通过调用数据访问层 Das 将消息落库,具体是分别写入 “消息库” 两条记录和 “联系人库” 两条记录;

  • 1-5:消息成功落库后,Logic 构造回复包,通过 Entry 主动建立的 RPC 连接,将回复包发送给 Entry;

  • 1-6:Entry 将回复包发送给客户端 101。

这是生产消息阶段所要完成的事情,有几个关键的设计点需要注意:

  1. Logic 接收消息请求后,一定是 “先落库,再回复”,顺序不能反,很容易可以思考到为什么;

  2. 对于客户端来讲,消息发送后,如果在一定时间内没有收到 response,则表示消息发送失败;此时在客户端界面上,消息的旁边会出现红色的感叹号,提示用户消息发送失败,点击重发;根据网络的二将军原理,我们知道,客户端虽然没有收到 response,但消息可能已经落库成功,若重发消息,会造成消息的重复,如何幂等呢?通过客户端来生产在客户端范围内唯一的 client_msg_id 即可;(

  3. Logic 接收消息落库时,为了消息定制化需要和读取的方便,同时也为了分库分表时同一用户的数据能落在一张表中,针对每一条消息会存两条消息记录和两条联系人记录。

二、推送消息阶段

推送消息阶段完成服务端推送消息到客户端的过程。

  • 2-1:Logic 访问路由层 Router,获取消息接收方 uid=102 是否在线和连接的 Entry节点;若客户端 uid=102 不在线,Logic 会封装相关数据,发送到 MQ,由 Extlogic 进行离线用户的召回逻辑处理,包括向离线客户端 uid=102 推送手机push、发送微信公众号消息(对于关注公众号的用户)、发送短信、发送VoIP等;

  • 2-2:若客户端  uid=102 在线,Logic 基于与 Entry 之间的 RPC 连接(对于这条 RPC 连接,Logic 是客户端,Entry 是服务端),将消息发送给 Entry;若用户 uid=102 处于假在线状态,即 Router 认为 102 在线,但 Entry 中已没有 102 的连接句柄,Entry 会直接回调 Logic 的 unreachabled 接口,在该接口中,Logic 一方面会写 Router,修复 uid=102 的假在线状态,一方面会封装相关数据发送到 MQ,由 Extlogic 进行离线用户的召回逻辑处理;

  • 2-3:若 Entry 可以在内存中找到 uid=102 的连接句柄,则基于客户端与 Entry 之间的长连接,将消息推送给客户端。

这是推送消息阶段所要完成的事情,有几个关键的设计点需要注意:

  1. Logic 分别通过访问 Router 和 Entry 识别出假在线用户,然后通过 Extlogic 完成离线用户的召回逻辑;对于真在线和真离线用户,都有相关的处理方式,如果不能识别出假在线用户,会极大影响 IM 系统的可靠性;

  2. Logic 除了向 Entry 提供 msg_send 接口外,还需要提供 unreachabled 接口,从整体逻辑上简化流程实现。

访问 Router 和 Entry 是识别用户假在线的两重保障,第三重保障发生在第三阶段。

三、确认消息阶段

在第二阶段中,Entry  若从内存中找到客户端的连接句柄,会基于此将消息推送;在 Entry 内存中存在句柄的客户端,仍然有可能是假在线状态;确认消息阶段完成客户端接收到消息后向服务端回复确认的过程。

  • 3-1:客户端 uid=102 接收到消息后,构造确认包,基于与 Entry 之间的长连接,发送给 Entry;

  • 3-2:Entry 基于与 Logic 之间的 RPC 连接(Entry 是客户端,Logic 是服务端),调用 Logic 的 msg_ack 接口,向  Logic 确认消息已经被客户端 uid=102 成功接收;如果在一定时间内(比如15秒),Logic 没有接收到客户端发送的该条消息的确认请求时,根据消息的级别和产品规则,要么对该消息进行重复推送,要么认为客户端 uid=102 已经离线,此时 Logic 一方面写 Router,一方面封装相关数据发送给 MQ,由 Extlogic 进行离线用户的召回逻辑处理。

这就是识别假在线用户的第三重保障。确认消息阶段所要完成的事情不多,但有一个非常关键的设计。

大家想一下,对于 Logic 推送的每一条消息,Logic 都要等待(假设15秒)其被确认;对每一条消息单独开启一个线程,我们可以很容易落地实现;如果在15秒内,有10万条消息被推送,是不是要开启10万个线程来出来呢?有没有更轻量级的实现方案呢?(我们在下篇文章中解析该方案)

总结文中关键

  1. 用户登录时,Logic 完成四件事情:调用 Passport 进行用户合法鉴权、将客户端与 Entry 的映射关系写入到  Router 、将用户登录事件封装成消息发送到MQ、回复 Entry 是否要保留客户端连接;

  2. 判断用户是否重复登录的关键是访问 Router 获取用户登录状态,如果已经登录,通过发送  kickout 包进行踢人处理;

  3. 实现用户多端登录的核心是 Router 支持 三元组存储,即 <uid , <type,entryIp>> ;

  4. 用户登出时,Logic 完成三件事情:清理 Router 中用户在线数据、将用户登出事件封装成消息发送到MQ、回复 Entry;

  5. 当客户端心跳失活时, Entry 模拟客户端构造用户登出请求。

  6. IM,要求消息具备 “及时性” 和 “可靠性”;

  7. 在用户量较大的分层架构的 IM 系统中,通常基于 “电话模型” 实现消息从服务端到客户端的传输;

  8. 分层架构 IM 系统的消息收发流程,包括三个阶段:

    • 生产消息阶段,完成客户端成功发送消息到服务端的过程;

    • 推送消息阶段,完成服务端推送消息到客户端的过程;

    • 确认消息阶段,完成客户端接收到消息后向服务端回复确认的过程。

  9. 识别用户假在线状态的三重保障:

    • 访问 Router;

    • 访问 Entry;

    • 客户端回复确认包。

  • 24
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

之乎者也·

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值