RabbitMQ实现消息发送接收——实战篇(路由模式)

本篇博文将带领大家一起学习rabbitMQ如何进行消息发送接收,我也是在写项目的时候边学边写,有不足的地方希望在评论区留下你的建议,我们一起讨论学习呀~

需求背景

先说一下我的项目需求背景,社区之间可以进行物资借用,当有社区提交物资借用申请时,需要通过RabbitMQ将这条消息发送到被借用物资的社区,同时在界面进行提示。

先把依赖引入一下

 <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-amqp</artifactId></dependency>

application.yml做好配置:

spring:rabbitmq:host: localhostport: 5672username: guestpassword: guest

工具类实现

先选择以何种方式进行消息发送,这里根据需求我选择使用RabbitMQ的路由模式进行消息发送,先来配置一下相应工具类:

先配置RabbitMQ的配置类

/*** @Title: RabbitMQConfig* @Author yinan* @Package com.yinan.config.RabbitConfig* @Date 2024/12/13 13:58* @description: RabbitMQ配置类*/
@Configuration
public class RabbitMQConfig {@Beanpublic RabbitAdmin rabbitAdmin(ConnectionFactory connectionFactory) {return new RabbitAdmin(connectionFactory);}//    声明一个交换机@Beanpublic DirectExchange borrowMaterialExchange(){return new DirectExchange("borrow_material_exchange");}//    动态绑定队列时使用的方法(具体绑定逻辑在下面的监听器中实现)
//    @Bean
//    public Queue communityQueue(){
//        return new Queue("communityQueue");
//    }@Beanpublic Jackson2JsonMessageConverter messageConverter() {return new Jackson2JsonMessageConverter();}@Beanpublic AmqpTemplate amqpTemplate(ConnectionFactory connectionFactory) {RabbitTemplate rabbitTemplate = new RabbitTemplate(connectionFactory);rabbitTemplate.setMessageConverter(messageConverter());return rabbitTemplate;}
}

为了确保消息发送和接收时都以 JSON 格式处理,可以在 Spring 配置中添加 Jackson2JsonMessageConverter。这样,发送端会将 MaterialBorrowing 对象序列化为 JSON,接收端会自动将 JSON 反序列化回 MaterialBorrowing 对象。 

绑定交换机和对应队列

/*** @Title: RabbitMQBindRoutingConfig* @Author yinan* @Package com.yinan.config.RabbitConfig* @Date 2024/12/13 14:21* @description:  动态绑定路由配置*/
@Component
@Slf4j
public class RabbitMQBindRoutingConfig {@Autowiredprivate DirectExchange borrowMaterialExchange;@Autowiredprivate RabbitAdmin rabbitAdmin;/*** 以社区ID为路由键,为指定社区动态创建队列并绑定到交换机* @param communityId 社区ID*/public void bindRouting(String communityId){
//        创建队列Queue queue = new Queue("queue_" + communityId);
//        动态绑定交换机和指定队列Binding binding = BindingBuilder.bind(queue).to(borrowMaterialExchange).with(communityId);rabbitAdmin.declareExchange(borrowMaterialExchange);rabbitAdmin.declareBinding(binding);log.info("队列绑定成功,社区ID----》" + communityId + ",队列名称----》" + queue.getName() + ",交换机名称----》" + borrowMaterialExchange.getName());}}

动态声明队列

@Configuration
@Slf4j
public class QueueDeclareConfig {@Autowiredprivate RabbitAdmin rabbitAdmin;public void dynamicDeclareQueue(String communityId){String queueName = String.format("queue_%s",communityId);Queue queue = new Queue(queueName,true);rabbitAdmin.declareQueue(queue);log.info("队列声明成功");}
}

在创建声明队列的时候,我们希望的是根据我们的规则在调用接口的时候去创建指定名称的队列,所以可以使用动态声明对列而不是直接在平台上进行配置。

消息发送

@Component
@Slf4j
public class MessageSendConfig {@Autowiredprivate AmqpTemplate amqpTemplate;public void sendMessage(Object message,String communityId){System.out.println("发送消息:" + message);amqpTemplate.convertAndSend("borrow_material_exchange",communityId, message);log.info("发送消息成功------->"+message);}}

消息接收(动态声明与监听结合),这里你可以先思考一下为什么要用这种方式实现消息接收,而不是使用@RabbitListener去动态获取某个队列接收消息。

/*** @Title: MessageRecieveConfig* @Author yinan* @Package com.yinan.config.RabbitConfig* @Date 2024/12/13 12:53* @description: 动态监听接收消息*/
@Service
@Slf4j
public class MessageRecieveConfig<T> {private final ConnectionFactory connectionFactory;public MessageRecieveConfig(ConnectionFactory connection) {this.connectionFactory = connection;}public void recieveMessage(String communityId,Class<T> objectType){String queueName = String.format("queue_%s",communityId);
//        创建监听容器SimpleMessageListenerContainer container = new SimpleMessageListenerContainer(connectionFactory);container.setQueueNames(queueName);
//        处理消息消费逻辑container.setMessageListener(message -> {try {// 将字节数组转换为字符串String messageBody = new String(message.getBody(), StandardCharsets.UTF_8);System.out.println("接收到的消息:" + messageBody);// 如果需要将消息解析为对象(例如 MaterialBorrowing)ObjectMapper objectMapper = new ObjectMapper();T result = objectMapper.readValue(messageBody, objectType);System.out.println("反序列化后的消息:" + result);} catch (Exception e) {log.error("处理消息时发生错误:", e);}});// 确保自动确认container.setAcknowledgeMode(AcknowledgeMode.AUTO);container.start();log.info("动态监听已启动,监听队列------->"+queueName);}}

在你的项目中分别调用就行了,需要注意的是你必须确保在消息发送的时候你的队列已经创建完成且和对应交换机进行了绑定,不然可能会导致消息发送失败。

ok,我们启动项目

你会发现你的项目根本启动不起来,原因是因为对于 Spring AMQP 的监听器来说,必须确保监听的队列已经存在于 RabbitMQ 中,否则会抛出类似 DeclarationException 的错误。

所以我们考虑可以通过动态声明队列,在程序运行时确保 RabbitMQ 上创建好所需的队列。

动态声明队列的含义

动态声明队列是指程序在运行时,通过代码检查或创建 RabbitMQ 中尚不存在的队列,而不是手动预先配置好所有队列。这种方式可以自动帮你在 RabbitMQ 中创建所需的队列,而无需手动操作。

这里说一下为什么需要动态绑定队列而不直接使用@RabbitListener?

为什么使用 SimpleMessageListenerContainer 动态绑定队列

  • SimpleMessageListenerContainer 不需要在项目启动时绑定队列。你可以在用户调用接口时动态创建队列,并动态监听它。
  • 特点
    • 队列在用户调用接口时才会被动态创建(通过 RabbitAdmin 或其他机制)。
    • 动态创建队列和监听时,项目启动时不会尝试绑定不存在的队列,因此不会报错。
  • 适用场景:非常适合动态队列需求,比如队列名依赖用户输入或业务逻辑,且不想在项目启动时绑定固定的队列。

使用 @RabbitListener 的情况

@RabbitListener 会在项目启动时绑定到指定的队列。

  • 要求:如果绑定的队列在 RabbitMQ 中不存在,项目启动时就会抛出异常,类似 DeclarationException,这也就是上面为什么会报错的原因。
  • 解决办法
    • 提前创建队列:在 RabbitMQ 中手动创建队列,或通过 RabbitAdmin 在项目启动时自动创建队列。
    • 动态队列名:如果队列名是动态的,可以结合 SpEL 表达式,但队列仍然需要在项目启动时确保存在。
SpEL 表达式

如果你的需求中已经确定队列已经创建好的,但是需要动态去获取队列,可以使用如下形式:

@RabbitListener(queues = "#{T(java.lang.String).format('queue_%s', 'borrowedCommunityId')}")

这个表达式 是 Spring AMQP 中用于动态指定队列名称的 SpEL 表达式(Spring Expression Language),它的作用就是会动态生成一个队列名称,基于你传入的参数构造队列名。

详解
1. 关键部分解析
  • T(java.lang.String)

    • T 是 SpEL 用于引用 Java 类 的方式。
    • java.lang.String 是目标 Java 类,表明你可以调用 String 类的静态方法。
  • .format()

    • String.format() 是 Java 中的静态方法,用于格式化字符串。
    • 格式化字符串的格式是 'queue_%s'%s 是占位符,用于拼接动态内容。
  • 'queue_%s'

    • 这是格式化字符串的模板。%s 表示字符串占位符。
  • 动态参数(例如 borrowedCommunityId

    • 它会替换 %s,生成队列名。例如,当 borrowedCommunityId 的值是 123 时,结果是:queue_123
2. 具体实例

假设 borrowedCommunityId = "123"

String result = String.format("queue_%s", "123");
System.out.println(result); // 输出:queue_123

在 SpEL 中,这等同于:

queues = "#{T(java.lang.String).format('queue_%s', '123')}"

这会动态生成队列名称为 queue_123


为什么用 SpEL?

Spring AMQP 的 @RabbitListener 注解中,queues 参数支持 SpEL 表达式。这使得我们可以动态决定要监听的队列,而不是写死某个固定的队列名称。


实际应用场景

就比如在我的代码中,可能有多个社区队列,例如:

  • queue_123(社区 ID 为 123 的队列)
  • queue_456(社区 ID 为 456 的队列)

使用 queues = "#{T(java.lang.String).format('queue_%s', borrowedCommunityId)}",可以动态生成不同社区的队列名称,从而实现按社区路由的功能。

启动项目之后,调用接口就可以发送消息了

但是你会发现消息消费的逻辑并没有在控制台中打印出来,这个时候你就要考虑是不是以下几个问题了:

交换机和队列是否已经绑定成功(可以在平台上进行查看)

是否绑定到了对应的交换机:amqpTemplate.convertAndSend("borrow_material_exchange",communityId, message);红色部分指定交换机名称,如果不指定,那么就会使用默认的交换机,所以肯定也是接收不到值的。

当然,还有其他可能,如果你的项目中遇到了,可以在评论区留言,我们一起学习~

最后,重新修改代码调用接口,就可以接收到消息了

对于在界面进行消息提示的功能,这里先不写出来了,我会在后面的博客中进行更新~

【都看到这了,点赞加关注,收藏不迷路呀~】😚😚

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

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

相关文章

The Past, Present and Future of Apache Flink

摘要&#xff1a;本文整理自阿里云开源大数据负责人王峰&#xff08;莫问&#xff09;在 Flink Forward Asia 2024上海站主论坛开场的分享&#xff0c;今年正值Flink开源项目诞生的第10周年&#xff0c;借此时机&#xff0c;王峰回顾了Flink在过去10年的发展历程以及 Flink社区…

城市大脑新型智慧城市数据中台建设方案

建设背景与现状 随着城市化进程的加速&#xff0c;城市数据呈现出爆炸式增长&#xff0c;但数据的整合、共享和利用却面临诸多挑战。信息孤岛、数据冗余、管理分散等问题日益突出&#xff0c;制约了智慧城市的发展。为了解决这些问题&#xff0c;构建城市大脑新型智慧城市数据…

力扣-图论-12【算法学习day.62】

前言 ###我做这类文章一个重要的目的还是给正在学习的大家提供方向和记录学习过程&#xff08;例如想要掌握基础用法&#xff0c;该刷哪些题&#xff1f;&#xff09;我的解析也不会做的非常详细&#xff0c;只会提供思路和一些关键点&#xff0c;力扣上的大佬们的题解质量是非…

每日一站技術架構解析之-cc手機桌布網

# 網站技術架構解析&#xff1a; ## 一、整體架構概述https://tw.ccwallpaper.com是一個提供手機壁紙、桌布免費下載的網站&#xff0c;其技術架構設計旨在實現高效的圖片資源管理與用戶訪問體驗優化。 ### &#xff08;一&#xff09;前端展示 1. **HTML/CSS/JavaScript基礎構…

代码随想录算法训练营第三十二天|动态规划理论基础|LC509.肥波那些数|LC70.爬楼梯|LC746.使用最小花费爬楼梯

动态规划理论基础 解释&#xff1a;动态规划&#xff0c;英文&#xff1a;Dynamic Programming&#xff0c;简称DP&#xff1b;如果某一问题有很多重叠子问题&#xff0c;使用动态规划是最有效的。 动态规划五部曲&#xff1a; 1、确定dp数组&#xff08;dp table&#xff09;…

RabbitMQ Work Queues (工作队列模式) 使用案例

Hi~&#xff01;这里是奋斗的明志&#xff0c;很荣幸您能阅读我的文章&#xff0c;诚请评论指点&#xff0c;欢迎欢迎 ~~ &#x1f331;&#x1f331;个人主页&#xff1a;奋斗的明志 &#x1f331;&#x1f331;所属专栏&#xff1a;RabbitMQ &#x1f4da;本系列文章为个人学…

【安卓开发】【Android Studio】启动时报错“Unable to access Android SDK add-on list”

一、问题描述 在启动Android Studio时&#xff0c;软件报错&#xff1a;Unable to access Android SDK add-on list&#xff0c;报错截图如下&#xff1a; 二、原因及解决方法 初步推测是由于网络节点延迟&#xff0c;无法接入谷歌导致的。点击Cancel取消即可。

掌握线性回归:从简单模型到多项式模型的综合指南

目录 一、说明 二、简单线性回归 三、线性回归的评估指标 3.1 线性回归中的假设 四、从头开始的简单线性回归代码 五、多元线性回归 六、多元线性回归代码 七、多项式线性回归 八、多项式线性回归代码 九、应用单变量多项式回归 十、改变多项式的次数 十一、多列多项式回归 一、…

sqlmap详解

一.sqlmap -u URL --forms sqlmap -u http://192.168.11.136:1337//978345210/index.php --forms 针对特定的 URL 进行 SQL 注入测试&#xff0c;特别是针对表单&#xff08;form&#xff09;的 POST 注入 forms&#xff1a;这个参数告诉 sqlmap 解析并测试目标 URL 中的表单…

OBS + SRS:打造专业级直播环境的入门指南

OBS SRS&#xff1a;打造专业级直播环境的入门指南 1. OBS简介2. OBS核心功能详解2.1 场景&#xff08;Scenes&#xff09;管理2.2 源&#xff08;Sources&#xff09;控制2.3 混音器功能2.4 滤镜与特效2.5 直播控制面板 3. OBS推流到SRS服务器配置指南3.1 环境准备3.2 OBS推流…

Vue组件相关记录

Vue组件开发 非单文件组件 创建组件api Vue.extend({}) const student Vue.extend({template: <div>{{studentName}} - {{age}}</div>,data() {return {studentName: jjking,age: 12}}})new Vue({el: #app,//局部注册components: {student: student}})不能使用e…

【潜意识Java】深入理解 Java 面向对象编程(OOP)

目录 什么是面向对象编程&#xff08;OOP&#xff09;&#xff1f; 1. 封装&#xff08;Encapsulation&#xff09; Java 中的封装 2. 继承&#xff08;Inheritance&#xff09; Java 中的继承 3. 多态&#xff08;Polymorphism&#xff09; Java 中的多态 4. 抽象&…

【Linux SH脚本】LinuxCheck 应急检查信息脚本

LinuxCheck 1.下载地址 【Linux SH脚本】LinuxCheck 应急检查信息脚本 2.简介 LinuxCheck 是一个开源的自动化检查脚本&#xff0c;旨在快速检测 Linux 系统的安全配置和潜在问题。它支持多种发行版&#xff0c;能够扫描并生成详细的报告&#xff0c;涵盖用户管理、权限配置…

docker 部署 redis

docker 部署 redis 1. 下载 redis 镜像 # docker images | grep redis bitnami/redis 7.2.4-debian-11-r5 45de196aef7e 10 months ago 95.2MB2. docker-compose 部署 version: "3" services:redis:image: bitnami/redis:7.2.4-debian-11-…

相机测距原理

基础概念的回顾 焦距的定义 焦距是指透镜或镜头的光学中心&#xff08;通常是透镜的几何中心&#xff09;到其焦点的距离。 焦点是光线的交点&#xff0c;它指的是透镜或镜头聚焦所有入射光线后汇聚的位置。焦点的位置与透镜的曲率和光线的入射角度相关。就是说所有光线经过…

Python粉色圣诞树

系列文章 序号直达链接表白系列1Python制作一个无法拒绝的表白界面2Python满屏飘字表白代码3Python无限弹窗满屏表白代码4Python李峋同款可写字版跳动的爱心5Python流星雨代码6Python漂浮爱心代码7Python爱心光波代码8Python普通的玫瑰花代码9Python炫酷的玫瑰花代码10Python多…

数据分析学习Day1-使用matplotlib生成2小时每分钟的气温可视化分析图

注意&#xff1a;需要提前下载matplotlib包 pip install matplotlib -i https://pypi.tuna.tsinghua.edu.cn/simple import matplotlib.pyplot as plt import random from matplotlib import font_manager # 数据准备 x list(range(121)) # 使用 list() 转换为列表 y [rando…

uniapp uni-table最简单固定表头

需求&#xff1a;固定表头数据&#xff0c;在网上找了半天&#xff0c;啥都有&#xff0c;就是一直实现不了&#xff0c;最后更改代码实现 1.效果 2.主要代码讲解完整代码 表格的父级一定要设置高度&#xff0c;不然会错位&#xff0c;我看网上说设置position&#xff1a;fixed…

HTML、CSS表格的斜表头样式设置title 画对角线

我里面有用到layui框架的影响&#xff0c;实际根据你自己的框架来小调下就可以 效果如下 上代码 <!DOCTYPE html> <html lang"zh"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-wi…

【人工智能】OpenAI O1模型:超越GPT-4的长上下文RAG性能详解与优化指南

在人工智能&#xff08;AI&#xff09;领域&#xff0c;长上下文生成与检索&#xff08;RAG&#xff09; 已成为提升自然语言处理&#xff08;NLP&#xff09;模型性能的关键技术之一。随着数据规模与应用场景的不断扩展&#xff0c;如何高效地处理海量上下文信息&#xff0c;成…