IM专题:分层架构IM系统(6)— Router能力分析

在分层架构 IM 系统中,路由层 Router 的核心职责是作为中央存储记录在线用户的连接状态,见下图。

Router 其本质是一个巨大的  Map<uid,  EntryIp>,其 key 是表示客户端用户的 uid, 其 value 是客户端连接的 Entry 节点;要判断一个用户是否在线,该用户客户端连接到了哪一个 Entry 节点,Router 是门儿清的。

明确了 Router 的核心职责后,那么Router 需要提供哪些能力呢?

先分析一下,在分层架构 IM 系统中 Entry、Router 和  Logic 三者之间的调用关系,见下图。

  1. Entry 通过 RPC 调用 Logic,实现业务逻辑的处理;

  2. Logic 通过 RPC 调用 Entry,实现消息向用户侧的推送;

  3. Logic 通过 RPC 调用 Router,维护在线用户信息;

  4. Router 回调 Logic,实现心跳失活用户的及时通知。

所以,Router 需要提供两方面能力:业务接口 和 通知回调。


一、 业务接口

Router 的核心数据结构是一个 Map,其 key 是唯一标识用户的 uid,而其 value 为了方便功能上扩展,可以落地为一个 结构体对象,代码如下:

Map<uid, userInfo>userInfo {  int userStat;//用户状态 0—离线 1—在线 2—......  long heartbeat;//最近一次心跳时间  int deviceType;//设备类型 0—安卓 1—IOS 2—......  string deviceToken;//用户移动设备token,用于手机推送  string entryAddr;  //Entry节点地址, ip:port}

Router 基于该核心数据结构,向 Logic 提供四个业务接口;这四个业务接口,纯内存操作,而且逻辑简单。

  1. 用户登录

    Logic 校验完成用户登录请求后,构建 userInfo 对象,写入到 Router 中。

  2. 用户登出

    用户登出时,Logic 直接从 Router 中根据用户 uid 删除记录,或将 userStat 字段置为 0。

  3. 设置用户信息

    在多种业务场景下,Logic 会调用该接口:客户端发送心跳请求时,修改 heartbeat 字段值;客户端在线状态下,使用了新的设备进行登录,修改 deviceType  和 deviceToken 字段值,如果连接的是新的 Entry 节点,修改 entryAddr 字段值。

  4. 查询用户信息

    Logic 需要向用户主动推送消息时,在 Router 中查询用户是否在线,如果在线,则获取用户客户端连接的 Entry 节点地址。


二、 通知回调

Router 中保存了所有的在线用户;而 Router 不仅仅只是一个内存数据库,它能根据客户端心跳数据判断出失活用户,然后通过回调方式及时通知 Logic 进行处理;Logic 处理的方式是通知相关 Entry 节点及时关闭与客户端之间的连接,释放资源。

我们在前面的文章(IM专题:分层架构IM系统(4)—Entry心跳算法)中分析过,Entry 也会根据客户端心跳数据识别出失活用户,这和 Router 的识别失活用户工作并不冗余,而是相辅相成。


Router 的核心数据结构是一个很大的 Map。通常我们会通过定时扫描的方式,扫描出很长时间未发送心跳包的客户端;在扫描的时候,为了安全,会给这个很大的 Map 加上一把大锁,这会非常影响 Router 的服务性能;如何进行优化呢?见下图

将一个大的 Map 划分成 16 个小 Map,也可以形象地称之为 16 个桶,标记为 0~15;根据公式 uid % 16 可以简单算出用户会落入到哪个桶中。扫描线程按桶进行扫描,扫描到哪个桶,就只对哪个桶进行加锁,如此则大大降低了锁的粒度。根据在线客户端数目,可以对桶的数量进行调整。


最后,总结文中关键:

1、Entry、Logic、Router 三者之间的调用关系为:

   a. Entry 接收客户端请求调用 Logic;

   b. Logic 向客户端推送消息调用 Entry;

   c. Logic 维护在线用户数据调用 Router;

   d. Router 扫描出失活用户,回调通知 Logic。

2、Router 向 Logic 提供四个业务接口:

   a. 用户登录

   b. 用户登出

   c. 设置用户信息

   d. 查询用户信息

3、Router 通过定时扫描 Map 的方式获取失活用户,为了提升服务性能,将一个大  Map 划分成 多个桶,然后对桶加锁,降低锁的粒度。


大家思考一下:Router 作为内存数据库,高可用的重要性不言而喻,如何实现其高可用呢?随着在线客户端数量的增多,怎样对 Router 进行维护呢?

相关推荐

  • 看完《三体》后,我要为它鼓掌叫好
  • 如何去阅读源码,我总结了18条心法
  • 实在想不通,为毛两个已婚男人,对脱单还十分感兴趣。
  • 所谓工作能力出众,就是要做好这7件事
  • 春节老家相亲市场,华为约等于公务员
  • 我,阿里P8,32岁决定裸辞:勤奋,只会让我们越来越平庸!
  • ChatGPT爆火,LeCun心态崩了!称大语言模型是邪路,Meta模型3天惨遭下线
  • 多模态版ChatGPT,拿下视觉语言新SOTA, 代码已开源
  • 挑战ChatGPT,谷歌正式发布Bard,CEO亲自下场邀请测试
  • Spring Boot + Redis 实现点赞功能的缓存和定时持久化
  • 阅读的意义:读什么?
  • MySQL 对已存在数据表添加自增 ID 字段
  • 今日公开课|面试通关之从RabbitMQ、Kafka到RocketMQ
  • 《狂飙》播完了,孙红雷的强盛集团也赚了.....
  • 颠覆美国科技界的华裔天才出生 | 历史上的今天
  • 微信文章底部增加淘宝入口;Google 推出 ChatGPT 竞品 Bard;苹果或推出无接口设计iPhone|极客头条
  • Redis面试夺命十六问,我差点就没抗住!
  • 字节一面:HTTP 长连接和 TCP 长连接有区别?
  • git pull 提示错误 fatal: refusing to merge unrelated histories
  • 一行代码引发的“血案”:价值5亿欧元的火箭,发射40秒后凌空爆炸