2024.2.4 模拟实现 RabbitMQ —— 实现核心类

目录

引言

创建 Spring Boot 项目

编写 Exchange 实体类

编写 Queue 实体类

编写 Binding 实体类

编写 Message 实体类


引言

  • 上图为模块设计图

  • 此处实现核心类为了简便,我们引用 Lombok(可点击下方链接了解 Lombok 的使用)

IDEA 配置 Lombok

创建 Spring Boot 项目

1、创建一个 Spring Boot 项目 并 创建相应的目录结构


注意:

  • 消息队列中存在下列比较核心的概念
  1. 交换机(exchange)
  2. 队列(queue)
  3. 绑定(binding)
  4. 消息(message)
  • 上述这些均存在于 Broker Server 中,所以我们在 mqserver 目录中进行创建实体类

编写 Exchange 实体类

1、使用一个枚举类 枚举出三种交换机类型

public enum ExchangeType {DIRECT(0),FANOUT(1),TOPIC(2);private final int type;private ExchangeType(int type) {this.type = type;}private int getType() {return type;}
}

2、编写 Exchange 实体类

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.Data;import java.util.HashMap;
import java.util.Map;/*
* 这个类表示一个交换机
* */
@Data
public class Exchange {
//    此处使用 name 来作为交换机的身份标识 (唯一的)private String name;
//    交换机类型,DIRECT,FANOUT,TOPICprivate ExchangeType type = ExchangeType.DIRECT;
//    该交换机是否要持久化存储 ture 表示需要持久化;false 表示不必持久化private boolean durable = false;
//    针对 交换机 队列,绑定,消息.... 内存中也需要存储(执行效率高),硬盘上也需要存储(持久化)
//    有些交换机,队列,绑定等,是需要持久化存储,但是有些则不需要
//    用户使用的时候,就可以通过 开关(boolean 值)来决定是否真的需要持久化//    如果当前交换机没人使用了,就会自动被删除(对于生产者而言)
//    这个属性暂时先列在这里,后续的代码中并没有真的实现这个自动删除功能(RabbitMQ 是有的)private boolean autoDelete = false;
//    argument 表示的是创建交换机时指定的一些额外的参数选项,后续代码中并没有真的实现对应的功能,先列出来(RabbitMQ 也是有的)
//    为了把这个 argument 给存到数据库中,就需要把 Map 转成 json 格式的字符串private Map<String,Object> arguments = new HashMap<>();
//    argument 可以称为 "参数" 或 "选项", 可以有,也可以没有,通过选项来开启不同的功能
}

编写 Queue 实体类

1、编写 Queue 实体类

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.Data;import java.util.HashMap;
import java.util.Map;/*
* 这个类表示一个存储消息的队列
* MSG ——> Message
* */
@Data
public class MSGQueue {
//    表示队列的身份标识private String name;
//    标识队列是否持久化,true 持久化保存,false 表示不持久化private boolean durable = false;
//    这个属性为 true,表示这个队列只能被一个消费者使用(别人用不了)如果为 false 则是大家都能使用
//    这个 独占 功能,也是先把字段列在这里,具体的独占功能暂时先不实现private boolean exclusive = false;
//    为 ture 表示没有人使用之后,就自动删除  false 则是不会自动删除(对于消费者而言)private boolean autoDelete = false;
//    表示拓展参数,当前也是先列在这里,先暂时不实现private Map<String,Object> arguments = new HashMap<>();
}

注意:

  • 此处为了与标准库中的 Queue 类稍作区分,我们直接取名为 MSGQueue 类 

编写 Binding 实体类

1、编写 Binding 实体类

import lombok.Data;/*
* 表示队列和交换机之间的关联关系
* */
@Data
public class Binding {private String exchangeName;private String queueName;
//    这里的 bindingKey 就是在出题private String bindingKey;//    binding 这个东西,依附于 Exchange 和 Queue 的
//    比如,对于持久化来说,如果 Exchange 和 Queue 任何一个都没有持久化
//    此时你针对 Binding 持久化是没有任何意义的
}

编写 Message 实体类

  • Message 主要包含三个部分
  1. 属性部分 BasicProperties
  2. 正文部分 byte[]
  3. 辅助属性 offsetBeg、offsetEnd 、isValid

1、编写 Message 实体类

import lombok.Data;import java.io.Serializable;
import java.util.UUID;/*
* 表示一个要传递的消息
* 注意!!此处的 Message 对象,是需要能够在网络上删除,并且也需要能写入到文件中
* 此时就需要针对 Message 进行序列化和反序列化
* 此处使用 标准库自带的 序列化/反序列化 操作
* */
@Data
public class Message implements Serializable {
//    这两个属性是 Message 最核心的部分private BasicProperties basicProperties = new BasicProperties();private byte[] body;//    下面的属性则是辅助用的属性
//    Message 后续会持久化到文件中(如果持久化的话)
//    一个文件中会存储很多的消息,如何找到某个消息,在文件中的具体位置呢?
//    使用下面两个偏移量来进行表示 [offsetBeg,offsetEnd)
//    这俩属性并不需要被序列化保存到文件中 此时消息一旦被写入文件之后,所在的位置就固定了,并不需要单独存储
//    这俩属性存在的目的,主要就是为了让内存中的 Message 对象,能够快速找到对应的硬盘上的 Message 的位置private transient long offsetBeg = 0; // 消息数据的开头距离文件开头的位置偏移(字节)private transient long offsetEnd = 0; // 消息数据的结尾距离文件开头的位置便宜(字节)//    使用这个属性表示该消息在文件中是否是有效消息(针对文件中的消息,如果删除,使用逻辑删除的方式)
//    0x1 表示有效,0x0 表示无效private byte isValid = 0x1;//    创建一个工厂方法,让工厂方法来帮我们封装一下创建 Message 对象的过程
//    这个方法中创建 Message 对象,会自动生成唯一的 MessageId
//    万一 routingKey 和 basicProperties 里的 routingKey 冲突,以外面的为主public static Message createMessageWithId(String routingKey, BasicProperties basicProperties,byte[] body) {Message message = new Message();if (basicProperties != null) {message.setBasicProperties(basicProperties);}
//        此处生成的 MessageId 以 M- 作为前缀message.setMessageId("M-" + UUID.randomUUID());message.setRoutingKey(routingKey);message.body = body;
//        此处是把 body 和 basicProperties 先设置出来,这俩个是 Message 的核心内容
//        而 offsetBeg,offsetEnd,isValid,则是消息持久化的时候才会用到,在把消息写入文件之前再进行设定
//        此处只是在内存中创建一个 Message 对象return message;}public String getMessageId() {return basicProperties.getMessageId();}private void setMessageId(String messageId) {basicProperties.setMessageId(messageId);}public String getRoutingKey() {return basicProperties.getRoutingKey();}public void setRoutingKey(String routingKey) {basicProperties.setRoutingKey(routingKey);}public int getDeliverMode() {return basicProperties.getDeliverMode();}public void setDeliverMode(int deliverMode) {basicProperties.setDeliverMode(deliverMode);}
}

注意点一:

  •  此处我们将使用标准库自带的方式进行序列化/反序列化,而不使用 JSON 方式
  • JSON 本质上是文本格式,即里面放的也是文本类型的数据
  • 而 Message 中存储的是 二进制数据
  • 所以我们直接让需要被序列化的类实现一个 Serializable 接口即可

注意点二:

  • 以往实现一个接口,都是为了重写里面的某个/某些方法
  • 但是 Serializable 接口无需重写任何方法

注意点三:

  • 如图所示,辅助属性 offsetBeg 和 offsetEnd 仅用于标识某一消息在文件中的具体位置
  • 而不是将这俩属性,像如同 属性部分 和 正文部分 序列化保存到文件中!
  • 所以我们给这两个成员变量加上 trasient 关键字,用于防止被序列化

注意点四:

  • UUID 是编程中,一种用来生成唯一 id 的算法

注意点五:

  • 数据库也好,硬盘文件也好,在删除数据时,很多时候均会使用到 逻辑删除
  • 不是真删除了,而是标记成 "无效"

2、编写属性部分 BasicProperties 

import lombok.Data;import java.io.Serializable;@Data
public class BasicProperties implements Serializable {
//    消息的唯一身份标识 此处为了保证 id 的唯一性,使用 UUID 来作为 messageIdprivate String messageId;
//    是一个消息上带有的内容,和 bindingKey 做匹配
//    如果当前的交换机类型是 DIRECT,此时 routingKey 就表示要转发的队列名
//    如果当前的交换机类型是 FANOUT,此时 routingKey 无意义(不使用)
//    如果当前的交换机类型是 TOPIC,此时 routingKey 就要和 bindingKey 做匹配,符合要求才能转发给对应队列private String routingKey;//    表示消息是否要持久化, 1 表示不持久化, 2 表示持久化 (RabbitMQ 就是这样做的)private  int deliverMode = 1;//    其实针对 RabbitMQ 来说,BasicProperties 里面还有很多别的属性,其他的属性暂时先不考虑了
}

注意:

  • Message 实体类实现了 Serializable 接口用于序列化
  • BasicProperties 类作为 Message 的属性部分,也需实现 Serializable 接口用于序列化

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

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

相关文章

读千脑智能笔记08_人工智能的未来(下)

1. 机器智能存在的风险 1.1. “人工智能”这个名字应用到几乎所有涉及机器学习的领域 1.2. 技术专家对人工智能的态度也从“人工智能可能永远不会实现”快速转变为“人工智能可能在不久的将来毁灭所有人类” 1.3. 每一项新技术都可能会被滥用…

面试经典150题——盛最多水的容器(图解从本质看问题)

​"Hardships often prepare ordinary people for an extraordinary destiny." - C.S. Lewis 1. 题目描述 2. 题目分析与解析 2.1 思路一——暴力求解 遇到问题最怕的就是没有思路&#xff0c;就好像人迷茫的时候最怕的就是一直迷茫&#xff0c;不知道怎么干那就…

Lua 教程

Lua 教程 (今天又又又开新坑啦) Lua 教程 手册简介 Lua 是一种轻量小巧的脚本语言&#xff0c;用标准C语言编写并以源代码形式开放。 手册说明 Lua是什么? Lua 是一个小巧的脚本语言。是巴西里约热内卢天主教大学&#xff08;Pontifical Catholic University of Rio de …

Java stream 流的基本使用

Java stream 的基本使用 package com.zhong.streamdemo.usestreamdemo;import jdk.jfr.DataAmount; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor;import java.util.ArrayList; import java.util.Comparator; import java.util.Li…

源码梳理(3)MybatisPlus启动流程

文章目录 1&#xff0c;MybatisPlus的使用示例2&#xff0c;BaseMapper方法的执行2,1 MybatisMapperProxy代理对象2.2 InvocationHandler接口&#xff08;JDK动态代理&#xff09;2.3 MapperMethodInvoker接口2.4 MybatisMapperMethod 3&#xff0c;SqlSession的执行流程3.1 Sq…

js基础(1)

操作数组 数组.push() 将一个或多个元素添加到数组末尾&#xff0c;返回数组新长度 数组.unshift() 将一个或多个元素添加到数组末尾&#xff0c;返回数组新长度 数组.pop() 删除最后一个元素&#xff0c;返回该元素的值 更灵活的删除方法&#xff0c;删除指定元素 数组.spli…

Github 2024-02-09 开源项目日报 Top10

根据Github Trendings的统计&#xff0c;今日(2024-02-09统计)共有10个项目上榜。根据开发语言中项目的数量&#xff0c;汇总情况如下&#xff1a; 开发语言项目数量Python项目4Go项目2Scala项目1PLpgSQL项目1Ruby项目1HTML项目1Solidity项目1Lua项目1 开源个人理财应用 Mayb…

如何连接ChatGPT?无需科学上网,使用官方GPT教程

随着AI的发展&#xff0c;ChatGPT也越来越强大了。 它可以帮你做你能想到的几乎任何事情&#xff0c;妥妥的生产力工具。 然而&#xff0c;对于许多国内的用户来说&#xff0c;并不能直接使用ChatGPT&#xff0c;不过没关系&#xff0c;我最近发现了一个可以直接免科学上网连…

【图形图像的C++ 实现 01/20】 2D 和 3D 贝塞尔曲线

目录 一、说明二、贝塞尔曲线特征三、模拟四、全部代码如下​五、资源和下载 一、说明 以下文章介绍了用 C 计算和绘制的贝塞尔曲线&#xff08;2D 和 3D&#xff09;。    贝塞尔曲线具有出色的数学能力来计算路径&#xff08;从起点到目的地点的曲线&#xff09;。曲线的形…

Java汽车销售管理

技术架构&#xff1a; springboot mybatis Mysql5.7 vue2 npm node 有需要该项目的小伙伴可以私信我你的Q。 功能描述&#xff1a; 针对汽车销售提供客户信息、车辆信息、订单信息、销售人员管理、财务报表等功能&#xff0c;提供经理和销售两种角色进行管理 效果图&…

医院挂号预约|医院挂号预约小程序|基于微信小程序的医院挂号预约系统设计与实现(源码+数据库+文档)

医院挂号预约小程序目录 目录 基于微信小程序的医院挂号预约系统设计与实现 一、前言 二、系统功能设计 三、系统实现 1、小程序用户端 2、系统服务端 &#xff08;1&#xff09; 用户管理 &#xff08;2&#xff09;医院管理 &#xff08;3&#xff09;医生管理 &…

Cisco firepower2100系列使用FDM管理FTD

Cisco firepower2100系列使用FDM管理FTD 啥是FDM Firepower Device Manager 当思科Firepower系列运行的FTD镜像时&#xff0c;可以通过2种方式进行管理 第1种方式&#xff1a; FMC (Firepower management Center) 可以进行统一管理&#xff0c;一台FMC可以管理多个FTD&…

[office] 怎么在Excel2003菜单栏自定义一个选项卡 #其他#微信#知识分享

怎么在Excel2003菜单栏自定义一个选项卡 怎么在Excel2003菜单栏自定义一个选项卡 ①启动Excel2003&#xff0c;单击菜单栏--工具--自定义。 ②在自定义界面&#xff0c;我们单击命令标签&#xff0c;在类别中选择新菜单&#xff0c;鼠标左键按住新菜单&#xff0c;拖放到菜单栏…

设计模式巡礼:多板适配案例解析与深度重构

theme: cyanosis 月黑风高&#xff0c;好兄弟发给我一个重构需求&#xff0c;咨询我的意见。 一、 场景分析 开发的产品是需要运行到不同的定制Android板子&#xff0c;不同板子有对应的不同SDK提供的API&#xff0c;目前的业务端&#xff0c;业务流程基本是确定的&#xff0…

Python中HTTP隧道的基本原理与实现

HTTP隧道是一种允许客户端和服务器之间通过中间代理进行通信的技术。这种隧道技术允许代理服务器转发客户端和服务器之间的所有HTTP请求和响应&#xff0c;而不需要对请求或响应内容进行任何处理或解析。Python提供了强大的网络编程能力&#xff0c;可以使用标准库中的socket和…

单片机学习笔记---DS1302实时时钟工作原理

目录 DS1302介绍 学会读芯片手册&#xff08;DS1302芯片手册&#xff09; 封装 引脚定义 电源部分 时钟部分 通信部分 总结列表 内部结构图 电源控制部分 时钟控制部分 寄存器部分 访问部分 寄存器部分的详细定义 命令字 时序的定义 单字节读 单字节写 提前预…

【JAVA WEB】CSS

目录 CSS是什么&#xff1f; 基本语法规范 引入方式 内部样式表 行内样式表 外部样式表 常用选择器的种类 基础选择器 标签选择器 类选择器 id选择器 通配符选择器 复合选择器 后代选择器 伪类选择器 常用元素属性&#xff1a; 字体属性&#xff1a; 文本属性…

Sklearn、TensorFlow 与 Keras 机器学习实用指南第三版(一)

原文&#xff1a;Hands-On Machine Learning with Scikit-Learn, Keras, and TensorFlow 译者&#xff1a;飞龙 协议&#xff1a;CC BY-NC-SA 4.0 前言 机器学习海啸 2006 年&#xff0c;Geoffrey Hinton 等人发表了一篇论文&#xff0c;展示了如何训练一个能够以最先进的精度…

Mac 下JDK环境变量配置 及 JDK多版本切换

一、推荐官网下载&#xff1a; 二、环境变量配置 1、查看JDK地址&#xff0c;在终端输入以下命令&#xff1a; /usr/libexec/java_home -V 我的路径&#xff1a; /Library/Java/JavaVirtualMachines/jdk-17.jdk/Contents/Home /Library/Java/JavaVirtualMachines/zulu-11.j…

图像批量重命名(基于Python,本地运行)

图像批量重命名(基于Python&#xff0c;本地运行) &#x1f335;文章目录&#x1f335; &#x1f333;引言&#x1f333;&#x1f333;场景假设&#x1f333;&#x1f333;知识储备&#x1f333;os.path.splitext方法语法示例 os.listdir方法语法示例 &#x1f333;解决方案&am…