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 --- 直连交换机 【 同步操作,等到消费者处理完后返回处理结果 】的更多相关文章

  1. RabbitMQ --- 直连交换机 【 有回调方法,获取消费结果 】

    1.前言 上一随笔详细记录了直连交换机的方法,发送的消息是异步的,如果消息未被消费者消费,那么可以一直存在消息队列中. 那么有没有办法做一个回调,当消息被消费后,被通知消息成功被消费者消费啦? 答案是 ...

  2. RabbitMQ --- 直连交换机 【 无回调方法,不能获取消费结果 】

    1.前言 消息队列除了kafka 外,还有许多种,比如RabbitMQ .ActiveMQ.ZeroMQ.JMQ等. 老牌的ActiveMQ ,底层使用Java写的,资源消耗大,速度也慢,但是适合 J ...

  3. rabbitmq学习(二):rabbitmq(消息队列)的作用以及rabbitmq之直连交换机

    前言 上篇介绍了AMQP的基本概念,组成及其与rabbitmq的关系.了解了这些东西后,下面我们开始学习rabbitmq(消息队列)的作用以及用java代码和rabbitmq通讯进行消息发布和接收.因 ...

  4. RabbitMQ指南之四:路由(Routing)和直连交换机(Direct Exchange)

    在上一章中,我们构建了一个简单的日志系统,我们可以把消息广播给很多的消费者.在本章中我们将增加一个特性:我们可以订阅这些信息中的一些信息.例如,我们希望只将error级别的错误存储到硬盘中,同时可以将 ...

  5. rabbitmq学习(三):rabbitmq之扇形交换机、主题交换机

    前言 上篇我们学习了rabbitmq的作用以及直连交换机的代码实现,这篇我们继续看如何用代码实现扇形交换机和主题交换机 一.扇形交换机 1.生产者 /** * 生产者 */ public class ...

  6. Rabbit的直连交换机direct

    直连交换机类型为:direct.加入了路由键routingKey的概念. 就是说 生产者投递消息给指定交换机的指定路由键. 只有绑定了此交换机指定路由键的消息队列才可以收到消息. 生产者: packa ...

  7. SpringCloudStream学习(二)RabbitMQ中的交换机跟工作模式

    知识储备: 交换机: ​ RabbitMQ中有4中交换机,分别是 (FANOUT)扇形交换机: 扇形交换机是最基本的交换机类型,它所能做的事情非常简单---广播消息.扇形交换机会把能接收到的消息全部发 ...

  8. RabbitMQ 消息确认与公平调度消费者

    一.消息确认 为了确保消息一定被消费者处理,rabbitMQ提供了消息确认功能,就是在消费者处理完任务之后,就给服务器一个回馈,服务器就会将该消息删除,如果消费者超时不回馈,那么服务器将就将该消息重新 ...

  9. 【RabbitMQ学习之二】RabbitMQ四种交换机模式应用

    环境 win7 rabbitmq-server-3.7.17 Erlang 22.1 一.概念1.队列队列用于临时存储消息和转发消息.队列类型有两种,即时队列和延时队列. 即时队列:队列中的消息会被立 ...

随机推荐

  1. 记一次单机Nginx调优,效果立竿见影

    一.物理环境 1.系统是Centos 8,系统配置 2核4G,8M带宽,一台很轻的应用服务器. 2.站点部署情况.但站点部署两个实例,占用两个端口,使用nginx 负载转发到这两个web站点.  二. ...

  2. Jenkins 关闭和重启的实现方式

    关闭jenkins 只需要在访问jenkins服务器的网址url地址后加上exit.例如我jenkins的地址http://localhost:8080/ , 那么我只需要在浏览器地址栏上敲下 htt ...

  3. TSN(Time-Sensitive Networking)协议导读

    前言 上一个主题我们介绍了TSN的发展历史和协议族现状,它为建立统一的确定性网络传输基础设施提供了有力保障. TSN是一套协议标准,以保证确定性信息在标准以太网的不同场景下的顺利传输.TSN协议族本身 ...

  4. php常用的数组排序函数

    sort() 函数用于对数组单元从低到高进行排序.rsort() 函数用于对数组单元从高到低进行排序.asort() 函数用于对数组单元从低到高进行排序并保持索引关系.arsort() 函数用于对数组 ...

  5. how2heap学习(一)

    接下来的时间会通过how2heap学习堆的知识,这个系列可能会更新很多篇,因为每天学习到的东西要保证吸收消化,所以一天不会学习很多,但是又想每天记录一下.所以开个系列. first_fit 此题的源码 ...

  6. ORALE 误删表 Flashback 恢复表

    昨天因为种种原因误删了很多表(160多个),一下炸了锅. 我知道影响很大,第一时间想到使用 Flashback 恢复过来.Flashback 听过很久,但是没真正上手过,恢复之后发现使用起来也很简单. ...

  7. Excel.CurrentWorkbook数据源(Power Query 之 M 语言)

    数据源: 任意超级表 目标: 将超级表中的数据加载到Power Query编辑器中 操作过程: 选取超级表中任意单元格(选取普通表时会自动增加插入超级表的步骤)>数据>来自表格/区域 M公 ...

  8. 第四届“传智杯”全国大学生IT技能大赛题解

    目录 A B C D E F G 今年题目难度普遍偏低.只有 D,F 还好. A 按题目给的公式计算即可.注意应在最后的答案中去掉小数部分. B 按照题意模拟即可.注意答案要与 \(0\) 取 \(\ ...

  9. mapbox获取各种经纬度

    点击地图即可获取经纬度,也可以手动输入经纬度来换算 在线查看运行效果 实现方法 mapbox中通过地图点击事件来获取到坐标,然后转换为其他的坐标系并输出在屏幕上即可 获取坐标 方法很简单,给地图添加一 ...

  10. CF1437A Marketing Scheme 题解

    Content 有 \(t\) 组询问,每组询问给定两个整数 \(l,r\),问是否存在一个 \(a\),使得 \(\forall x\in[l,r]\),都有 \(x\mod a\geqslant\ ...