RabbitMQ --- 直连交换机 【 同步操作,等到消费者处理完后返回处理结果 】
1.前言
RabbleMQ这种消息中间件,主要的功能是使用异步操作,来达到解耦的目的,比如,有系统a和系统b,
系统a通过消息中间件通知系统b来做业务,那么系统a只需要把要做的事情【也就是消息】发给消息中间件后,
消息中间件就会把消息转发给系统b,系统a不需要关心系统b是怎么完成业务的,也不需要关心业务完成的结果,
这是就是异步操作。
如果系统a希望获得系统b的处理结果,那么系统a使用消息中间件发送消息后需要原地等待,做阻塞操作,但是
等待时长不能超过最大超时时间,可设置RabbleMQ自定义超时时间,这样还不如直接调用该业务呢,何必再加个消息消息中间件通知他来做?
所以,一般不会做这样的同步阻塞操作,违背了消息中间件的开发初衷【提高吞吐量和系统业务的响应速度】,虽然
不影响解耦度,但是这样的操作使得消息中间件变得不伦不类了。
那么要问了,使用RabbleMQ消息中间件的具体好处是什么?
- 第一,解耦,是非常明显的好处,通过消息中间件,系统a通知系统b做什么业务,不需要关心系统b是怎么实现业务的。
- 第二,异步,非必要的业务,不需要特别关心结果的业务,可以写入消息中间件以异步方式操作,横向编程,加快响应速度。
- 第三,削峰,并发量大的时候直接将所有数据怼到数据库,数据库会异常的,使用消息队列的特性,所有的操作会进行排队,
- 消息也不会丢失,当消息被消费后才会销毁,比如秒杀系统就是这样实现的。
那么,虽然同步阻塞操作偶很大的闭端,那么到底该怎么操作?
- 这篇随笔 以 随笔 https://www.cnblogs.com/c2g5201314/p/13156932.html 为基础 ,修改部分代码实现演示
2.操作
(1)修改消息生产者的消息生产类的 rabbitTemplate 模板发送消息方法 ,同步则使用 convertSendAndReceive(),参数与异步非阻塞操作的一样。
源码


- package com.example.rabbitmqproducer1004.rabbitmqFactory;
- import com.example.rabbitmqproducer1004.config.RabbitmqConfig;
- import org.slf4j.Logger;
- import org.slf4j.LoggerFactory;
- import org.springframework.amqp.rabbit.connection.CorrelationData;
- import org.springframework.amqp.rabbit.core.RabbitTemplate;
- import org.springframework.beans.factory.annotation.Autowired;
- import org.springframework.context.annotation.Bean;
- import org.springframework.stereotype.Component;
- import java.util.UUID;
- /**
- * 消息生产类
- */
- @Component
- //实现接口
- public class SendMessage {
- //需要设置回调方法,获取消费结果才需要实现 RabbitTemplate.ConfirmCallback 接口,
- //public class SendMessage implements RabbitTemplate.ConfirmCallback {
- Logger logger = LoggerFactory.getLogger(this.getClass());
- //======================================================================
- // /**
- // * 方法一:设置回调方法,获取消费结果,
- // * <p>
- // * 缺点是:必须手动配置RabbitTemplate模板 ,代码量大
- // */
- //
- // //存储 rabbitmq模板的临时变量
- // private final RabbitTemplate rabbitTemplate;
- //
- // /**
- // * 构造注入rabbitmq模板,这样可以设置回调方法,获取消费结果,但是必须手动配置RabbitTemplate模板
- // */
- // @Autowired
- // public SendMessage(RabbitTemplate rabbitTemplate) {
- // this.rabbitTemplate = rabbitTemplate;
- // //设置确认回调的方法,参数类型为ConfirmCallback
- // this.rabbitTemplate.setConfirmCallback(this);
- // }
- //
- // /**
- // * 回调方法,获取消费结果
- // *
- // * @param correlationData 关联数据
- // * @param b 消息是否被消费成功,成功为true ,失败为false
- // * @param s 原因 ,消费成功则返回null,否则返回失败原因
- // */
- // @Override
- // public void confirm(CorrelationData correlationData, boolean b, String s) {
- // logger.warn("回调的连接数据:" + correlationData);
- // if (correlationData != null) {
- // //CorrelationData [id=1bcab025-2b4c-4f74-a22d-41007e30f551]
- // logger.warn("获取correlationData的id值:" + correlationData.getId());
- // }
- // //1bcab025-2b4c-4f74-a22d-41007e30f551
- // if (b) {
- // logger.warn("回调结果:消息消费成功");
- // } else {
- // logger.warn("回调结果:失败。原因:" + s);
- // }
- // }
- //========================================================================
- // /**
- // * 方法二 :不需要获取获取消费结果,只需要发送即可
- // *
- // * 优点:自动装配,代码量少
- // */
- @Autowired
- private RabbitTemplate rabbitTemplate;
- //========================================================================
- /**
- * 发送消息
- * <p>
- * 参数是消息内容
- */
- public void send(String message) {
- logger.warn("发送消息,内容:" + message);
- /**
- * 方法一:异步操作,不等待消费者端返回处理结果,设置在回调操作的关联数据,用于识别是哪一条消息和确认是否执行成功
- */
- //// 实例关联数据对象,使用UUID随机数 作为 回调id
- // CorrelationData correlationData = new CorrelationData(UUID.randomUUID().toString());
- //// 发送消息 ,参数分别是 : 指定的交换机名字 、指定的路由关键字、消息字符串、关联数据
- // rabbitTemplate.convertAndSend(RabbitmqConfig.EXCHANG_1,RabbitmqConfig.ROUTINGKEY_1,message,correlationData);
- /**
- * 方法二:异步操作,不等待消费者端返回处理结果,且在消息回调操作的关联数据为null,如果不做回调操作,则建议这样使用
- */
- // //发送消息 ,参数分别是 : 指定的交换机名字 、指定的路由关键字、消息字符串
- // rabbitTemplate.convertAndSend(RabbitmqConfig.EXCHANG_1, RabbitmqConfig.ROUTINGKEY_1, message);
- /**
- * 方法三:同步操作,等待消费者端返回处理结果
- */
- Object dd = rabbitTemplate.convertSendAndReceive(RabbitmqConfig.EXCHANG_1, RabbitmqConfig.ROUTINGKEY_1, message);
- logger.warn("你大爷的,有么有延迟?是不是同步阻塞?");
- logger.warn("结果是什么???==" + dd);
- }
- }
(2)修改消息消费者的监听方法 ,可设置任意类型返回值,但是在生产者端需要解析,我这里使用 字符串,休眠3秒
源码


- package com.example.rabbitmqconsumer1002.rabbitmqListener;
- import com.example.rabbitmqconsumer1002.config.RabbitConfig;
- import org.slf4j.Logger;
- import org.slf4j.LoggerFactory;
- import org.springframework.amqp.rabbit.annotation.RabbitHandler;
- import org.springframework.amqp.rabbit.annotation.RabbitListener;
- import org.springframework.stereotype.Component;
- /**
- * 消息监听类--发短信
- */
- //注册bean
- @Component
- //设置需要监听的消息队列
- @RabbitListener(queues = RabbitConfig.QUEUE_1)
- public class SendMessageListener {
- Logger logger = LoggerFactory.getLogger(getClass());
- //消息事件处理--有返回结果
- @RabbitHandler
- public String sendMessage(String msg) throws InterruptedException {
- logger.warn("我是端口1002的消费者,收到信息:" + msg);
- logger.warn("休眠3秒");
- Thread.sleep(3000);
- logger.warn("休眠结束");
- return "发送成功,是同步阻塞的么?";
- }
- }
(3)启动工程
访问网址 http://localhost:1004/mq?msg=你大爷,帮我发短信3999
查看消费者端控制台打印
查看生产者端控制台打印
生产者端等待了3秒后才收到结果
3.如果换成10秒会怎么样?
修改消息消费者的监听方法
启动工程后 访问 网址 http://localhost:1004/mq?msg=你大爷,帮我发短信3999
查看消费者端控制台打印
查看生产者端控制台打印
打印控制台源码


- 2020-06-19 02:40:17.100 WARN http-nio-1004-exec-4 | com.example.rabbitmqproducer1004.rabbitmqFactory.SendMessage | 发送消息,内容:你大爷,帮我发短信3999
- 2020-06-19 02:40:17.111 INFO http-nio-1004-exec-4 | org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler | Initializing ExecutorService
- 2020-06-19 02:40:17.117 INFO http-nio-1004-exec-4 | org.springframework.amqp.rabbit.listener.DirectReplyToMessageListenerContainer | Container initialized for queues: [amq.rabbitmq.reply-to]
- 2020-06-19 02:40:17.128 INFO http-nio-1004-exec-4 | org.springframework.amqp.rabbit.listener.DirectReplyToMessageListenerContainer | SimpleConsumer [queue=amq.rabbitmq.reply-to, consumerTag=amq.ctag-pPnAaXXfnkytocGRd7ES5Q identity=7ba94c9e] started
- 2020-06-19 02:40:22.137 WARN http-nio-1004-exec-4 | com.example.rabbitmqproducer1004.rabbitmqFactory.SendMessage | 你大爷的,有么有延迟?是不是同步阻塞?
- 2020-06-19 02:40:22.137 WARN http-nio-1004-exec-4 | com.example.rabbitmqproducer1004.rabbitmqFactory.SendMessage | 结果是什么???==null
- 2020-06-19 02:40:27.158 WARN pool-1-thread-4 | org.springframework.amqp.rabbit.core.RabbitTemplate | Reply received after timeout for 1
- 2020-06-19 02:40:27.163 WARN pool-1-thread-4 | org.springframework.amqp.rabbit.listener.ConditionalRejectingErrorHandler | Execution of Rabbit message listener failed.
- org.springframework.amqp.rabbit.listener.exception.ListenerExecutionFailedException: Listener threw exception
- at org.springframework.amqp.rabbit.listener.AbstractMessageListenerContainer.wrapToListenerExecutionFailedExceptionIfNeeded(AbstractMessageListenerContainer.java:1651) ~[spring-rabbit-2.1.7.RELEASE.jar:2.1.7.RELEASE]
- at org.springframework.amqp.rabbit.listener.AbstractMessageListenerContainer.doInvokeListener(AbstractMessageListenerContainer.java:1555) ~[spring-rabbit-2.1.7.RELEASE.jar:2.1.7.RELEASE]
- at org.springframework.amqp.rabbit.listener.AbstractMessageListenerContainer.actualInvokeListener(AbstractMessageListenerContainer.java:1478) ~[spring-rabbit-2.1.7.RELEASE.jar:2.1.7.RELEASE]
- at org.springframework.amqp.rabbit.listener.AbstractMessageListenerContainer.invokeListener(AbstractMessageListenerContainer.java:1466) ~[spring-rabbit-2.1.7.RELEASE.jar:2.1.7.RELEASE]
- at org.springframework.amqp.rabbit.listener.AbstractMessageListenerContainer.doExecuteListener(AbstractMessageListenerContainer.java:1461) ~[spring-rabbit-2.1.7.RELEASE.jar:2.1.7.RELEASE]
- at org.springframework.amqp.rabbit.listener.AbstractMessageListenerContainer.executeListener(AbstractMessageListenerContainer.java:1410) ~[spring-rabbit-2.1.7.RELEASE.jar:2.1.7.RELEASE]
- at org.springframework.amqp.rabbit.listener.DirectMessageListenerContainer$SimpleConsumer.callExecuteListener(DirectMessageListenerContainer.java:996) [spring-rabbit-2.1.7.RELEASE.jar:2.1.7.RELEASE]
- at org.springframework.amqp.rabbit.listener.DirectMessageListenerContainer$SimpleConsumer.handleDelivery(DirectMessageListenerContainer.java:956) [spring-rabbit-2.1.7.RELEASE.jar:2.1.7.RELEASE]
- at com.rabbitmq.client.impl.ConsumerDispatcher$5.run(ConsumerDispatcher.java:149) [amqp-client-5.4.3.jar:5.4.3]
- at com.rabbitmq.client.impl.ConsumerWorkService$WorkPoolRunnable.run(ConsumerWorkService.java:104) [amqp-client-5.4.3.jar:5.4.3]
- at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149) [na:1.8.0_221]
- at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624) [na:1.8.0_221]
- at java.lang.Thread.run(Thread.java:748) [na:1.8.0_221]
- Caused by: org.springframework.amqp.AmqpRejectAndDontRequeueException: Reply received after timeout
- at org.springframework.amqp.rabbit.core.RabbitTemplate.onMessage(RabbitTemplate.java:2535) ~[spring-rabbit-2.1.7.RELEASE.jar:2.1.7.RELEASE]
- at org.springframework.amqp.rabbit.listener.DirectReplyToMessageListenerContainer.lambda$setMessageListener$1(DirectReplyToMessageListenerContainer.java:115) ~[spring-rabbit-2.1.7.RELEASE.jar:2.1.7.RELEASE]
- at org.springframework.amqp.rabbit.listener.AbstractMessageListenerContainer.doInvokeListener(AbstractMessageListenerContainer.java:1552) ~[spring-rabbit-2.1.7.RELEASE.jar:2.1.7.RELEASE]
- ... 11 common frames omitted
- 2020-06-19 02:40:27.164 ERROR pool-1-thread-4 | org.springframework.amqp.rabbit.listener.DirectReplyToMessageListenerContainer | Failed to invoke listener
- org.springframework.amqp.rabbit.listener.exception.ListenerExecutionFailedException: Listener threw exception
- at org.springframework.amqp.rabbit.listener.AbstractMessageListenerContainer.wrapToListenerExecutionFailedExceptionIfNeeded(AbstractMessageListenerContainer.java:1651) ~[spring-rabbit-2.1.7.RELEASE.jar:2.1.7.RELEASE]
- at org.springframework.amqp.rabbit.listener.AbstractMessageListenerContainer.doInvokeListener(AbstractMessageListenerContainer.java:1555) ~[spring-rabbit-2.1.7.RELEASE.jar:2.1.7.RELEASE]
- at org.springframework.amqp.rabbit.listener.AbstractMessageListenerContainer.actualInvokeListener(AbstractMessageListenerContainer.java:1478) ~[spring-rabbit-2.1.7.RELEASE.jar:2.1.7.RELEASE]
- at org.springframework.amqp.rabbit.listener.AbstractMessageListenerContainer.invokeListener(AbstractMessageListenerContainer.java:1466) ~[spring-rabbit-2.1.7.RELEASE.jar:2.1.7.RELEASE]
- at org.springframework.amqp.rabbit.listener.AbstractMessageListenerContainer.doExecuteListener(AbstractMessageListenerContainer.java:1461) ~[spring-rabbit-2.1.7.RELEASE.jar:2.1.7.RELEASE]
- at org.springframework.amqp.rabbit.listener.AbstractMessageListenerContainer.executeListener(AbstractMessageListenerContainer.java:1410) ~[spring-rabbit-2.1.7.RELEASE.jar:2.1.7.RELEASE]
- at org.springframework.amqp.rabbit.listener.DirectMessageListenerContainer$SimpleConsumer.callExecuteListener(DirectMessageListenerContainer.java:996) [spring-rabbit-2.1.7.RELEASE.jar:2.1.7.RELEASE]
- at org.springframework.amqp.rabbit.listener.DirectMessageListenerContainer$SimpleConsumer.handleDelivery(DirectMessageListenerContainer.java:956) [spring-rabbit-2.1.7.RELEASE.jar:2.1.7.RELEASE]
- at com.rabbitmq.client.impl.ConsumerDispatcher$5.run(ConsumerDispatcher.java:149) [amqp-client-5.4.3.jar:5.4.3]
- at com.rabbitmq.client.impl.ConsumerWorkService$WorkPoolRunnable.run(ConsumerWorkService.java:104) [amqp-client-5.4.3.jar:5.4.3]
- at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149) [na:1.8.0_221]
- at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624) [na:1.8.0_221]
- at java.lang.Thread.run(Thread.java:748) [na:1.8.0_221]
- Caused by: org.springframework.amqp.AmqpRejectAndDontRequeueException: Reply received after timeout
抛出了监听异常,显然是等待超时了,然后继续执行执行下面的操作,返回结果以null处理
4.分析与思考
如果等待10分钟才能运行完这个业务,使用同步阻塞操作,岂不是需要等10分钟?那还要消息中间件有什么用?
因此,一般是使用异步非阻塞操作消息中间件的,因此,会尽可能的把一些不需要返回结果的操作用消息中间件转发完成。
对于失败和抛出异常的操作,可以制作一个死信队列,将这些无法完成的消息重写放入死信队列,让专门的系统监听进行处理,一般这样的业务很少出现,基本上是可以成功完成的,
对那些重要的,必须获取处理结果的业务,直接调用实现就好了,因为这样是默认同步阻塞操作的,不需要再转一趟消息中间件啦。
RabbitMQ --- 直连交换机 【 同步操作,等到消费者处理完后返回处理结果 】的更多相关文章
- RabbitMQ --- 直连交换机 【 有回调方法,获取消费结果 】
1.前言 上一随笔详细记录了直连交换机的方法,发送的消息是异步的,如果消息未被消费者消费,那么可以一直存在消息队列中. 那么有没有办法做一个回调,当消息被消费后,被通知消息成功被消费者消费啦? 答案是 ...
- RabbitMQ --- 直连交换机 【 无回调方法,不能获取消费结果 】
1.前言 消息队列除了kafka 外,还有许多种,比如RabbitMQ .ActiveMQ.ZeroMQ.JMQ等. 老牌的ActiveMQ ,底层使用Java写的,资源消耗大,速度也慢,但是适合 J ...
- rabbitmq学习(二):rabbitmq(消息队列)的作用以及rabbitmq之直连交换机
前言 上篇介绍了AMQP的基本概念,组成及其与rabbitmq的关系.了解了这些东西后,下面我们开始学习rabbitmq(消息队列)的作用以及用java代码和rabbitmq通讯进行消息发布和接收.因 ...
- RabbitMQ指南之四:路由(Routing)和直连交换机(Direct Exchange)
在上一章中,我们构建了一个简单的日志系统,我们可以把消息广播给很多的消费者.在本章中我们将增加一个特性:我们可以订阅这些信息中的一些信息.例如,我们希望只将error级别的错误存储到硬盘中,同时可以将 ...
- rabbitmq学习(三):rabbitmq之扇形交换机、主题交换机
前言 上篇我们学习了rabbitmq的作用以及直连交换机的代码实现,这篇我们继续看如何用代码实现扇形交换机和主题交换机 一.扇形交换机 1.生产者 /** * 生产者 */ public class ...
- Rabbit的直连交换机direct
直连交换机类型为:direct.加入了路由键routingKey的概念. 就是说 生产者投递消息给指定交换机的指定路由键. 只有绑定了此交换机指定路由键的消息队列才可以收到消息. 生产者: packa ...
- SpringCloudStream学习(二)RabbitMQ中的交换机跟工作模式
知识储备: 交换机: RabbitMQ中有4中交换机,分别是 (FANOUT)扇形交换机: 扇形交换机是最基本的交换机类型,它所能做的事情非常简单---广播消息.扇形交换机会把能接收到的消息全部发 ...
- RabbitMQ 消息确认与公平调度消费者
一.消息确认 为了确保消息一定被消费者处理,rabbitMQ提供了消息确认功能,就是在消费者处理完任务之后,就给服务器一个回馈,服务器就会将该消息删除,如果消费者超时不回馈,那么服务器将就将该消息重新 ...
- 【RabbitMQ学习之二】RabbitMQ四种交换机模式应用
环境 win7 rabbitmq-server-3.7.17 Erlang 22.1 一.概念1.队列队列用于临时存储消息和转发消息.队列类型有两种,即时队列和延时队列. 即时队列:队列中的消息会被立 ...
随机推荐
- mybatis的dao层和service层的编码设计的配置
/** 书写pojo类------>dao接口------>resources下建立同路径的dao.xml------>配置applicationContext.xml文件 **/ ...
- Groovy获取Bean两种方式(奇淫技巧操作)
前言:请各大网友尊重本人原创知识分享,谨记本人博客:南国以南i 背景: 在Java代码中当我们需要一个Bean对象,通常会使用spring中@Autowired注解,用来自动装配对象. 在Groovy ...
- [源码解析] PyTorch 分布式(16) --- 使用异步执行实现批处理 RPC
[源码解析] PyTorch 分布式(16) --- 使用异步执行实现批处理 RPC 目录 [源码解析] PyTorch 分布式(16) --- 使用异步执行实现批处理 RPC 0x00 摘要 0x0 ...
- Python __new__ 方法解释与使用
解释 我们通常把 __init__ 称为构造方法,这是从其他语言借鉴过来的术语. 其实,用于构建实例的是特殊方法 __new__:这是个类方法(使用特殊方式处理,因此不必使用 @classmethod ...
- 【web】php文件包含(利用phpinfo)
Docker搭建复现环境 地址:https://github.com/vulhub/vulhub/tree/master/php/inclusion ps. github单独下载一个文件夹的方法: 安 ...
- Eclipse切换不同版本的jdk
var会在java1.8中报错,安装10版本以上的jdk可以解决问题,但是安装后Eclipse无法正常工作,后来发现是Eclipse没有切换版本,在网上找了好多教程都是切换系统变量,后来我发现可以直接 ...
- 一台电脑如何安装多个版本的JDK及其说明
一.安装多个版本的JDK及其说明 1.准备多个版本的JDK 需要请点击下载 2.安装过程,除了根据需要更改安装路径,其他"傻瓜式"安装("下一步"),各版本都是 ...
- TensorFlow.NET机器学习入门【1】开发环境与类型简介
项目开发环境为Visual Studio 2019 + .Net 5 创建新项目后首先通过Nuget引入相关包: SciSharp.TensorFlow.Redist是Google提供的TensorF ...
- AT2664 [AGC017A] Biscuits 题解
Content 有一个长度为 \(n\) 的数列 \(a\).你希望从中选出一些数,使得这些数的和对 \(2\) 取模后的结果为 \(P\).求方案数. 数据范围:\(1\leqslant n\leq ...
- wordpress页面F12时源码多出的内容在index.php header.php找不到
wordpress页面按F12时源码多出的内容在index.php header.php找不到 比如类似这样的内容: <div style="position:absolute;fil ...