RabbitMQ 篇-深入了解 RabbitMQ 安装以及 SpringAMQP 的基础使用(声明队列和交换机、发送接收消息、配置 JSON 消息转化器)

🔥博客主页: 【小扳_-CSDN博客】
❤感谢大家点赞👍收藏⭐评论✍

文章目录

        1.0 RabbitMQ 初识

        1.1 RabbitMQ 安装

        2.0 数据隔离

        2.1 用户管理

        2.2 virtual host 虚拟主机

        3.0 SpringAMQP

        3.1 RabbitMQ 配置

        3.2 发送消息

        3.3 接收消息

        3.4 WorkQueues 模式

        4.0 交换机类型

        4.1 Fanout 交换机

        4.2 Direct 交换机

        4.3 Topic 交换机

        5.0 声明队列和交换机

        5.1 基本的 API

        5.2 fanout 示例

        5.3 direct 示例

        5.4 基于注解声明

        5.4.1 Fanout 模式的交换机

        5.4.2 Direct 模式的交换机

        6.0 消息转换器

        6.1 配置 JSON 转化器

        6.2 实现业务幂等性


        1.0 RabbitMQ 初识

        RabbitMQ 是基于 Erlang 语言开发的开源消息通信中间件,官网地址:https://www.rabbitmq.com/

        RabbitMQ 中间件的使用目的,基于消息通知实现异步调用,一般包含三个角色:

        1)消息发送者:投递消息的人,就是原来的调用方。

        2)消息 Broker:管理、暂存、转发消息,可以理解为容器。

        3)消息接收者:接收和处理消息的人,服务提供方。

        在异步调用中,发送者不再直接同步调用接收者的业务接口,而是发送一条消息投递给消息 Broker,然后接收者根据自己的需求从消息 Broker 那里订阅消息。每当发送方发送消息后,接收者都能接收消息并处理,这样发送消息的人与接收消息的人完全解耦了。

RabbitMQ 对应的框架:

其中包含的几个概念:

        1)Publish:生产者,也就是发送消息的一方。

        2)consumer:消费者,也就是消费消息的一方。

        3)queue:队列,存储消息。生产者投递的消息会暂存在消息队列中,等待消费者处理。

        4)exchange:交换机,负责消息路由。生产者发送的消息由交换机决定投递到哪个队列。

        5)virtual host:虚拟主机,起到数据隔离的作用。每个虚拟主机相互独立,有各自的 exchange、queue

        1.1 RabbitMQ 安装

        1)基于 Docker 来安装 RabbitMQ,使用下面命令即可:

docker run \-e RABBITMQ_DEFAULT_USER=itheima \-e RABBITMQ_DEFAULT_PASS=123321 \-v mq-plugins:/plugins \--name mq \--hostname mq \-p 15672:15672 \-p 5672:5672 \-d \rabbitmq:3.8-management

        2)如果部署在云服务器中,则还需要添加管理员,命令如下:

1. 进入 RabbitMQ Docker 容器:

        首先找到正在运行的 RabbitMQ 容器的 ID 或名称:

docker ps

2. 然后使用 docker exec 命令进入容器:

docker exec -it mq /bin/bash

3. 添加用户:

rabbitmqctl add_user admin admin_password

4. 设置用户权限:

        将用户设置为超级管理员:

rabbitmqctl set_user_tags admin administrator

5. 配置用户虚拟主机权限:

        如果 RabbitMQ 使用虚拟主机 vhost,需要为用户设置相应的权限,假设虚拟主机为 “/”

rabbitmqctl set_permissions -p / admin ".*" ".*" ".*"

6. 退出容器:

exit

        可以看到安装命令中有两个映射的端口:

        15672:RabbitMQ 提供的管理控制台的端口。

        5672:RabbitMQ 的消息发送处理接口。

        安装完成后,访问自己的 "IP:15672" 即可看到管理控制台。

        如果是首次访问需要登录,默认的用户名和密码就是在设置超级管理员的时候所对应的用户名和密码。

        2.0 数据隔离

        2.1 用户管理

        点击 admin 选项卡,首先看到 RabbitMQ 控制台的用户管理界面:

        Name:用户名。

        Tages:administrator,说明该用户是超级管理员,拥有所有权限。

        Can access virtual host:/,可以访问的 virtual host,这里的 / 是默认的 virtual host 。

添加用户:

        xbs 用户已经拥有了超级管理员的权限,但是没有属于自己的虚拟主机。 

        因此,可以通过添加用户,且创建另一个 virtual host,实现数据隔离。

        2.2 virtual host 虚拟主机

        先退出原先的用户,切换到刚刚创建的 xbs 用户登录,然后点击 Virtual Host 菜单,进入 virtual hsot 管理页:

创建虚拟主机:

        不同的虚拟主机之间,数据是隔离的,相互不会受到影响。简单理解成 MySQL 中的数据库,数据库与数据库之间的数据不会受到影响。

        3.0 SpringAMQP

        由于 RabbitMQ 采用了 AMQP 协议,因此具备跨语言的特性。任何语言只要遵循 AMQP 协议收发消息,都可以与 RabbitMQ 交互。并且 RabbitMQ 官方也提供了各种不同语言的客户端。但是,RabbitMQ 官方提供的 Java 客户端编码相对复杂,一般生产环境下更多结合 Spring 来使用,而 Spring 的官方刚好基于 RabbitMQ 提供了这样一套消息收发的模板工具:SpringAMQP,并且还基于 SpringBoot 对其实现了自动装配,使用起来非常方便。

        SpringAMQP 官方地址:Spring AMQP

        SpringAMQP 提供了三个功能:

        1)自动声明队列、交换机及其绑定关系。

        2)基于注解的监听器模式,异步接收消息。

        3)封装了 RabbitTemplate 工具,用于发送消息。

        3.1 RabbitMQ 配置

        1)依赖引入:

        <!--AMQP依赖,包含RabbitMQ--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-amqp</artifactId></dependency>

        2)application.yml 添加配置:

spring:rabbitmq:host: 113.45.166.112 # 你的虚拟机IPport: 5672 # 端口virtual-host: /tt # 虚拟主机username: xbs # 用户名password: ******** # 密码

        3.2 发送消息

        在发送消息之前,需要先根据图形化界面创建一个 queue1 队列:

成功创建 queue1 队列:

        接着在测试类 Test 中编写,并利用 RabbitTemplate 实现消息发送:

代码如下:

    @Testvoid contextLoads() {//发送消息sendMessage("hello world");}// 首先引入依赖@Autowiredprivate RabbitTemplate rabbitTemplate;private void sendMessage(String massage){//需要明确发送的队列名称,以及发送的消息rabbitTemplate.convertAndSend("queue1",massage );}

测试结果:

        可以看到 queue1 队列中接收到消息了:

        3.3 接收消息

        刚刚发送的消息,已经到 queue1 队列中,但是没有消费者进行消费,所以现在创建消费者进行处理消息。

        在 SpringAMQP 中,是使用了监听器来进行对绑定的队列进行监听,将来一旦监听的队列中有了消息,就会推送给当前服务,调用当前方法,处理消息。

代码如下:

@Slf4j
@Component
public class RabbitMQ {//接收消息//监听queue1队列@RabbitListener(queues = "queue1")private void receiveMessage(String massage) {//用什么类型发送,就接收什么类型log.info("接收到的消息为: " + massage);}
}

        注意:需要加上 @Component 注解,成为 IOC 容器中的 Bean 对象。 

执行结果:

        3.4 WorkQueues 模式

        Work queues,任务模型,简单来说就是让多个消费者绑定到一个队列,共同消费队列中的消息,如果直接将消息发送到队列中,则队列每一个消息只能被处理一次,每一个消息都不能被多个消费者同时消费。

        而在 WorkQueues 模式中,就是将消息直接发送到队列中,且多个消费者绑定同一个队列。

WorkQueues 应用场景:

        当消息处理比较耗时时的时候,可能产生消息的速度会远远大于消息的消费速度。长此以往,消息就会堆积越来越多,无法及时处理。

        此时就可以使用 work 模型,多个消费者共同处理消息处理,消息处理的速度就能大大提高了。

        默认情况下,消息是平均分配每个消费者,并没有考虑到消费者的处理能力,没有充分利用每一个消费者的能力,这样显然是有问题的。在 Spring 中有一个简单的配置,可以解决这个问题,通过修改 application.yml 文件,添加配置:

spring:rabbitmq:host: 113.45.166.112 # 你的虚拟机IPport: 5672 # 端口virtual-host: /tt # 虚拟主机username: xbs # 用户名password: ****** # 密码listener:simple:prefetch: 1 # 每次只能获取一条消息,处理完成才能获取下一个消息

        能者多劳,只要完成处理了消息,就能从队列获取一条消息。

测试:

        1)定义了两个消费者进行对队列中的消息进行消费:

@Slf4j
@Component
public class RabbitMQ {//接收消息//监听queue1队列@RabbitListener(queues = "queue1")private void receiveMessage1(String massage) throws InterruptedException {String coloredMessage = String.format("\u001B[32m%s\u001B[0m", massage); // 绿色字体//用什么类型发送,就接收什么类型log.info("receiveMessage1接收到的消息为: " + coloredMessage);//手动阻塞,模拟消息处理中Thread.sleep(200);}//接收消息//监听queue1队列@RabbitListener(queues = "queue1")private void receiveMessage2(String massage) throws InterruptedException {String coloredMessage = String.format("\\u001B[0m", massage); // 黄色字体//用什么类型发送,就接收什么类型log.info("receiveMessage2接收到的消息为: " + massage);//手动阻塞,模拟消息处理中Thread.sleep(800);}
}

        2)发送多条消息到队列中:

    @Testvoid contextLoads() {//发送多条消息到queue1队列中setRabbitTemplate();}// 首先引入依赖@Autowiredprivate RabbitTemplate rabbitTemplate;private void setRabbitTemplate(){for (int i = 1; i <= 100; i++) {rabbitTemplate.convertAndSend("queue1","接收到第"+i+"条消息");}}

测试结果:

        很明显看得出来,消费能力强的消费者处理队列中的消息越多。正所谓能者多劳,这样充分利用每一个消费者的处理能力,可以有效避免消息积压问题。

        4.0 交换机类型

        在之前没有交换机,都是生产者直接发送消息给队列,而一旦引入交换机,消息发送的模式会有很大变化:

        Exchange 交换机,只负责转发消息,不具备存储消息的能力,因此如果没有任何队列与 Exchange 绑定,或者没有符合路由规则的队列,那么消息会丢失。

        而如果将消息直接发送到队列中,那么队列可以将消息进行存储,不会丢失。

交换机的类型有四种:

        1)Fanout:广播,将消息交给所有绑定到交换机的队列。

        2)Direct:订阅,基于 RoutingKey(路由 key)发送给订阅了消息的队列。

        3)Topic:通配符订阅,与 Direct 类型,只不过 RoutingKey 可以使用通配符。

        4)Headers:头匹配,基于 MQ 的消息匹配,用的较少。

        4.1 Fanout 交换机

        在广播模式下,消息发送流程是这样的:

  • 1) 可以有多个队列

  • 2) 每个队列都要绑定到 Exchange(交换机)

  • 3) 生产者发送的消息,只能发送到交换机

  • 4) 交换机把消息发送给绑定过的所有队列

  • 5) 订阅队列的消费者都能拿到消息

广播模式演示:

        先创建 Fanout 类型的交换机:

        再创建两个队列 queue1、queue2:

        接着 xbs.fanout 交换需要绑定 queue1、queue2 队列:

        再接着用代码实现发送消息、接收消息:

代码如下:

        1)接收消息:

@Slf4j
@Component
public class RabbitMQ {//接收消息//监听queue1队列@RabbitListener(queues = "queue1")private void receiveMessage1(String massage) throws InterruptedException {String coloredMessage = String.format("\u001B[32m%s\u001B[0m", massage); // 绿色字体//用什么类型发送,就接收什么类型log.info("receiveMessage1接收到的消息为: " + coloredMessage);}//接收消息//监听queue2队列@RabbitListener(queues = "queue2")private void receiveMessage2(String massage) throws InterruptedException {//用什么类型发送,就接收什么类型log.info("receiveMessage2接收到的消息为: " + massage);}
}

        2)发送消息:

    @Testvoid contextLoads() {//发送多条消息到queue1队列中sendMessageByExchange("广播通知:狼来啦!!!!!!");}// 首先引入依赖@Autowiredprivate RabbitTemplate rabbitTemplate;//发送消息到交换机private void sendMessageByExchange(String msg){rabbitTemplate.convertAndSend("xbs.fanout","",msg);}

执行结果:

        4.2 Direct 交换机

        在 Fanout 模式中,一条消息,会被所有订阅的队列都消费。但是,在某些场景下,我们希望不同的消息被不同的队列消费。这时就要用到 Direct 类型的 Exchange 。

在Direct模型下:

        1)队列与交换机的绑定,不能是任意绑定了,而是要指定一个 RoutingKey(路由key)

        2)消息的发送方在 向 Exchange 发送消息时,也必须指定消息的 RoutingKey。

        3)Exchange 不再把消息交给每一个绑定的队列,而是根据消息的 RoutingKey 进行判断,只有队列的 RoutingKey 与消息的 RoutingKey 完全一致,才会接收到消息。

Direct 交换机演示:

        1)首先创建 xbs.direct 交换机:

        2)再创建两个队列 queue3、queue4:

        3)xbs.direct 交换机绑定两个队列:

        4)通过 RoutingKey 路由关键字来指定。对于 queue4 队列也是同样的道理:

        接着使用代码来实现发送消息、接收消息:

代码实现:

        1)接收消息:

@Slf4j
@Component
public class RabbitMQ {//接收消息//监听queue1队列@RabbitListener(queues = "queue3")private void receiveMessage1(String massage) throws InterruptedException {String coloredMessage = String.format("\u001B[32m%s\u001B[0m", massage); // 绿色字体//用什么类型发送,就接收什么类型log.info("receiveMessage1接收到的消息为: " + coloredMessage);}//接收消息//监听queue2队列@RabbitListener(queues = "queue4")private void receiveMessage2(String massage) throws InterruptedException {//用什么类型发送,就接收什么类型log.info("receiveMessage2接收到的消息为: " + massage);}
}

        2)发送消息:

    @Testvoid contextLoads() {sendMessageByDirect1();}@Testpublic void sendMessageByDirect1(){rabbitTemplate.convertAndSend("xbs.direct","red","指定 red 路由进行发送消息");}@Testpublic void sendMessageByDirect2(){rabbitTemplate.convertAndSend("xbs.direct","blue","指定 blue 路由进行发送消息");}

执行结果:

        当指定 red 的路由关键字,那么只有队列 queue1 才能接收得到,因此对应的消费者才能进行处理。

        同理,当指定 blue 的路由关键字,那么只有队列 queue2 才能接收得到,因此对应的消费者才能进行处理。

        4.3 Topic 交换机

        Topic 类型的 Exchange 与 Direct 相比,都是可以根据 RoutingKey 吧消息路由到不同的队列。只不过 Topic 类型 Exhchange 可以让队列在绑定 BindingKey 的时候使用通配符。

通配符规则:

        1)#:匹配一个或多个词。

        2)*:只匹配一个词。

Topic 交换机演示:

        1)创建 xbs.topic 交换机:

        2)创建两个队列 queue5、queue5:

        3)将该两个队列进行绑定到 xbs.topic 交换机上,并且指定对应通配符的 RoutingKey 关键字:

        4)使用代码实现发送消息、接收消息:

代码如下:

        发送消息:

    @Testvoid contextLoads() {//两个队列绑定同一个交换机sendMessageByTopic1();}// 首先引入依赖@Autowiredprivate RabbitTemplate rabbitTemplate;@Testpublic void sendMessageByTopic1(){rabbitTemplate.convertAndSend("xbs.topic","class.student","该消息通知学生以及老师:狼来啦!!");}@Testpublic void sendMessageByTopic2(){rabbitTemplate.convertAndSend("xbs.topic","class.teacher","该消息不会通知学生:狼来啦!!");}

        接收消息:

@Slf4j
@Component
public class RabbitMQ {//接收消息//监听queue1队列@RabbitListener(queues = "queue5")private void receiveMessage1(String massage) throws InterruptedException {String coloredMessage = String.format("\u001B[32m%s\u001B[0m", massage); // 绿色字体//用什么类型发送,就接收什么类型log.info("receiveMessage1接收到的消息为: " + coloredMessage);}//接收消息//监听queue2队列@RabbitListener(queues = "queue6")private void receiveMessage2(String massage) throws InterruptedException {//用什么类型发送,就接收什么类型log.info("receiveMessage2接收到的消息为: " + massage);}
}

测试结果:

        当发送的消息的关键字为 class.student 时,则会将该消息发送到 queue5、queue6 队列中,所以对应的两个消费者都能进行消费:

        当发送的消息的关键字为 class.teacher 时,则该消息只会发送到 queue6 队列中:

        5.0 声明队列和交换机

        在之前都是基于 RabbitMQ 控制台来创建队列、交换机。但是在实际开发时,队列和交换机是程序员定义的,将来项目上线,又要交给运维去创建,那么程序员就需要把程序中运行的所有队列和交换机都写下来,交给运维,在这个过程中是是很容易出错的。

        因此推荐的做法是由程序员启动时检查队列和交换机是否存在,如果不存在则自动创建。

        5.1 基本的 API

        1)SpringAMQP 提供了一个 Queue 类,用来创建队列:

        2)SpringAMQP 还提供了一个 Exchange 接口,来表示所有不同类型的交换机:

        我们可以自己创建队列和交换机,不过 SpringAMQP 还提供了 ExchangeBuilder 来简化这个过程:

        3)而在绑定队列和交换机时,则需要使用 BindingBuilder 来创建 Binding 对象:

        5.2 fanout 示例

        在 FanoutExchangeCom 中创建一个类,声明两个队列和一个交换机:

@Configuration
public class FanoutExchangeCom {/*** 声明交换机* @return Fanout类型交换机*/@Beanpublic FanoutExchange fanoutExchange(){return ExchangeBuilder.fanoutExchange("tt.fanout").build();}/*** 声明队列* @return Queue*/@Beanpublic Queue queue1(){return new Queue("tt.queue1");}/*** 声明队列* @return Queue*/@Beanpublic Queue queue2(){return new Queue("tt.queue2");}/*** 绑定第一个队列和交换机*/@Beanpublic Binding bindingQueue1(Queue queue1, FanoutExchange fanoutExchange){return BindingBuilder.bind(queue1).to(fanoutExchange);}/*** 绑定第二个队列和交换机*/@Beanpublic Binding bindingQueue2(Queue queue2, FanoutExchange fanoutExchange){return BindingBuilder.bind(queue2).to(fanoutExchange);}
}

        当代码运行起来,则会自动创建交换机和队列:

        5.3 direct 示例

        在 DirectExchangeCom 中创建一个类,声明两个队列和一个交换机:

@Configuration
public class DirectExchangeCom {/*** 创建一个direct交换机* @return DirectExchange*/@Beanpublic DirectExchange directExchange(){return ExchangeBuilder.directExchange("tt.direct").build();}/*** 创建一个队列* @return Queue*/@Beanpublic Queue queue3(){return new Queue("tt.queue3");}/*** 创建一个队列* @return Queue*/@Beanpublic Queue queue4(){return new Queue("tt.queue4");}/*** 绑定队列到交换机* @param queue1* @param directExchange* @return 且指定路由键 xbs*/@Beanpublic Binding binding1(Queue queue3,DirectExchange directExchange){return BindingBuilder.bind(queue3).to(directExchange).with("xbs");}/*** 绑定队列到交换机* @param queue2* @param directExchange* @return 且指定路由键 tt*/@Beanpublic Binding binding2(Queue queue4,DirectExchange directExchange){return BindingBuilder.bind(queue4).to(directExchange).with("tt");}}

程序执行结果:

        自动创建队列:

        自动创建交换机:

        5.4 基于注解声明

        基于注解 @Bean 的方式声明队列和交换机比价麻烦,Spring 还提供了基于注解方式来声明。在监听队列 @RabbitListener 注解上进行添加相关的注解来声明交换机或者队列。

        5.4.1 Fanout 模式的交换机

代码如下:

    //基于注解声明交换机和队列@RabbitListener(bindings = @QueueBinding(value = @Queue(name = "tt.queue5"),exchange = @Exchange(name = "tt.fanoutExchange", type = ExchangeTypes.FANOUT)))private void sendMessage(String massage) throws InterruptedException {log.info("sendMessage发送的消息为: " + massage);}

        通过注解 @Queue 声明队列,@Exchange 声明交换机,可以指定类型,默认的类型为 Direct 类型的交换机。最后再通过 @QueueBinding 注解进行绑定队列到交换机中。

当程序运行起来:

        tt.fanoutExchange 交换机:

       tt.queue5 队列:

    //基于注解声明交换机和队列@RabbitListener(bindings = @QueueBinding(value = @Queue(name = "tt.queue6"),exchange = @Exchange(name = "tt.directExchange",type = ExchangeTypes.DIRECT),key = "tt.xbs"))private void listenMessageByDirect(String massage) throws InterruptedException {log.info("sendMessageByFanout发送的消息为: " + massage);}

        5.4.2 Direct 模式的交换机

代码如下:

    //基于注解声明交换机和队列@RabbitListener(bindings = @QueueBinding(value = @Queue(name = "tt.queue6"),exchange = @Exchange(name = "tt.directExchange",type = ExchangeTypes.DIRECT),key = "tt.xbs"))private void listenMessageByDirect(String massage) throws InterruptedException {log.info("sendMessageByFanout发送的消息为: " + massage);}

        通过 @Queue 注解声明队列,@Exchange 注解声明交换机,且指定交换机类型。还通过 @QueueBinding 注解将队列绑定到交换机中,通过 key 指定路由关键字。

程序执行结果:

        tt.directExchange 交换机:

        tt.queue6 队列:

        6.0 消息转换器

        Spring 的消息发送代码接收的消息体是一个 Object:

    // 首先引入依赖@Autowiredprivate RabbitTemplate rabbitTemplate;@Testvoid contextLoads() {//发送Object类型的消息textObject();}@Testpublic void textObject(){User user = new User(1,"xbs","123456",null,null);rabbitTemplate.convertAndSend("text",user);}

        此外,还需要确保 User 类实现 Serializable 接口,这是因为 SimpleMessageConverter 只支持 String、byte[] 和 Serializable 类型的消息负载。

此时队列中的消息:

        在数据传输时,SimpleMessageConverter 会把发送的消息序列化为字节发送给 MQ,接收消息的时候,还会把字节反序列化为 Java 对象。

        只不过,默认情况下 Spring 采用的序列化方式是 JDK 序列化。从所周知,JDK 序列化存在以下问题:

        1)数据体积过大。

        2)有安全漏洞。

        3)可读性差。

        6.1 配置 JSON 转化器

        显然,JDK 序列化方式并不合适,期望消息体的体积更小、可读性更高,因此可以使用 JSON 方式来做序列化和反序列化。

        1)引入依赖:

<dependency><groupId>com.fasterxml.jackson.dataformat</groupId><artifactId>jackson-dataformat-xml</artifactId><version>2.9.10</version>
</dependency>

        注意,如果项目中引入了 Spring-boot-start-wed 依赖,则无需再次引入 Jackson 依赖。

        2)添加新 MessageConverter 类型

    @Beanpublic MessageConverter messageConverter(){//1.定义消息转化器Jackson2JsonMessageConverter converter = new Jackson2JsonMessageConverter();//2.配置自定创建消息id,用于识别不同消息,也可以在业务中基于ID判断是否是重复消息converter.setCreateMessageIds(true);return converter;}

        消息转换器中添加的 messageId 可以便于我们将来做幂等性判断。

配置好了 Json 消息转化器之后,进行测试:

        发送的消息是 User 类型,且该 User 类不需要再实现 SimpleMessageConverter 接口。

    // 首先引入依赖@Autowiredprivate RabbitTemplate rabbitTemplate;@Testvoid contextLoads() {//发送Object类型的消息textObject();}@Testpublic void textObject(){User user = new User(1,"xbs","123456",null,null);rabbitTemplate.convertAndSend("text",user);}

执行结果:

        可以很容易看到消息体中的内容,且字节只需要 81 个。

        6.2 实现业务幂等性

        幂等是一个数学概念,用函数表达式来描述:f(x)=f(f(x)) 。在程序开发中,则是指同一个业务,执行一次或多次对业务状态的影响是一致的。

        实现业务幂等可以通过唯一消息 id,是给每个消息都设置一个唯一 ID,利用 id 区分是否是重复消息:

        1)每一条消息都生成一个唯一的 id,与消息一起投递给消费者。

        2)消费者接收到消息后处理自己的业务,业务处理成功后将消息 ID 保存到数据库。

        3)如果下次又收到相同消息,去数据库查询判断是否存在,存在则为重复消息放弃处理。

        那么通过配置 JSON 转化器中,设置 setCreateMessageIds(true) 方法,接收者都可以在消息中获取到唯一 id 。

代码如下:

        1)发送消息:

    // 首先引入依赖@Autowiredprivate RabbitTemplate rabbitTemplate;@Testvoid contextLoads() {//发送Object类型的消息textObject();}@Testpublic void textObject(){User user = new User(1,"xbs","123456",null,null);rabbitTemplate.convertAndSend("textExchange", "xbs", user, new MessagePostProcessor() {@Overridepublic Message postProcessMessage(Message message) throws AmqpException {message.getMessageProperties().setMessageId("123456");return message;}});}

         通过匿名类 MessagePostProcessor 重写 postProcessMessage 方法,将消息发送之前对消息的属性进行修改,比如说配置 id 属性、配置消息头等等,而对于 postProcessMessage 方法不能携带消息本体,只能在 convertAndSend() 方法中属性进行配置,比如说发送 user 实体类。

        2)接收消息:

    @AutowiredMessageConverter messageConverter;@RabbitListener(queues = "text")private void listenMessage(Message massage) throws InterruptedException {log.info("sendMessage发送的消息为: " + massage.getMessageProperties().getMessageId());User user = (User) messageConverter.fromMessage(massage);log.info("sendMessage发送的消息为: " + user);}

        监听 text 队列中的消息,接收的参数不再是 user,而是 Message 类型,通过 massage.getMessageProperties().getMessageId() 获取唯一 id,再通过 MessageConverter 消息转化器将 massage 的本体消息转化为 User 类型。

接收消息的结果:

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

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

相关文章

基础归一化方法 数学理论及其matlab代码

归一化是一种简化计算的方式&#xff0c;即将有量纲的表达式&#xff0c;经过变换&#xff0c;化为无量纲的表达式&#xff0c;成为标量。在多种计算中都经常用到这种方法。以下是常见的基础归一化方法&#xff1a;Min-Max 归一化&#xff0c;Z-Score 归一化、小数定标归一化、…

漫漫数学之旅038

文章目录 经典格言数学习题古今评注名人小传 - 古斯塔夫福楼拜 经典格言 诗歌是一门像几何一样严格的科学。——古斯塔夫福楼拜&#xff08;Gustave Flaubert&#xff09; 古斯塔夫福楼拜将诗歌比作几何学&#xff0c;强调了诗歌创作的严谨性。他认为诗歌不仅仅是情感的流露&a…

前端md5加密

npm下载 npm install --save ts-md5页面引入 import { Md5 } from ts-md5使用 const md5PwdMd5.hashStr("123456")md5Pwd&#xff08;加密后的数据&#xff09; .toUpperCase()方法转大写

植物神经紊乱不用怕,这些维生素来帮你!

在现代快节奏的生活中&#xff0c;植物神经紊乱已成为一种常见的健康问题。你是否经常感到焦虑、失眠、疲劳&#xff0c;甚至消化不良&#xff1f;这些可能都是植物神经紊乱的表现。幸运的是&#xff0c;通过适当补充维生素&#xff0c;我们可以有效缓解症状&#xff0c;恢复身…

华为ENSP--ISIS路由协议

项目背景 为了确保资源共享、办公自动化和节省人力成本&#xff0c;公司E申请两条专线将深圳总部和广州、北京两家分公司网络连接起来。公司原来运行OSFP路由协议&#xff0c;现打算迁移到IS-IS路由协议&#xff0c;张同学正在该公司实习&#xff0c;为了提高实际工作的准确性和…

二分查找习题篇(下)

二分查找习题篇(下) 1.山脉数组的峰顶索引 题目描述&#xff1a; 给定一个长度为 n 的整数 山脉 数组 arr &#xff0c;其中的值递增到一个 峰值元素 然后递减。 返回峰值元素的下标。 你必须设计并实现时间复杂度为 O(log(n)) 的解决方案。 示例 1&#xff1a; 输入&#xf…

playground.tensorflow神经网络可视化工具

playground.tensorflow 是一个可视化工具&#xff0c;用于帮助用户理解深度学习和神经网络的基本原理。它通过交互式界面使用户能够构建、训练和可视化简单的神经网络模型。以下是一些主要的数学模型和公式原理&#xff0c;它们在这个平台中被应用&#xff1a; 1. 线性模型 线…

LSM-TREE和SSTable

一、什么是LSM-TREE LSM Tree 是一种高效的写优化数据结构&#xff0c;专门用于处理大量写入操作 在一些写多读少的场景&#xff0c;为了加快写磁盘的速度&#xff0c;提出使用日志文件追加顺序写&#xff0c;加快写的速度&#xff0c;减少随机读写。但是日志文件只能遍历查询…

SDL线程

文章目录 SDL线程相关 SDL线程相关 SDL线程创建&#xff1a;SDL_CreateThreadSDL线程等待: SDL_WaitThreadSDL互斥锁 :SDL_CreateMutex/SDL_DestoryMutexSDL锁定互斥: SDL_LockMutex/SDL_UnlockMutexSDL条件变量:SDL_CreateCond/SDL_DestoryCondSDL条件变量 等待通知: SDL_Con…

DevExpress JS ASP.NET Core v24.1亮点 - 支持DateOnly/TimeOnly类型

DevExtreme拥有高性能的HTML5 / JavaScript小部件集合&#xff0c;使您可以利用现代Web开发堆栈&#xff08;包括React&#xff0c;Angular&#xff0c;ASP.NET Core&#xff0c;jQuery&#xff0c;Knockout等&#xff09;构建交互式的Web应用程序。从Angular和Reac&#xff0c…

【HarmonyOS】键盘遮挡输入框UI布局处理

【HarmonyOS】键盘遮挡输入框UI布局处理 问题背景&#xff1a; 在开发输入框UI时&#xff0c;特别是登录页面的密码输入框靠下&#xff0c;或者是评论底部的pop弹框。 当我们输入框获得焦点后&#xff0c;键盘自下而上显示&#xff0c;一般情况下会遮挡住我们的UI布局。 导致…

Rust重写万物之——从头开始编写浏览器引擎

一款用 Rust 编写的全新“轮子”最近备受关注—— 因不满大公司垄断,Gosub 项目团队用 Rust 从头开始编写了一个新的浏览器引擎,目前 star 数已超过 3k。 Gosub 项目的诞生是因为不少用户对当前的 Web 浏览器现状感到不满。 尽管市面上有许多浏览器可供选择,但其中大多数…

抗辐照MCU芯片工艺解析:如何保障芯片的可靠性

行星探索、轨道飞行器任务和空间研究在内的太空项目需要创新的航天器系统技术提供通信与处理功能。随着商业航天的发展&#xff0c;对于航天电子系统需要考虑高可靠与高性能的同时&#xff0c;还需要考虑降低开发成本和缩短上市时间。 以MCU芯片AS32A401为例&#xff0c;该芯片…

python(自用查看版)

目录 1.注意事项 1.1 python的除法不是整除&#xff0c;得到的是浮点数 1.2算术符号基于数学的算术优先级。具体可自行查看。 1.3注释 1.4缩进 1.5换行 1.6常见关键字 1.7续行符 1.8报错 1.9赋值 1.10比较运算符 2.常量和表达式 3.变量 4.数据类型 4.1整型int …

微信小程序,点击bindtap事件后,没有跳转到详情页,有可能是app.json中没有正确配置页面路径

文章目录 1、index.wxml2、index.js检查点1. 确保目标页面存在2. 确保页面路径配置正确3. 检查页面接收参数productDetail.jsproductDetail.wxmlproductDetail.wxss 总结 1、index.wxml <!-- 商品搜索结果卡片容器 --><view class"search-result"><bl…

科技改变生活:最新智能开关、调光器及插座产品亮相

根据QYResearch调研团队的最新力作《欧洲开关、调光器和插座市场报告2023-2029》显示&#xff0c;预计到2029年&#xff0c;欧洲开关、调光器和插座市场的规模将攀升至57.8亿美元&#xff0c;并且在接下来的几年里&#xff0c;将以4.2%的复合年增长率&#xff08;CAGR&#xff…

OpenGL入门006——着色器在纹理混合中的应用

本节将理解顶点和片段着色器在纹理混合中的应用 文章目录 一些概念纹理时间依赖动画 实战简介dependenciesshader.fsshader.vsteenager.pngtex.png utilswindowFactory.hshader.hRectangleModel.hRectangleModel.cpp main.cppCMakeLists.txt最终效果 一些概念 纹理 概述&…

【9695】基于springboot+vue的学生就业管理系统

作者主页&#xff1a;Java码库 主营内容&#xff1a;SpringBoot、Vue、SSM、HLMT、Jsp、PHP、Nodejs、Python、爬虫、数据可视化、小程序、安卓app等设计与开发。 收藏点赞不迷路 关注作者有好处 文末获取免费源码 项目描述 本学生就业管理系统以springboot作为框架&#xff…

C语言的进制表示【八进制,十六进制】

文章目录 C语言的进制表示【八进制&#xff0c;十六进制】题目介绍C语言的进制表示1. 十进制2. 八进制3. 十六进制4. 二进制 进制表示总结 C语言的进制表示【八进制&#xff0c;十六进制】 题目介绍 故事的起因是今天在群里看到有人发来的问题 //原题目 int main() {int a 0…

高通Quick板上安装编译Ros1 noetic,LeGO_LOAM,FAR_Planner和rslidar_sdk

环境要求&#xff1a; 这里quick板上安装的是Ubuntu20.04版本 Ros Noeti安装&#xff1a; 1.设置软件源&#xff1a; 官方提供的软件源&#xff1a; sudo sh -c echo "deb http://packages.ros.org/ros/ubuntu $(lsb_release -sc) main" > /etc/apt/sources.list.…