当前位置: 首页 > article >正文

单体架构 IM 系统之 Server 节点状态化分析

基于 http 短轮询模式的单体架构的 IM 系统见下图,即客户端通过 http 周期性地轮询访问 server 实现消息的即时通讯,也就是我们前面提到的 “信箱模型”。“信箱模型” 虽然实现非常容易,但是消息的实时性不高。

我们在上一篇文章(单体架构 IM 系统之长轮询方案设计)中提出了优化方案,即通过 http 长轮询方式模拟出长连接的效果。

基于 http 长轮询方式实现的 IM 系统的单体架构中, server 节点还是无状态化的吗?所谓 “无状态化” 节点,是指进程在内存和硬盘中没有独立的数据;很明显,不同的 server 节点会 hold 住不同客户端的 http 请求,也就是不同的 server 节点中会存储不同客户端的数据, server 节点是有状态化的;此时,点对点的消息发送逻辑肯定需要进行调整。

大家可以先思考几个问题:

  1. 在 http 长轮询模式下, server 节点是有状态的,如何实现 server 节点的高可用呢?

  2. 客户端 x 发消息给 y,如果 x 和 y 访问的是不同的 server 节点,应该如何处理呢?

  3. 在 http 长轮询模式下,怎样判断消息接收方是否在线呢?

我们直接给出在 http 长轮询模式下,消息点对点的发送流程;以客户端 x 发消息给客户端 y 为例,如下:

  • 客户端 x 向 server 端发送 http 消息请求;

  • server 首先将消息直接落库,分别写 “云消息表” 和 “离线表”;

  • 然后 server 访问缓存,获取消息接收方 y 的在线状态,若 y 不在线,则整个流程结束;

  • 如果消息接收方 y 在线,通过访问缓存获取 y 连接的是哪一个 server 节点;

  • 如果 y 和 x 连接的同一个 server 节点,则 server 将该消息通过 http 长轮询返回给客户端 y;

  • 如果 y 连接的是另一个 server 节点,此时需要当前 server节点将消息推送到目标 server 节点;

  • 最后目标 server 节点将消息通过 http 长轮询返回给客户端 y。

在上述流程中,有两个地方需要特别注意:

  1. 客户端每一次发起 http 长轮询请求,相当于一次心跳,表示用户的在线状态,需要在缓存中记录客户端的在线数据;在 http 短轮询模式中,缓存中记录的 session 数据是 map<uid, {type, cmd, time}> ,在 http 长轮询模式中,需要记录客户端请求的是哪一个 server 节点,所以 session 类型为 map<uid, {type, cmd, time, serverip}>。

  2. 不管消息接收方在线与否,server 节点接收消息后,都需要写 “离线表”,这样设计的原因是为了提高消息的可靠性;因为即使用户 “在线”,在 http 长轮询返回时,客户端有可能接收不到消息,同时,在一次完整的 http 长轮询请求的间隙中,消息都是有丢失的可能的,所以持久化 “离线表” 是可靠性的保证;因此,在每一次 http 长轮询请求中,都需要访问 “离线表”,一是删除客户端已经收到的消息,二是从 “离线表” 中获取还未收到的消息。

在 http 长轮询模式下, server 节点是有状态的,那么其高可用如何保证呢?这个问题很容易解决:首先 server 节点肯定要集群化部署,然后由 反向代理 nginx 转发请求到 server ; nginx 通过配置实现客户端ip的会话保持,即将相同的客户端请求始终转发到固定的 server 节点; 当 server 节点挂掉之后,nginx 将请求转发到其他 server 节点即可,服务仍将持续提供,只需变更缓存中客户端状态信息即可。

单体架构 IM 系统,从架构到设计,从协议到逻辑,其关键点都进行了 一 一 分析;最后,我们简单聊一下 server 的整体设计,server 通过 Go语言进行了实现,见下图。

  1. 主协程,不处理任何的业务逻辑,用于接收外部信号,如关闭程序等;

  2. 子协程,用于接收客户端连接,针对每一个客户端连接,子协程都会生成两个协程来维护该连接,即:每一条连接会有一个独立的协程组来维护(该协程组中有两个协程,一个用于读,一个用于写);

  3. 连接管理器,实现对所有连接的管理,从连接中读取请求交由业务逻辑模块处理;

  4. 业务逻辑模块,实现核心的业务逻辑,包括:登录、登出、心跳、发消息等;

  5. 在线用户管理器,维护连接当前 server 节点所有的客户端;如果消息接收方在当前 server 节点,在线用户管理器通过 管道(chan)将消息传输给连接管理器中消息接收方的连接;

  6. 通讯协议,是公共协议定义,由【连接管理器】【业务逻辑模块】【在线用户管理器】共同引用。

关于 “每一条连接会有一个独立的协程组来维护”,是 Go 语言通用的高效网络编程模型,见下图。

  • 客户端与服务端建立连接时,在服务端其实创建了一个 socket (即 fd 或句柄);

  • 然后为该 socket 生成一个协作组,该协程组包括两个协程: 协程1-1,负责对 socket 进行读; 协程1-2,负责对 socket 进行写;这两个协程,一个读一个写互不影响,高效协作;

  • 当需要向客户端写消息时,不管是当前socket 请求的数据,还是从其他 socket 中读取的数据,必须通过协程组的管道(channel) 作为入口,然后协程1-2会从 channel 中读取数据然后写入到 socket 中。

最后,总结文中关键:

1、基于 http 长轮询方式实现的 IM 系统的单体架构中, server 节点是有状态的;

2、基于 http 长轮询发消息流程:消息到达 serer 后,先落库;若消息接收方在当前 server 节点,直接返回,否则需要将消息推送到目标 server 节点;

3、 基于 http 长轮询方式实现的 IM 系统,缓存中需要记录客户端连接的是哪一个 server 节点;

4、 在 http 长轮询模式中,不管消息接收方在线与否,server 节点接收消息后,都需要写 “离线表”;

5、 Go 语言通用的高效网络编程模型:每一条连接会有一个独立的协程组来维护;协程1-1,负责对 socket 进行读; 协程1-2,负责对 socket 进行写。

至此,“单体架构 IM 系统” 核心问题全部讲完了,你是否还记得如下关键点:

为什么要采用单体架构?

单体架构有怎样的优势?

单体架构的IM系统是怎样的?

单体架构 IM 系统的消息收发逻辑是如何实现的?

什么是 “信箱模型” ,有什么优势和缺点?

“信箱模型” 消息的实时性如何提升?

http 长轮询方式的两种落地方案:“定时器” 和 “时间轮” 如何实现?

上述问题都可从以下四篇文章中找到答案:

《单体架构 IM 系统之架构设计》

《单体架构 IM 系统之核心业务工作实现》

《单体架构 IM 系统之长轮询方案设计》

《单体架构 IM 系统之 Server 节点状态化分析》

分层架构 IM 系统的关键问题,后续文章马上更新跟进......


http://www.kler.cn/a/399235.html

相关文章:

  • 【Golang】——Gin 框架中的模板渲染详解
  • PostgreSQL序列:创建、管理与高效应用指南
  • git push时报错! [rejected] master -> master (fetch first)error: ...
  • 基础IO2
  • 鸿蒙中如何实现图片拉伸效果
  • Cartographer激光雷达slam -20241116
  • 【Rust中的策略模式实现】
  • 10款PDF合并工具的使用体验与推荐!!!
  • 【Redis】使用redis实现登录校验功能
  • vim配置 --> 在创建的普通用户下
  • linux,一、部署LNMP环境二、配置动静分离三、地址重写四、编写systemd Unit文件
  • Azure pipeline 通过git命令修改文件
  • 记录配置ubuntu18.04下运行ORBSLAM3的ros接口的过程及执行单目imu模式遇到的问题(详细说明防止忘记)
  • 【Python刷题】最少拐弯路线问题
  • 实战:深入探讨 MySQL 和 SQL Server 全文索引的使用及其弊端
  • 从北美火到中国,大数据洞察品牌“STANLEY”的突围之路
  • Java基础-I/O流
  • 了解什么是Python(简介)
  • ES6笔记
  • 38配置管理工具(如Ansible、Puppet、Chef)
  • ssm114基于SSM框架的网上拍卖系统的设计与实现+vue(论文+源码)_kaic
  • 两种鼠标hover切换对应图片方法对比
  • Linux下编译MFEM
  • 基于YOLOv8深度学习的智慧课堂学生专注度检测系统(PyQt5界面+数据集+训练代码)
  • Prometheus面试内容整理-Alertmanager
  • C++: string(二)