消息队列(五):虚拟主机设计

虚拟主机存在的意义

一个虚拟主机类似于 MySQL 的 database,把交换机,队列,绑定,消息....进⾏逻辑上的隔离,⼀个服务器可以有多 个虚拟主机,此处我们项⽬就设计了⼀个虚拟主机(VirtualHost)。
虚拟主机需要为上层提供可调用的 API,针对 VirtualHost 这个类, 作为业务逻辑的整合者

核心 API 如下:

虚拟主机参数的设计

为了方便后期的可扩展,我们给每个虚拟主机设置一个主机名,所以有了第一个参数 : virturalHostName

由于虚拟主机要操作内存中的对象,所以需要提供 以下两个类的实例化对象:                          MemoryDataCenter    DiskDataCenter

我们先前提到过对于虚拟机,我们设置了三种交换机,每种交换机对应着不同的转发规则:  Router                  实例化对象,这也是个核心类,等会进行一个补充。

此外,我们还需要记录以下消费者的信息,毕竟虚拟主机设计出来是要被调用的。

至于其他的聊到再加。

虚拟主机方法的设计

创建交换机(exchangeDelcare)

一个虚拟主机需要管理多个交换机,在不同的虚拟主机中可以存在同名的交换机,我们在这里给他们进行一个逻辑上的隔离设计。这个设计有多种方案,比如:

  • ⽅案⼀:参考数据库设计,“⼀对多”⽅案,⽐如给交换机表,添加个属性,虚拟主机 id/name
  • ⽅案⼆:交换机的名字 = 虚拟主机名字 + 交换机的真实名字

这里采用的是方案二,采用二有啥好处呢?

我们利用同样的方式给队列进行隔离,这样就进一步对绑定进行了隔离;再进一步说就是,消息和队列是强相关的,队列名区分开了,消息自然也就区分开了。

创建交换机大致流程:

  1. 把交换机名字加上虚拟主机名字作为前缀(只要是操作交换、队列、绑定都需要这一步操作,我就不在每一个流程中都说明一次了)
  2. 判断交换机是否存在,直接通过内存查询
  3. 真正去构造交换机对象(把参数都赋值上去)
  4. 当参数为 durable的时候,将交换机对象写⼊硬盘
  5. 将交换机写⼊内存

此外,除了第一步赋值名字不需要加锁外,其余的都需要加一个锁(如果不加锁,可以自己思考以下后果),交换机不加锁不会影响。因为这里的交换机锁会用到好几次,不如就把他设为一个属性:

// 操作交换机的锁对象
private final Object exchangeLocker = new Object();

上述逻辑, 先写硬盘, 后写内存. 目的就是因为硬盘更容易写失败. 如果硬盘写失败了, 内存就不写了;要是先写内存, 内存写成功了, 硬盘写失败了, 还需要把内存的数据给再删掉. 就比较麻烦了

这就不演示代码了,这一篇的代码非常多,在文章末尾,我会存放相关代码,根据我给出的方法名,可以查看到相关代码。

删除交换机(exchangeDelete)

删除交换机,顾名思义就是将虚拟主机上的交换机给删除掉;大致流程如下:

  1. 根据交换机的名字找到对应的交换机
  2.  删除硬盘数据(查看是否持久化,持久化了就删,没有就没必要)
  3.  删除内存中数据

三个步骤都需要给他们加锁,用的也是  exchangLocker 这个锁。

创建队列(queueDelcare)

  1. 判断队列是否存在
  2. 不存在则创建队列,设定参数
  3. 队列参数 durable 为 true的时候存⼊硬盘
  4. 将队列写⼊到内存

同样的,这里是操作队列,也需要给它进行一个加锁操作,这个队列又不同于交换机,所以给它设置另外一个锁,同样的,不只是这一步需要这个锁,于是我将其设为一个属性:

// 操作队列的锁对象
private final Object queueLocker = new Object();

删除队列(queueDelete)

  1. 判断队列是否存在
  2. 存在则删除,先在硬盘删除
  3. 在内存中删除

同样是需要加队列锁。

创建绑定(queueBind)

  1. 判断当前绑定在不在
  2. 验证当前的 routingKey 合不合法
  3. 如果合法,就创建绑定,设置参数
  4. 从内存中获取下绑定关系的队列和交换机是否存在
  5. 都存在,再次判定队列和交换机的durable是否都为 true
  6. 都为 true 则存⼊硬盘
  7. 再写⼊内存

关于绑定呢,我们这里既操作了队列,有需要交换机,所以需要使用到上述两个锁:

删除绑定(queueUnbind)

关于删除绑定,这有个依赖关系问题,就是 线程 A  先删除了队列,而此时,线程B 再去删除绑定消息时就会失败,关于此问题 我们有两种方法:

  1. ⽅案⼀:参考类似于 MySQL 的外键⼀样,删除交换机/队列的时候,判定⼀下当前队列/交换机是 否存在对应的绑定,如果存在,则禁⽌删除,要求先解除绑定,再尝试删除
  2. ⽅案⼆:直接删除,不判断 交换机和队列是否存在

我们采用第二种方式,直接删(暴力删除),无论绑定是否持久化了, 都尝试从硬盘删一下. 就算不存在, 这个删除也无副作用

大致流程就是:

  1. 获取绑定是否存在
  2. 删除硬盘上的数据,需要判断该绑定 durable 是否为 true
  3. 从内存中删除绑定

同样的,这里也需要加两个锁。

发送消息(basicPublish)

发送消息就是指:将消息发送到指定的 交换机/队列 中。

转发消息的大致逻辑如下图:

发送消息的时候,就会往 ConsumerManager 类中的阻塞队列种

BlockingQueue<String> tokenQueue

存在该队列,表示该队列存在消息。

关于 Consumer 相关的现在先不讲解,后面放在消费者管理模块细讲。

大致思路就是:

  1. 获取交换机名字
  2. 确认 routingKey 是否合法
  3. 查找交换机对象
  4. 判定交换机类型
  5. 根据不同的交换机作出不同的消息转发

如果是直接交换机,那么 routingKey 就是队列的名字,直接把消息写入指定的队列中;随后构造消息对象,查找该队列名对应的对象;(判断队列是否存在)最后给队列写入消息。

至于其他交换机:

  1. 需要找到该交换机关联的所有绑定,并且遍历这些绑定对象
  2. 获取绑定对象,判定对应的队列是否存在(不存在不需要抛异常,因为可能有多个队列,不能因为一个队列的失败,影响到其他队列的消息的传输)
  3. 构造对象,再判断交换机类型

如果是扇出交换机,那么就是直接给所有的绑定的队列都要转发消息。

如果是主题交换机,需要 匹配  bindingKey 和 routingKey 。

Topic 交换机转发规则

bingdingKey(创建绑定的时候,给绑定指定的特殊字符串) 我们约定:

  • 只存在 数字、字母、下划线
  • 使用 “  .  ”  把整个 routingKey 分为若干个部分  形如: aaa.vvv.eewe
  • 存在两种特殊符号,做为通配符
    • 一个是 *    形如 : aaa.*.bbb(只能作为被 . 分割单独的存在
    • 一个是 #   形如 :  aaa.#.bbb

上述规则,是根据 AMQP 协议规定的

验证 bindingKey 是否合法(checkBindingKey)

大致逻辑:

这一段代码其实也挺简单的,看源代码就好。

验证 routingKey 是否合法(checkRoutingKey)

大致逻辑:

匹配规则

约定,啥叫匹配成功:

大致逻辑图:

订阅消息(basicComsume)

什么是函数式接口

由于 Java的函数不能脱离类的存在,为了实现 lambda 表达式, Java 引入了函数式接口

如何实现?

  1. interface
  2. 只能有⼀个⽅法
  3. 还需要加 @FunctionalInterface 注解

一个虚拟主机种,有很多队列,每个队列上都有很多消息。那么针对是哪个消费者订阅了哪条队列的消息需要进行一个管理。

订阅消息

添加一个队列的订阅者,当队列收到消息之后,就需要把消息对送给他的订阅者;

根据传入的 autoAck 的值,判断是否要手动删除消息。

这一步涉及到了消费者等等,这一章就先到这。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/82984.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

GO-日志分析

GO-日志分析 log包简介 Go提供了logger包来做日志记录。使用方式如下所示 package mainimport ("log""os" )func main() {// 创建一个新的日志文件.默认是stdOutfile, err : os.Create("app.log")if err ! nil {log.Fatal(err)}defer file.Cl…

源码编译Qt 5.15.9+msvc2019

官方文档里给出了详细步骤&#xff1a; Building Qt Sources Building Qt 5 from Git (Wiki) 注&#xff1a;本文基于windows11vs2019x64qt5.15.9&#xff0c;不编译Qt WebEngine 归纳总结如下&#xff1a; 准备阶段 Qt for Windows - Requirements 安装python&#xff0c;…

[npm]package.json文件

[npm]package.json文件 生成 package.jsonpackage.json 必须属性nameversion 描述信息descriptionkeywordsauthorcontributorshomepagerepositorybugs 依赖配置dependenciesdevDependenciespeerDependenciesoptionalDependenciesbundledDependenciesengines 脚本配置scriptscon…

无涯教程-JavaScript - PI函数

描述 PI函数返回数字3.14159265358979,数学常数pi,精确到15位数字。 语法 PI ()争论 PI函数语法没有参数。 适用性 Excel 2007,Excel 2010,Excel 2013,Excel 2016 Example JavaScript 中的 PI函数 - 无涯教程网无涯教程网提供描述PI函数返回数字3.14159265358979,数学常…

半导体划片机工艺应用

半导体划片工艺是半导体制造过程中的重要步骤之一&#xff0c;主要用于将大尺寸的晶圆切割成小片&#xff0c;以便进行后续的制造和封装过程。以下是一些半导体划片工艺的应用&#xff1a; 晶圆划片&#xff1a;在半导体制造过程中&#xff0c;需要将大尺寸的晶圆切割成小片&am…

虚拟机(VMM)

一、虚拟机概念 虚拟机又名虚拟机管理程序、虚拟机监控程序、VMM 使用虚拟化技术&#xff0c;将一台物理机器虚拟化为多台虚拟机器&#xff0c;每台虚拟机器都可以独立一个操作系统。 传统的计算机&#xff0c;一台物理机器只能运行一个操作系统。 二、虚拟机的分类 第一类VMM&…

【Linux 服务器运维】定时任务 crontab 详解 | 文末送书

文章目录 前言一、crontab 介绍1.1 什么是 crontab1.2 crontab 命令工作流程1.3 Linux 定时任务分类 二、crontab 用法详解2.1 crond 服务安装2.2 crontab 文件内容分析2.3 crontab 命令用法2.3.1 查看定时任务列表2.3.2 编辑/创建定时任务2.3.3 删除定时任务2.3.4 其他 cronta…

微服务07-认识MQ+RabbitMQ入门

1.前言 了解同步调用和异步调用 1.1.同步调用 比如这里的支付服务&#xff0c;需要等待订单服务、短信服务…执行完毕才能执行&#xff0c;这样支付整个流程完毕需要500ms 然后如果订单、仓储等其中一个服务挂掉了&#xff0c;那么支付服务请求请求不了&#xff0c;挂掉的服…

typescrip接口 interface详解,以及ts实现多态

ts 接口 当一个对象类型被多次使用时,一般会使用接口(interface)来描述对象的类型,达到复用的目的 示例如下 当一个对象类型被多次使用时,可以看到,很明显代码有大量的冗余 let personTom: { name: string, age?: number, sayHi(name: string): void } {name: Tom,sayHi(n…

JVM G1垃圾回收器学习笔记

前言 最近在工作中遇到频繁FullGC且YoungGC时间有时特别长的情况&#xff0c;而自己对JVM的垃圾回收也是一知半解&#xff0c;因此需要对JVM做系统的了解&#xff0c;为快速解决工作中的问题&#xff0c;能有效分析GC日志和业务代码&#xff0c;先从G1垃圾回收器开始学习&…

【操作系统笔记】程序运行机制CPU指令集

内存地址 指针 / 引用 指针、引用本质上就是内存地址&#xff0c;有了内存地址就可以操作对应的内存数据了。 不同的数据类型 字节序 大端序&#xff08;Big Endian&#xff09;&#xff1a;字节顺序从低地址到高地址顺序存储的字节序小端序&#xff08;Little Endian&#…

Spring Boot2.7生成用于登录的图片验证码

先在 pom.xml 注入依赖 <dependency><groupId>com.github.penggle</groupId><artifactId>kaptcha</artifactId><version>2.3.2</version> </dependency>然后 需要在配置文件中声明一下DefaultKaptcha 的 bean对象 然后 我们…

76、SpringBoot 整合 MyBatis------使用 sqlSession 作为 Dao 组件(就是ssm那一套,在 xml 写sql)

就是 ssm 那套&#xff0c;在xml 上面写sql ★ 基于SqlSession来实现DAO组件的方式 - MyBatis提供的Starter会自动在Spring容器中配置SqlSession&#xff08;其实SqlSessionTemplate实现类&#xff09;、并将它注入其他组件&#xff08;如DAO组件&#xff09;- DAO组件可直接…

js-nginx配置字段适配前端服务

当我们有这样一个需求&#xff0c;前端同一套代码&#xff0c;但要根据一些特殊字段展示不同的内容&#xff0c;比如我们有一个场id&#xff0c;暂时这个场id放在前端&#xff0c;后端根据这个场id返回不同的数据&#xff0c;这里前端部署用的是yaml文件&#xff0c;平台是ranc…

如何使用Python构建OTP验证系统?

即使您的密码被盗&#xff0c;OTP验证系统也可以充当安全的关键要素。它让您无需记住密码&#xff0c;充当额外的安全层&#xff0c;并降低了网络钓鱼的风险。 不妨学习用Python建立一个OTP验证系统&#xff0c;它会向您的手机号码发送一个OTP&#xff0c;有效期只有两分钟&am…

linux 文件锁

建议锁,强制锁,记录锁的概念 建议锁&#xff1a; 如果某一个进程对一个文件持有一把锁之后&#xff0c;其他进程仍然可以直接对文件进行操作(open, read, write)而不会被系统禁止&#xff0c;即使这个进程没有持有锁。只是一种编程上的约定。建议锁只对遵守建议锁准则的进程生…

知识付费平台开发技术实践:构建数字学习的未来

引言 知识付费平台的兴起正在塑造着数字学习的未来。本文将介绍一些关键的技术实践&#xff0c;帮助开发者构建强大的知识付费平台&#xff0c;提供出色的数字学习体验。 1. 选择适当的技术栈 在开始知识付费平台的开发之前&#xff0c;首要任务是选择适当的技术栈。这包括…

TS中的数据类型

一、number类型 let c: number; c 10; c "hello"; // 不能复制string类型 二、string类型 let d: string; d "hello"; d 10; // 不能复制number类型 三、boolean类型 let e: boolean true; e false; e 10; // 不能赋值true和false以外的值 四…

嵌入式裸机轻量级架构探索总结

为什么会想着探索下嵌入式裸机的架构呢&#xff1f;是因为最近写了一个项目&#xff0c;项目开发接近尾声时&#xff0c;发现了一些问题&#xff1a; 1、项目中&#xff0c;驱动层和应用层掺杂在一起&#xff0c;虽然大部分是应用层调用驱动层&#xff0c;但是也存在驱动层调用…

笔试面试相关记录(4)

&#xff08;1&#xff09;实现防火墙的主流技术有哪些&#xff1f; 实施防火墙主要采用哪些技术 - 服务器 - 亿速云 (yisu.com) &#xff08;2&#xff09; char arr[][2] {a, b, c, d}; printf("%d", *(arr1)); 输出的是谁的地址&#xff1f;字符c 测试代码如下…