SpringBoot整合RabbitMQ direct交换机、fanout交换机、topic交换机

2024-04-27 04:20

本文主要是介绍SpringBoot整合RabbitMQ direct交换机、fanout交换机、topic交换机,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

PS 常见错误
1、有匹配到交换机,但是没有匹配到绑定的队列。(交换机没有绑定队列)- not route
2、没有匹配到交换机。(交换机名称错误,not found - exchange)
3、交换机和队列都没有匹配(和第二种状态一样,没有匹配到交换机,直接返回。)

本篇主要讲的是
直连交换机(amq.direct)、
扇形交换机(amq.fanout)、
主题交换机(amq.topic)。
MQ中交换机和队列是绑定的关系,其中这三个交换机之间的差别就是,绑定路由键值的不同,看以下代码和解释:

1.BindingBuilder.bind(queue()).to(exchange()).with(routingkey);
2.BindingBuilder.bind(queue()).to(exchange());
第一种是直连交换机和主题交换机的用法。
第二种是扇形交换机的用法,不需要绑定路由键。
queue()方法:自定义的队列,也可以使用已存在的队列new Queue("已存在的队列名或自定义队列名",true,true,false)1.durable,是否持久化,默认为false。若为true,则将队列放入到磁盘中。否则只存再内存中,宕机后,所有数据都会消失。2.exclusive(排他性队列),是否仅当前连接可见,默认为false。若为true,则该队列在当前连接断开后,就会删除该队列,其优先级高于durable,也就是说无论是否设置为持久化,该操作都生效。3.autoDelete,自动删除队列,默认为false。若为true,则当消费者宕机后,会自动删除该队列。此时若生产者持续发送消息,这期间的发送内容都会丢失。这些自己测试一下就知道了。
exchange()方法:自定义的交换机,也可以使用已存在的交换机。(直连交换机) new DirectExchange("已存在交换机名或自定义交换机名",true);1.durable
routinekey:交换机与队列之间绑定的路由键。发送消息时,routinekey错误,则无法获取队列,则无法进行消息发送。直连交换机和主题交换机是必须加路由键,扇形交换机则不是必须加的。

项目依赖及配置

POM的依赖文件,基于SpringBoot 2.4.3版本,生产者项目和消费者项目都可以使用这样的依赖。

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>2.4.3</version><relativePath/> <!-- lookup parent from repository --></parent><groupId>com.mq.producter</groupId><artifactId>mqproducter</artifactId><version>0.0.1-SNAPSHOT</version><name>mqproducter</name><description>Demo project for Spring Boot</description><properties><java.version>1.8</java.version></properties><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-amqp</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency></dependencies><build><plugins><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId></plugin></plugins></build>
</project>

application.properties配置如下,生产者和消费者都可用一样的。

spring.application.name=MqProducer
##配置rabbitmq
spring.rabbitmq.host=localhost
spring.rabbitmq.port=5672
spring.rabbitmq.username=guest
spring.rabbitmq.password=guest

1、直连交换机

直连交换机与队列进行绑定时,需要绑定一个路由键X。此时若有消息载体中携带了X路由键,则会找到对应的队列,并将消息存放到对应的队列中。

直连交换机 及 队列配置代码

import org.springframework.amqp.core.Binding;
import org.springframework.amqp.core.BindingBuilder;
import org.springframework.amqp.core.Queue;
import org.springframework.amqp.core.TopicExchange;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;@Configuration
/*** 直连交换机配置*/
public class DirectConfig {@BeanQueue directQueue(){return new Queue("directQueue",true);}@BeanDirectExchange createExchange(){//原本存在的交换机,都是持久性的。return new DirectExchange("amq.direct");}@BeanBinding bindingExchange(){//可以绑定自定义交换机或者绑定已存在的交换机return BindingBuilder.bind(directQueue()).to(createExchange()).with("directRouting");}
}

监听直连交换机 及 队列配置

监听直连交换机绑定的队列

import org.springframework.amqp.rabbit.annotation.RabbitHandler;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;import java.util.Map;@Component
@RabbitListener(queues = {"directQueue"})
public class DirectListener {@RabbitHandlerpublic void process(Map m){System.out.println("DirectListener接受到的消息为:"+m.toString());}
}

2、扇形交换机(广播)

扇形交换机与队列进行绑定,不需要绑定路由键,就算绑定了也没有作用。若扇形交换机中绑定了N个队列,此时有消息载体到了扇形交换机,则会被存放到扇形交换机绑定的所有队列中,消费者消费时,就会消费其所有队列的消息。

扇形交换机 及 队列配置代码

import org.springframework.amqp.core.Binding;
import org.springframework.amqp.core.BindingBuilder;
import org.springframework.amqp.core.Queue;
import org.springframework.amqp.core.TopicExchange;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;@Configuration
/*** 直连交换机配置*/
public class FanoutConfig {@BeanQueue firstFanoutQueue(){return new Queue("firstFanoutQueue",true);}@BeanQueue secondFanoutQueue(){return new Queue("secondFanoutQueue",true);}@BeanFanoutExchange createFanoutExchange(){return new FanoutExchange("amq.fanout");}@BeanBinding bindingFirstQueue(){//可以绑定自定义交换机或者绑定已存在的交换机//需要注意的是,扇形交换机with就不需要了return BindingBuilder.bind(firstFanoutQueue()).to(createFanoutExchange());}@BeanBinding bindingSeccondQueue(){//可以绑定自定义交换机或者绑定已存在的交换机return BindingBuilder.bind(secondFanoutQueue()).to(createFanoutExchange());}
}

监听扇形交换机 及 队列配置

因为有N个队列,此时需要创建N个监听器,或者在一个监听器中,监听N个队列,这样子简单的监听就没办法对不同的队列进行逻辑处理了。
FirstFanoutListener.java

import org.springframework.amqp.rabbit.annotation.RabbitHandler;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;import java.util.Map;@Component
@RabbitListener(queues = {"firstFanoutQueue"})
public class FirstFanoutListener {@RabbitHandlerpublic void process(Map m){System.out.println("FirstFanoutListener接受到的消息为:"+m.toString());}
}

SecondFanoutListener.java

import org.springframework.amqp.rabbit.annotation.RabbitHandler;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;import java.util.Map;@Component
@RabbitListener(queues = {"secondFanoutQueue"})
public class SecondFanoutListener {@RabbitHandlerpublic void process(Map m){System.out.println("SecondFanoutListener接受到的消息为:"+m.toString());}
}

这两个类监听的是扇形交换机绑定的两个队列,若有消息载体进入扇形交换机,这两个监听类都会实时消费数据。

3、主题交换机

主题交换机和队列进行绑定时,可以通过通配符来进行路由键的模糊匹配。路由键可以使用topic.* 或者topic.#表示。其中*号代表了,句点后必须有1个或多个值。#号代表了,句点后必须有0个或多个值。
 主题交换机是很强大的,它可以代替直连交换机和扇形交换机使用。若路由键中设置#号(说明路由键可以为空),则可以代替扇形交换机。若路由键不包含 *号或者#号(说明不存在模糊匹配,需要全匹配。)则可以代替直连交换机使用。

看一下以下的例子:
 若有交换机绑定了队列1(topic.thing)、队列2(topic.
 此时若有消息载体携带topic.thing路由键,则消息就会进入队列1和队列2中。
 若有消息载体携带topic.other,则消息只会进入到队列2中,因为
号匹配了other。这就说明句点后可以输入任意字符。

主题交换机 及 队列配置代码

import org.springframework.amqp.core.Binding;
import org.springframework.amqp.core.BindingBuilder;
import org.springframework.amqp.core.Queue;
import org.springframework.amqp.core.TopicExchange;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;@Configuration
/*** 直连交换机配置*/
public class TopicConfig {@BeanQueue firstTopicQueue(){return new Queue("firstTopicQueue",true);}@BeanQueue secondTopicQueue(){return new Queue("secondTopicQueue",true);}@BeanTopicExchange createTopicExchange(){//选择已存在的交换机return new TopicExchange("amq.topic");}@BeanBinding bindingFirstTopicQueue(){//可以绑定自定义交换机或者绑定已存在的交换机//需要注意的是,扇形交换机with就不需要了return BindingBuilder.bind(firstTopicQueue()).to(createTopicExchange()).with("topic.thing");}@BeanBinding bindingSecondTopicQueue(){//可以绑定自定义交换机或者绑定已存在的交换机return BindingBuilder.bind(secondTopicQueue()).to(createTopicExchange()).with("topic.#");}
}

主题交换机的代码有所不同,在路由键中使用了通配符#

监听主题交换机 及 队列配置

因为有N个队列,此时需要创建N个监听器,或者在一个监听器中,监听N个队列,这样子简单的监听就没办法对不同的队列进行逻辑处理了。
FirstTopicListener.java

import org.springframework.amqp.rabbit.annotation.RabbitHandler;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;import java.util.Map;@Component
@RabbitListener(queues = {"firstTopicQueue"})
public class FirstTopicListener {@RabbitHandlerpublic void process(Map m){System.out.println("FirstTopicListener接受到的消息为:"+m.toString());}
}

SecondTopicListener.java

import org.springframework.amqp.rabbit.annotation.RabbitHandler;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;import java.util.Map;@Component
@RabbitListener(queues = {"secondTopicQueue"})
public class SecondTopicListener {@RabbitHandlerpublic void process(Map m){System.out.println("SecondTopicListener接受到的消息为:"+m.toString());}
}

这两个类监听的是主题交换机绑定的两个队列,若有消息载体进入扇形交换机,携带绑定的路由键,就会进入到指定队列,不同的监听器就会实时消费。

发送信息

发送信息接口代码

交换机对应的简单的配置代码都已经完成了,接下来就写一下发送消息的接口。

package com.mq.producter.mqproducter.action;import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;@RestController
@RequestMapping("/mq")
public class ProducerAction {@AutowiredRabbitTemplate rabbitTemplate;@RequestMapping("/sending")public void sending(){try{String message = "This is a first message!";Map<String,Object> m = commonMap(message);rabbitTemplate.convertAndSend("amq.direct","directRouting",m);}catch(Exception e){e.printStackTrace();}}@RequestMapping("/sendtofanout")public void sendToFanout(){try{String message = "This is a second message to Fanout!";Map<String,Object> m = commonMap(message);rabbitTemplate.convertAndSend("amq.fanout",null,m);}catch(Exception e){e.printStackTrace();}}@RequestMapping("/sendtotopic1")public void sendToTopicMan(){try{String message = "This is a first message to topic : Thing!";Map<String,Object> m = commonMap(message);rabbitTemplate.convertAndSend("amq.topic","topic.thing",m);}catch(Exception e){e.printStackTrace();}}@RequestMapping("/sendtotopic2")public void sendToTopicWoman(){try{String message = "This is a first message to topic : other!";Map<String,Object> m = commonMap(message);rabbitTemplate.convertAndSend("amq.topic","topic.asdasd",m);}catch(Exception e){e.printStackTrace();}}public Map<String,Object> commonMap(String message){String uuid = String.valueOf(UUID.randomUUID());String date = new SimpleDateFormat("yyyy-MM-dd HH:mm").format(new Date());Map<String,Object> m = new HashMap<>();m.put("messageId", uuid);m.put("createTime",date);m.put("message",message);return m;}
}

生产者和消费者都开发完成了,将两个项目都启动后,调用接口,往MQ中发送数据。

1.调用发送到直连交换机的接口

消费者端实时消费数据:

DirectListener接受到的消息为:{createTime=2024-02-25 13:55, messageId=cf0b8188-6fb4-43cb-86f2-ad0286889ceb, message=This is a first message!}

2.调用发送到扇形交换机的接口

FirstFanoutListener接受到的消息为:{createTime=2024-02-25 13:56, messageId=afe4206e-46e8-4e9c-9a99-d38d130b54e1, message=This is a second message to Fanout!}
SecondFanoutListener接受到的消息为:{createTime=2024-02-25 13:56, messageId=afe4206e-46e8-4e9c-9a99-d38d130b54e1, message=This is a second message to Fanout!}

两个监听器都实时消费了数据。

3.调用发送到主题交换机的两个接口

调用发送消息载体中携带topic.thing的接口:

SecondTopicListener{createTime=2024-02-25 14:02, messageId=7f412bbd-4032-476b-a578-cd7a0aa04e1d, message=This is a first message to topic : Man!}
FirstTopicListener接受到的消息为:{createTime=2024-02-25 14:02, messageId=7f412bbd-4032-476b-a578-cd7a0aa04e1d, message=This is a first message to topic : Man!}

明明topic.thing路由绑定的是队列firstTopicQueue,但是secondTopicQueue却也收到了,为什么呢?这是因为队列secondTopicQueue的路由键配置的是topic.*,那么句点后无论是什么都会和匹配成功。
若调用发送消息载体中携带topic.other的接口:

SecondTopicListener{createTime=2024-02-25 14:05, messageId=ad408aef-d678-4d06-be5c-7982403b3b48, message=This is a first message to topic : Woman!}

那么此刻只有队列2是匹配的。
以上就是简单的生产者与消费者的代码。

这篇关于SpringBoot整合RabbitMQ direct交换机、fanout交换机、topic交换机的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/939508

相关文章

Java堆转储文件之1.6G大文件处理完整指南

《Java堆转储文件之1.6G大文件处理完整指南》堆转储文件是优化、分析内存消耗的重要工具,:本文主要介绍Java堆转储文件之1.6G大文件处理的相关资料,文中通过代码介绍的非常详细,需要的朋友可... 目录前言文件为什么这么大?如何处理这个文件?分析文件内容(推荐)删除文件(如果不需要)查看错误来源如何避

SpringBoot整合Dubbo+ZK注册失败的坑及解决

《SpringBoot整合Dubbo+ZK注册失败的坑及解决》使用Dubbo框架时,需在公共pom添加依赖,启动类加@EnableDubbo,实现类用@DubboService替代@Service,配... 目录1.先看下公共的pom(maven创建的pom工程)2.启动类上加@EnableDubbo3.实

SpringBoot整合(ES)ElasticSearch7.8实践

《SpringBoot整合(ES)ElasticSearch7.8实践》本文详细介绍了SpringBoot整合ElasticSearch7.8的教程,涵盖依赖添加、客户端初始化、索引创建与获取、批量插... 目录SpringBoot整合ElasticSearch7.8添加依赖初始化创建SpringBoot项

JAVA覆盖和重写的区别及说明

《JAVA覆盖和重写的区别及说明》非静态方法的覆盖即重写,具有多态性;静态方法无法被覆盖,但可被重写(仅通过类名调用),二者区别在于绑定时机与引用类型关联性... 目录Java覆盖和重写的区别经常听到两种话认真读完上面两份代码JAVA覆盖和重写的区别经常听到两种话1.覆盖=重写。2.静态方法可andro

SpringBoot中六种批量更新Mysql的方式效率对比分析

《SpringBoot中六种批量更新Mysql的方式效率对比分析》文章比较了MySQL大数据量批量更新的多种方法,指出REPLACEINTO和ONDUPLICATEKEY效率最高但存在数据风险,MyB... 目录效率比较测试结构数据库初始化测试数据批量修改方案第一种 for第二种 case when第三种

Java docx4j高效处理Word文档的实战指南

《Javadocx4j高效处理Word文档的实战指南》对于需要在Java应用程序中生成、修改或处理Word文档的开发者来说,docx4j是一个强大而专业的选择,下面我们就来看看docx4j的具体使用... 目录引言一、环境准备与基础配置1.1 Maven依赖配置1.2 初始化测试类二、增强版文档操作示例2.

一文详解如何使用Java获取PDF页面信息

《一文详解如何使用Java获取PDF页面信息》了解PDF页面属性是我们在处理文档、内容提取、打印设置或页面重组等任务时不可或缺的一环,下面我们就来看看如何使用Java语言获取这些信息吧... 目录引言一、安装和引入PDF处理库引入依赖二、获取 PDF 页数三、获取页面尺寸(宽高)四、获取页面旋转角度五、判断

Spring Boot中的路径变量示例详解

《SpringBoot中的路径变量示例详解》SpringBoot中PathVariable通过@PathVariable注解实现URL参数与方法参数绑定,支持多参数接收、类型转换、可选参数、默认值及... 目录一. 基本用法与参数映射1.路径定义2.参数绑定&nhttp://www.chinasem.cnbs

JAVA中安装多个JDK的方法

《JAVA中安装多个JDK的方法》文章介绍了在Windows系统上安装多个JDK版本的方法,包括下载、安装路径修改、环境变量配置(JAVA_HOME和Path),并说明如何通过调整JAVA_HOME在... 首先去oracle官网下载好两个版本不同的jdk(需要登录Oracle账号,没有可以免费注册)下载完

Spring StateMachine实现状态机使用示例详解

《SpringStateMachine实现状态机使用示例详解》本文介绍SpringStateMachine实现状态机的步骤,包括依赖导入、枚举定义、状态转移规则配置、上下文管理及服务调用示例,重点解... 目录什么是状态机使用示例什么是状态机状态机是计算机科学中的​​核心建模工具​​,用于描述对象在其生命