单体架构 IM 系统之 Server 节点状态化分析
基于 http 短轮询模式的单体架构的 IM 系统见下图,即客户端通过 http 周期性地轮询访问 server 实现消息的即时通讯,也就是我们前面提到的 “信箱模型”。“信箱模型” 虽然实现非常容易,但是消息的实时性不高。
我们在上一篇文章(单体架构 IM 系统之长轮询方案设计)中提出了优化方案,即通过 http 长轮询方式模拟出长连接的效果。
基于 http 长轮询方式实现的 IM 系统的单体架构中, server 节点还是无状态化的吗?所谓 “无状态化” 节点,是指进程在内存和硬盘中没有独立的数据;很明显,不同的 server 节点会 hold 住不同客户端的 http 请求,也就是不同的 server 节点中会存储不同客户端的数据, server 节点是有状态化的;此时,点对点的消息发送逻辑肯定需要进行调整。
大家可以先思考几个问题:
-
在 http 长轮询模式下, server 节点是有状态的,如何实现 server 节点的高可用呢?
-
客户端 x 发消息给 y,如果 x 和 y 访问的是不同的 server 节点,应该如何处理呢?
-
在 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。
在上述流程中,有两个地方需要特别注意:
-
客户端每一次发起 http 长轮询请求,相当于一次心跳,表示用户的在线状态,需要在缓存中记录客户端的在线数据;在 http 短轮询模式中,缓存中记录的 session 数据是 map<uid, {type, cmd, time}> ,在 http 长轮询模式中,需要记录客户端请求的是哪一个 server 节点,所以 session 类型为 map<uid, {type, cmd, time, serverip}>。
-
不管消息接收方在线与否,server 节点接收消息后,都需要写 “离线表”,这样设计的原因是为了提高消息的可靠性;因为即使用户 “在线”,在 http 长轮询返回时,客户端有可能接收不到消息,同时,在一次完整的 http 长轮询请求的间隙中,消息都是有丢失的可能的,所以持久化 “离线表” 是可靠性的保证;因此,在每一次 http 长轮询请求中,都需要访问 “离线表”,一是删除客户端已经收到的消息,二是从 “离线表” 中获取还未收到的消息。
在 http 长轮询模式下, server 节点是有状态的,那么其高可用如何保证呢?这个问题很容易解决:首先 server 节点肯定要集群化部署,然后由 反向代理 nginx 转发请求到 server ; nginx 通过配置实现客户端ip的会话保持,即将相同的客户端请求始终转发到固定的 server 节点; 当 server 节点挂掉之后,nginx 将请求转发到其他 server 节点即可,服务仍将持续提供,只需变更缓存中客户端状态信息即可。
单体架构 IM 系统,从架构到设计,从协议到逻辑,其关键点都进行了 一 一 分析;最后,我们简单聊一下 server 的整体设计,server 通过 Go语言进行了实现,见下图。
-
主协程,不处理任何的业务逻辑,用于接收外部信号,如关闭程序等;
-
子协程,用于接收客户端连接,针对每一个客户端连接,子协程都会生成两个协程来维护该连接,即:每一条连接会有一个独立的协程组来维护(该协程组中有两个协程,一个用于读,一个用于写);
-
连接管理器,实现对所有连接的管理,从连接中读取请求交由业务逻辑模块处理;
-
业务逻辑模块,实现核心的业务逻辑,包括:登录、登出、心跳、发消息等;
-
在线用户管理器,维护连接当前 server 节点所有的客户端;如果消息接收方在当前 server 节点,在线用户管理器通过 管道(chan)将消息传输给连接管理器中消息接收方的连接;
-
通讯协议,是公共协议定义,由【连接管理器】【业务逻辑模块】【在线用户管理器】共同引用。
关于 “每一条连接会有一个独立的协程组来维护”,是 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 系统的关键问题,后续文章马上更新跟进......
相关文章:

单体架构 IM 系统之 Server 节点状态化分析
基于 http 短轮询模式的单体架构的 IM 系统见下图,即客户端通过 http 周期性地轮询访问 server 实现消息的即时通讯,也就是我们前面提到的 “信箱模型”。“信箱模型” 虽然实现非常容易,但是消息的实时性不高。 我们在上一篇文章(…...
java xml 文本解析
示例文本 <Message><MessageName>time_request</MessageName><Timestamp>20220217165432906359</Timestamp><Body><EQPID>CMMAB01-DTP01</EQPID></Body> </Message>示例代码 import org.w3c.dom.Document; impo…...
Docker占用空间太大磁盘空间不足清理妙招
docker占用空间太大了,磁盘空间不足,清理3妙招 清除所有已停止的容器(container)、未被任何容器所使用的卷(volume)、未被任何容器所关联的网络(network)、所有悬空镜像(…...

编程之路,从0开始:字符函数和字符串函数
Hello大家好!很高兴我们又见面了! 给生活添点passion,开始今天的编程之路! 目录 1、字符分类函数 2、字符转换函数 3、字符串函数 1、 strcpy 2、 strcat 3、 strcmp 4、strlen(s) 5、strstr(s1, s2) 6、 strtok(s1, s2…...

化工防爆巡检机器人:在挑战中成长,为化工安全保驾护航
随着全球能源需求的不断攀升,化工行业的安全性与高效性愈发受到关注。化工设施规模巨大,而且其中多数存在高风险因素,像是易燃易爆化学物质、高温环境、有毒有害物质以及高压设备等。仅2023年,国内危化品事故就多达652起ÿ…...

音频采样数据格式
音频信号在模拟到数字转换时,会涉及到多个关键参数,如采样率、位深度、通道数等。下面是常见的音频采样数据格式及其相关概念: 1. 采样率 (Sample Rate) 采样率指的是每秒钟对音频信号进行采样的次数,单位为赫兹 (Hz)。常见的值…...
【pytorch】常用强化学习算法实现(持续更新)
持续更新常用的强化学习算法,采用单python文件实现,简单易读 2024.11.09 更新:PPO(GAE); SAC2024.11.12 更新:OptionCritic(PPOC) "PPO" import copy import time import torch import numpy as np import torch.nn as …...

DAY59||并查集理论基础 |寻找存在的路径
并查集理论基础 并查集主要有两个功能: 将两个元素添加到一个集合中。判断两个元素在不在同一个集合 代码模板 int n 1005; // n根据题目中节点数量而定,一般比节点数量大一点就好 vector<int> father vector<int> (n, 0); // C里的一…...
Mybatis执行自定义SQL并使用PageHelper进行分页
Mybatis执行自定义SQL并使用PageHelper进行分页 基于Mybatis,让程序可以执行动态传入的SQL,而不需要在xml或者Select语句中定义。 代码示例 pom.xml 依赖 <dependency><groupId>org.mybatis.spring.boot</groupId><artifactId&g…...
OpenCV DNN
OpenCV DNN 和 PyTorch 都是常用的深度学习框架,但它们的定位、使用场景和功能有所不同。让我们来对比一下这两个工具: 1. 框架和功能 OpenCV DNN:OpenCV DNN 模块主要用于加载和运行已经训练好的深度学习模型,支持多种深度学习…...
什么时候需要复写hashcode()和compartTo方法
在Java编程中,复写(重写)hashCode()和compareTo()方法的需求通常与对象的比较逻辑和哈希集合的使用紧密相关。但请注意,您提到的compartTo可能是一个拼写错误,正确的方法名是compareTo()。以下是关于何时需要复写这两个…...
PostgreSQL 日志文件备份
随着信息安全的建设,在三级等保要求中,要求日志至少保留半年 180 天以上。那么 PostgreSQL 如何实现这一要求呢。 我们需要配置一个定时任务,定时的将数据库日志 log 下的文件按照生成的规则将超过一定时间的日志拷贝到其它的路径下…...

2023年MathorCup数学建模B题城市轨道交通列车时刻表优化问题解题全过程文档加程序
2023年第十三届MathorCup高校数学建模挑战赛 B题 城市轨道交通列车时刻表优化问题 原题再现: 列车时刻表优化问题是轨道交通领域行车组织方式的经典问题之一。列车时刻表规定了列车在每个车站的到达和出发(或通过)时刻,其在实际…...

数字农业产业链整体建设方案
1. 引言 数字农业产业链整体建设方案旨在通过数字化手段提升农业产业效率与质量,推动农业现代化进程。方案聚焦于资源数字化、产业数字化、全局可视化与决策智能化的实现,构建农业产业互联网平台,促进农业全流程、全产业链线上一体化发展。 …...
awk那些事儿:在awk中使用shell变量的两种方式
awk是Linux中一款非常好用的程序,可以逐行处理文件,并提供了强大的语法和函数,和grep、sed一起被称为“Linux三剑客”。 在使用awk处理文件时,有时会用到shell中定义的变量,由于在shell中变量的调用方式是通过$符号进…...

大数据面试题--kafka夺命连环问(后10问)
目录 16、kafka是如何做到高效读写? 17、Kafka集群中数据的存储是按照什么方式存储的? 18、kafka中是如何快速定位到一个offset的。 19、简述kafka中的数据清理策略。 20、消费者组和分区数之间的关系是怎样的? 21、kafka如何知道哪个消…...
智能量化交易的多样化策略与风险控制:中阳模型的应用与发展
随着金融市场的不断创新与发展,智能量化交易正逐渐成为金融投资的重要手段。中阳智能量化交易模型通过技术优势、策略优化与实时风险控制等多方面结合,为投资者提供了强有力的工具支持。本文将对中阳量化模型的技术细节、多策略组合与市场适应性进行深入…...

小皮PHP连接数据库提示could not find driver
最近遇到一个奇怪的问题,我的小皮上安装的8.0.2版本的php连接数据库正常。下载使用8.2.9时,没有php.ini,把php-development.ini改成 php.ini后,就提示could not find driver。 网上查了说把php.ini里的这几个配置打开,我也打开了&…...

2024.11.13(一维数组相关)
思维导图 1> 提示并输入一个字符串,统计该字符串中大写字母、小写字母、数字字符、空格字符的个数并输出 2> 提示并输入一个字符串,将该字符串中的所有字母挑选到一个新数组中,将所有的数字字符挑选到另一个新数组中。并且将数字字符对…...

豆包MarsCode算法题:数组元素之和最小化
数组元素之和最小化 问题描述思路分析分析思路解决方案 参考代码(Python)代码分析1. solution 函数2. 计算 1 2 3 ... n 的和3. 乘以 k 得到最终的数组元素之和4. 主程序(if __name__ __main__:)代码的时间复杂度分析&#x…...
云原生核心技术 (7/12): K8s 核心概念白话解读(上):Pod 和 Deployment 究竟是什么?
大家好,欢迎来到《云原生核心技术》系列的第七篇! 在上一篇,我们成功地使用 Minikube 或 kind 在自己的电脑上搭建起了一个迷你但功能完备的 Kubernetes 集群。现在,我们就像一个拥有了一块崭新数字土地的农场主,是时…...
ubuntu搭建nfs服务centos挂载访问
在Ubuntu上设置NFS服务器 在Ubuntu上,你可以使用apt包管理器来安装NFS服务器。打开终端并运行: sudo apt update sudo apt install nfs-kernel-server创建共享目录 创建一个目录用于共享,例如/shared: sudo mkdir /shared sud…...
DeepSeek 赋能智慧能源:微电网优化调度的智能革新路径
目录 一、智慧能源微电网优化调度概述1.1 智慧能源微电网概念1.2 优化调度的重要性1.3 目前面临的挑战 二、DeepSeek 技术探秘2.1 DeepSeek 技术原理2.2 DeepSeek 独特优势2.3 DeepSeek 在 AI 领域地位 三、DeepSeek 在微电网优化调度中的应用剖析3.1 数据处理与分析3.2 预测与…...

前端导出带有合并单元格的列表
// 导出async function exportExcel(fileName "共识调整.xlsx") {// 所有数据const exportData await getAllMainData();// 表头内容let fitstTitleList [];const secondTitleList [];allColumns.value.forEach(column > {if (!column.children) {fitstTitleL…...
【C++从零实现Json-Rpc框架】第六弹 —— 服务端模块划分
一、项目背景回顾 前五弹完成了Json-Rpc协议解析、请求处理、客户端调用等基础模块搭建。 本弹重点聚焦于服务端的模块划分与架构设计,提升代码结构的可维护性与扩展性。 二、服务端模块设计目标 高内聚低耦合:各模块职责清晰,便于独立开发…...

tree 树组件大数据卡顿问题优化
问题背景 项目中有用到树组件用来做文件目录,但是由于这个树组件的节点越来越多,导致页面在滚动这个树组件的时候浏览器就很容易卡死。这种问题基本上都是因为dom节点太多,导致的浏览器卡顿,这里很明显就需要用到虚拟列表的技术&…...

企业如何增强终端安全?
在数字化转型加速的今天,企业的业务运行越来越依赖于终端设备。从员工的笔记本电脑、智能手机,到工厂里的物联网设备、智能传感器,这些终端构成了企业与外部世界连接的 “神经末梢”。然而,随着远程办公的常态化和设备接入的爆炸式…...

技术栈RabbitMq的介绍和使用
目录 1. 什么是消息队列?2. 消息队列的优点3. RabbitMQ 消息队列概述4. RabbitMQ 安装5. Exchange 四种类型5.1 direct 精准匹配5.2 fanout 广播5.3 topic 正则匹配 6. RabbitMQ 队列模式6.1 简单队列模式6.2 工作队列模式6.3 发布/订阅模式6.4 路由模式6.5 主题模式…...

【MATLAB代码】基于最大相关熵准则(MCC)的三维鲁棒卡尔曼滤波算法(MCC-KF),附源代码|订阅专栏后可直接查看
文章所述的代码实现了基于最大相关熵准则(MCC)的三维鲁棒卡尔曼滤波算法(MCC-KF),针对传感器观测数据中存在的脉冲型异常噪声问题,通过非线性加权机制提升滤波器的抗干扰能力。代码通过对比传统KF与MCC-KF在含异常值场景下的表现,验证了后者在状态估计鲁棒性方面的显著优…...

Python 实现 Web 静态服务器(HTTP 协议)
目录 一、在本地启动 HTTP 服务器1. Windows 下安装 node.js1)下载安装包2)配置环境变量3)安装镜像4)node.js 的常用命令 2. 安装 http-server 服务3. 使用 http-server 开启服务1)使用 http-server2)详解 …...