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

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

在上一篇技术短文(单体架构 IM 系统之核心业务功能实现)中,我们讨论了 “信箱模型” 在单体架构 IM 系统中的应用,“信箱模型” 见下图。

客户端 A 将 “信件” 投入到客户端 B 的 “信箱” 中,然后客户端 B 去自己的 “信箱” 中取回自己的 “信件”, “信箱模型” 故此得名。

“信箱模型” 中,所有动作由客户端触发,作为 “信箱” 的服务端被动响应即可; 所以 “信箱模型” 实现逻辑非常简单,但是对于 “信件” 的实时性不高; 客户端 B 想要尽快获取到自己的 “信件”,只能周期性地高频请求。我们知道,在 IM 系统中,所有客户端都在高频请求时,无效请求(即没有获取到自己的消息)是非常高的,这对服务器资源和网络资源来说是一种浪费。 在当前单体架构 IM 系统背景下(单体架构 IM 系统之架构设计),如何进行优化呢?

我们知道,http 是短连接协议,即一次客户端请求和服务端回复后,连接就断开了;在不更换协议(更换协议代价很大)的前提下,我们对其进行优化:服务端接收请求到回复客户端的时间,完全是可以控制的。 描述到这里,很多同学应该已经非常清楚了,即:将短连接的 http 访问优化为 http 长轮询方式,通过 http 长轮询方式模拟出 “长连接” 的效果。 http 长轮询见下图。

http-client 向 http-server 发出请求,http-server 拿到请求后,会立刻 hold 住该请求不返回; http-server 返回响应需要满足下面任何一个条件:

  1. 超过了一定时间,比如15秒,即超时了;(该条件减少了无效的 http 请求)

  2. 在超时之前产生了该 http-client 的数据。(该条件提高了消息的实时性)

http-client 收到响应后,会立刻再次向 http-server 发出请求,重复上述过程。

在单体架构 IM 系统的服务端,只需改造 http 部分,增加一个【http 长轮询】的插件即可大大提高消息的实时性,改造成本低,见效快! 那么 【http 长轮询】插件应该如何实现呢?我们分别介绍 “定时器” 和 “时间轮” 两种实现方案。

方案一、 定时器方案

定时器方案非常容易理解,即针对每一个 http 客户端,当服务端接收到请求后,就开启 15秒(假设超时时间是 15秒)的定时器;15秒内若产生了消息,则立刻返回,否则就等15秒后超时返回。我们基于 Go 语言代码进行描述,如下。

chTimeout := make(chan struct{}, 1)				//定义 “超时管道”
go func() {time.Sleep(time.Second * POLLING_TIMEOUT)	//定时器15秒超时fmt.Printf("INFO | [heartHandler] timeout")chTimeout <- struct{}{}						//超时后,向“超时管道” 中写入元素
}()select {case <-chTimeout:							//从 “超时管道” 中读数据fmt.Fprint(w, "nonthing")case msg := <-chPushMsg:					//从 “消息管道” 中读数据bs, _ := json.Marshal(msg)fmt.Fprint(w, string(bs))
}

通过 Go 语言代码实现定时器方案非常简单!

在 Go 语言中有一个类似于 “IO 多路复用” 的用法,即通过 select-case 语句实现对多个管道的监听,哪个管道先有了数据,就执行哪个 case 语句。

基于此,我们定义了两个管道,一个是用于 15 秒定时器超时的 “超时管道”,一个是传输消息的 “消息管道”; “超时管道” 在一个独立的协程中,由 “定时器” 控制,超时后向 “超时管道” 写入一个元素数据 (即 struct{},内容不重要,有数据即可表示超时)。select-case 语句非常巧妙地帮助我们实现了 要么15秒后超时返回,要么15秒内有消息即可返回的选择情况。

我们分析一下这个定时器实现方案:服务端需要针对每一次的 http 请求,分别启动一个 “定时器”;”定时器“ 在本质上是一个计算脉冲的计数器,达到设定值之后,通过软中断方式向 CPU 发起中断请求;当 http 客户端并发请求增大之后,服务端同时运行的 “定时器” 也会增多,于是软中断也增多,CPU 会经常性的停下手头工作去处理中断请求,CPU的工作效率会大大降低。那么,在 http 客户端数量不断增多的时候,如何进行优化呢? 下面的时间轮方案可以非常优雅地解决这个问题。

方案二、时间轮方案

在时间轮实现方案只需要一个每秒走一格的定时器即可,其核心思想是将同一秒内超时的所有客户端进行批量处理;见下图。

在该时间轮实现方案中,需要准备三个数据结构:

  1. 一个作为 “时间轮” 的循环队列,该时间轮的指针,每秒钟走一格,走一圈是一个完整的超时周期(图中超时时间是 13秒);

  2. 一个用户维度的 map<uid, 时间轮时间>,该 map 的 key 是用户 uid,value 是时间轮指针所指向的时间刻度;

  3. 一个时间轮时间维度的 map<时间轮时间, uid列表>,该 map 的 key 是时间轮的刻度,value 是这个时间刻度时所有发起 http 请求的 uid 列表。

当客户端发出 http 请求到服务端时,服务端将用户和当前时间刻度信息分别写入到上述的两个 map 中;在 13 秒超时之前,如果产生了用户的消息,则从上述两个 map 中删除用户和时间刻度信息;时间轮当前指针每走一格所指向的时间刻度,该时间刻度对应的用户列表就是 13 秒前发出 http 请求的用户列表,这些用户就是超时的客户端,需要超时返回,即返回空的 http 响应。

这样描述可能比较抽象,我们举一个例子:

  1. 假设当前时间轮指针指向了当前时间刻度 2,此时 有三个客户端分别是 101、102、103 发出 http 请求到服务端,服务端需要在 第一个 map 中分别写入 <101, 2> , <102, 2>,<103, 3>,在第二个 map 中写入 <2, [101, 102, 103]>;

  2. 三秒后,时间轮指针指向了当前时间刻度 5,此时产生了用户 102 的消息,服务端需要先从第一个 map 中删除元素 <102, 2>(同时记录下时间刻度 2,方便后续操作),再从第二个 map 中删除 102 的记录,删除后的map为 <2, [101, 103]>;

  3. 九秒后,在时间轮指针指向当前刻度 2 时,此时第二个 map 中,key 是 2 的所有的uid列表,即 [101, 103],就是所有超时的客户端列表,需要超时返回。

时间轮实现方案,通过一个定时器实现了对同一秒内超时的所有客户端的批量处理。

最后,对文中关键进行总结:

1、基于 http 周期轮询方式的 “信箱模型”,消息的实时性不高,可优化为 http 长轮询方式,通过 http 长轮询模拟出 “长连接” 的效果;

2、http 长轮询有两种实现方案:定时器方案和时间轮方案;

3、Go 语言实现的定时器方案,通过 select-case 语句实现了对多个管道的多路复用监听,达到了随时产生消息随时返回或超时返回的目的; 定时器方案适用于客户端数量较少的情况;

4、时间轮方案实现了对同一秒内所有超时客户端的批量处理,该方案需要三个数据结构:循环队列,map<uid,时间>, map<时间, uid列表>。

大家思考一下:

在该单体架构的 IM 系统中,http 短轮询方式优化成 http 长轮询方式后,点对点消息发送逻辑,需要调整吗?

(http 短轮询方式的消息发送逻辑参见技术短文:单体架构 IM 系统之核心业务功能实现)

相关文章:

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

在上一篇技术短文&#xff08;单体架构 IM 系统之核心业务功能实现&#xff09;中&#xff0c;我们讨论了 “信箱模型” 在单体架构 IM 系统中的应用&#xff0c;“信箱模型” 见下图。 客户端 A 将 “信件” 投入到客户端 B 的 “信箱” 中&#xff0c;然后客户端 B 去自己的 …...

Android Studio加载旧的安卓工程项目报错处理

文章目录 Invalid Gradle JDK configuration foundNDK not configuredCMake 3.10.2 was not found安装cmake适配cmake版本号 com.intellij.openapi.externalSystem.model.ExternalSystemExceptiongradle版本过低或下载不了下载gradle与依赖库超时替换gradle国内源替换Maven 仓库…...

阿里公告:停止 EasyExcel 更新与维护

最近&#xff0c;阿里发布公告通知&#xff0c;将停止对知名 Java Excel 工具库 EasyExcel 的更新和维护。EasyExcel 由阿里巴巴开源&#xff0c;作者是玉箫&#xff0c;在 GitHub 上拥有 30k stars、7.5k forks 的高人气。 据悉&#xff0c;EasyExcel 作者玉箫去年已从阿里离…...

Spring 中的 BeanWrapper

BeanWrapper 是 Spring 框架中的一个接口&#xff0c;它提供了一种方式来设置和获取 JavaBean 的属性。JavaBean 是一种特殊的 Java 类&#xff0c;遵循特定的编码约定&#xff08;例如&#xff0c;私有属性和公共的 getter/setter 方法&#xff09;&#xff0c;通常用于封装数…...

2024鹏城杯msic部分WP

MISC 网安第一课 查找字符key&#xff0c;发现key1&#xff0c;但是没看到key2 后缀改为zip&#xff0c;打开以后发现不一样的地方&#xff0c;三张图片和一个misc文件夹 图片放到010看一眼 编号为1的图片在文件尾发现key2 misc文件夹中是一个out.pcb&#xff0c;放到010发现…...

DAY23|回溯算法Part02|LeetCode: 39. 组合总和 、40.组合总和II 、131.分割回文串

目录 LeetCode: 39. 组合总和 基本思路 C代码 LeetCode: 40.组合总和II 基本思路 C代码 LeetCode: 131.分割回文串 基本思路 C代码 LeetCode: 39. 组合总和 力扣代码链接 文字讲解&#xff1a;LeetCode: 39. 组合总和 视频讲解&#xff1a;带你学透回溯算法-组合总和…...

go map

1、数据结构 // A header for a Go map. type hmap struct {// Note: the format of the hmap is also encoded in cmd/compile/internal/reflectdata/reflect.go.// Make sure this stays in sync with the compilers definition.count int // # live cells size of map.…...

三十七、Python基础语法(异常)

在 Python 中&#xff0c;异常是在程序执行过程中发生的错误情况。当出现异常时&#xff0c;程序的正常执行流程会被中断&#xff0c;并尝试寻找相应的异常处理机制来处理这个错误。 一、异常的类型 Python 中有很多内置的异常类型&#xff0c;例如&#xff1a; ZeroDivision…...

ThreadLocal的熟悉与使用

目录 1.ThreadLocal介绍2.ThreadLocal源码解析2.1 常用方法2.2 结构设计2.3 类图2.4 源码分析2.4.1 set方法分析2.4.2 get方法分析2.4.3 remove方法分析 3.ThreadLocal内存泄漏分析3.1 相关概念3.1.1 内存溢出3.1.2 内存泄漏3.1.3 强引用3.1.4 弱引用 3.2 内存泄漏是否和key使用…...

如何使用 Puppeteer 和 Browserless 抓取亚马逊产品数据?

您可以在亚马逊上找到所有有关产品、卖家、评论、评分、特价、新闻等的相关且有价值的信息。无论是卖家进行市场调研还是个人收集数据&#xff0c;使用高质量、便捷且快速的工具将极大地帮助您准确地抓取亚马逊上的各种信息。 为什么抓取亚马逊产品数据很重要&#xff1f; 亚…...

使用Python求解经典“三门问题”,揭示概率的奇妙之处

三门问题&#xff08;Monty Hall Problem&#xff09;是经典的概率问题&#xff0c;描述了一位游戏选手在三个门中选择一扇门&#xff0c;其中一扇门后有奖品&#xff0c;其余两扇门后是空的。选手做出选择后&#xff0c;主持人会打开另一扇空门&#xff0c;然后给选手一次更改…...

数据库基础(6) . DDL

3.2.DDL 数据定义语言 DDL : Data Definition Language 用于创建新的数据库、模式&#xff08;schema&#xff09;、表&#xff08;tables&#xff09;、视图&#xff08;views&#xff09;以及索引&#xff08;indexes&#xff09;等。 常见的DDL语句包括SHOW、CREATE、DRO…...

2024 年度分布式电力推进(DEP)系统发展探究

分布式电力推进 &#xff08;DEP&#xff09; 的发明是为了尝试和改进现代飞机&#xff1a;我们如何提高飞机的效率&#xff1f;提高它的机动性&#xff1f;缩短它的起飞和着陆距离&#xff1f; DEP 概念有望在提高性能的同时减少燃料消耗&#xff0c;在我们孜孜不倦地努力使航…...

vue通过iframe方式嵌套grafana图表

文章目录 前言一、iframe方式实现xxx.xxx.com拒绝连接登录不跳转Cookie 的SameSite问题解决不显示额外区域(kiosk1) 前言 我们的前端是vue实现的&#xff0c;监控图表是在grafana中的&#xff0c;需要在项目web页面直接显示grafana图表 一、iframe方式实现 xxx.xxx.com拒绝连…...

简单介绍下 Java 中的 @Validated 和 @Valid 注解的区别?

文章目录 Valid&#xff1a;专注单个对象的深度验证适用场景使用示例小结 Validated&#xff1a;聚焦接口分组的批量验证适用场景使用示例小结 主要区别总结如何选择&#xff1f;总结推荐阅读文章 在 Java 开发中&#xff0c;为了确保输入数据符合我们的要求&#xff0c;少不了…...

SpringBoot配置Rabbit中的MessageConverter对象

SpringAMQP默认使用SimpleMessageConverter组件对消息内容进行转换 SimpleMessageConverter&#xff1a; only supports String, byte[] and Serializable payloads仅仅支持String、Byte[]和Serializable对象Jackson2JsonMessageConverter&#xff1a;was expecting (JSON Str…...

C++ 错题本--duplicate symbol问题

顾名思义, duplicate symbol是重复符号的意思! 代码是用来做什么的(问题缘由 & 代码结构) 写排序算法, 提出了一个公共的头文件用来写一些工具方法, 比如打印数组内容. 以便于不同文件代码需要打印数组内容的时候,直接引入相关头文件即可, 但是编译时出现了 duplicate sym…...

Cursor的chat与composer的使用体验分享

经过一段时间的试用&#xff0c;下面对 Composer 与 Chat 的使用差别进行总结&#xff1a; 一、长文本及程序文件处理方面 Composer 在处理长文本时表现较为稳定&#xff0c;可以对长文进行更改而不会出现内容丢失的情况。而 Chat 在更改长的程序文件时&#xff0c;有时会删除…...

【优选算法 — 滑动窗口】最大连续1的个数 将 x 减到0的最小操作数

最大连续1的个数 最大连续1的个数 题目描述 题目解析 给我们一个元素全是0或者1的数组&#xff0c;和一个整数 k &#xff0c;然后让我们在数组选出最多的 k 个0&#xff1b;这里翻转最多 k 个0的意思&#xff0c;是翻转 0 的个数< k&#xff0c;而不是一定要翻转 k …...

《TCP/IP网络编程》学习笔记 | Chapter 8:域名及网络地址

《TCP/IP网络编程》学习笔记 | Chapter 8&#xff1a;域名及网络地址 《TCP/IP网络编程》学习笔记 | Chapter 8&#xff1a;域名及网络地址域名系统什么是域名&#xff1f;DNS 服务器IP 地址和域名之间的转换使用域名的必要性利用域名获取 IP 地址利用 IP 地址获取域名 基于 Wi…...

模型参数、模型存储精度、参数与显存

模型参数量衡量单位 M&#xff1a;百万&#xff08;Million&#xff09; B&#xff1a;十亿&#xff08;Billion&#xff09; 1 B 1000 M 1B 1000M 1B1000M 参数存储精度 模型参数是固定的&#xff0c;但是一个参数所表示多少字节不一定&#xff0c;需要看这个参数以什么…...

从零实现富文本编辑器#5-编辑器选区模型的状态结构表达

先前我们总结了浏览器选区模型的交互策略&#xff0c;并且实现了基本的选区操作&#xff0c;还调研了自绘选区的实现。那么相对的&#xff0c;我们还需要设计编辑器的选区表达&#xff0c;也可以称为模型选区。编辑器中应用变更时的操作范围&#xff0c;就是以模型选区为基准来…...

【Redis技术进阶之路】「原理分析系列开篇」分析客户端和服务端网络诵信交互实现(服务端执行命令请求的过程 - 初始化服务器)

服务端执行命令请求的过程 【专栏简介】【技术大纲】【专栏目标】【目标人群】1. Redis爱好者与社区成员2. 后端开发和系统架构师3. 计算机专业的本科生及研究生 初始化服务器1. 初始化服务器状态结构初始化RedisServer变量 2. 加载相关系统配置和用户配置参数定制化配置参数案…...

【第二十一章 SDIO接口(SDIO)】

第二十一章 SDIO接口 目录 第二十一章 SDIO接口(SDIO) 1 SDIO 主要功能 2 SDIO 总线拓扑 3 SDIO 功能描述 3.1 SDIO 适配器 3.2 SDIOAHB 接口 4 卡功能描述 4.1 卡识别模式 4.2 卡复位 4.3 操作电压范围确认 4.4 卡识别过程 4.5 写数据块 4.6 读数据块 4.7 数据流…...

系统设计 --- MongoDB亿级数据查询优化策略

系统设计 --- MongoDB亿级数据查询分表策略 背景Solution --- 分表 背景 使用audit log实现Audi Trail功能 Audit Trail范围: 六个月数据量: 每秒5-7条audi log&#xff0c;共计7千万 – 1亿条数据需要实现全文检索按照时间倒序因为license问题&#xff0c;不能使用ELK只能使用…...

工程地质软件市场:发展现状、趋势与策略建议

一、引言 在工程建设领域&#xff0c;准确把握地质条件是确保项目顺利推进和安全运营的关键。工程地质软件作为处理、分析、模拟和展示工程地质数据的重要工具&#xff0c;正发挥着日益重要的作用。它凭借强大的数据处理能力、三维建模功能、空间分析工具和可视化展示手段&…...

优选算法第十二讲:队列 + 宽搜 优先级队列

优选算法第十二讲&#xff1a;队列 宽搜 && 优先级队列 1.N叉树的层序遍历2.二叉树的锯齿型层序遍历3.二叉树最大宽度4.在每个树行中找最大值5.优先级队列 -- 最后一块石头的重量6.数据流中的第K大元素7.前K个高频单词8.数据流的中位数 1.N叉树的层序遍历 2.二叉树的锯…...

以光量子为例,详解量子获取方式

光量子技术获取量子比特可在室温下进行。该方式有望通过与名为硅光子学&#xff08;silicon photonics&#xff09;的光波导&#xff08;optical waveguide&#xff09;芯片制造技术和光纤等光通信技术相结合来实现量子计算机。量子力学中&#xff0c;光既是波又是粒子。光子本…...

论文笔记——相干体技术在裂缝预测中的应用研究

目录 相关地震知识补充地震数据的认识地震几何属性 相干体算法定义基本原理第一代相干体技术&#xff1a;基于互相关的相干体技术&#xff08;Correlation&#xff09;第二代相干体技术&#xff1a;基于相似的相干体技术&#xff08;Semblance&#xff09;基于多道相似的相干体…...

使用LangGraph和LangSmith构建多智能体人工智能系统

现在&#xff0c;通过组合几个较小的子智能体来创建一个强大的人工智能智能体正成为一种趋势。但这也带来了一些挑战&#xff0c;比如减少幻觉、管理对话流程、在测试期间留意智能体的工作方式、允许人工介入以及评估其性能。你需要进行大量的反复试验。 在这篇博客〔原作者&a…...