1.前言

  RabbleMQ这种消息中间件,主要的功能是使用异步操作,来达到解耦的目的,比如,有系统a和系统b,

系统a通过消息中间件通知系统b来做业务,那么系统a只需要把要做的事情【也就是消息】发给消息中间件后,

消息中间件就会把消息转发给系统b,系统a不需要关心系统b是怎么完成业务的,也不需要关心业务完成的结果,

这是就是异步操作。

  如果系统a希望获得系统b的处理结果,那么系统a使用消息中间件发送消息后需要原地等待,做阻塞操作,但是

等待时长不能超过最大超时时间,可设置RabbleMQ自定义超时时间,这样还不如直接调用该业务呢,何必再加个消息消息中间件通知他来做?

  所以,一般不会做这样的同步阻塞操作,违背了消息中间件的开发初衷【提高吞吐量和系统业务的响应速度】,虽然

不影响解耦度,但是这样的操作使得消息中间件变得不伦不类了。

  那么要问了,使用RabbleMQ消息中间件的具体好处是什么?

  1.   第一,解耦,是非常明显的好处,通过消息中间件,系统a通知系统b做什么业务,不需要关心系统b是怎么实现业务的。
  2.   第二,异步,非必要的业务,不需要特别关心结果的业务,可以写入消息中间件以异步方式操作,横向编程,加快响应速度。
  3.   第三,削峰,并发量大的时候直接将所有数据怼到数据库,数据库会异常的,使用消息队列的特性,所有的操作会进行排队,
  4.     消息也不会丢失,当消息被消费后才会销毁,比如秒杀系统就是这样实现的。

  那么,虽然同步阻塞操作偶很大的闭端,那么到底该怎么操作?

  1. 这篇随笔 随笔 https://www.cnblogs.com/c2g5201314/p/13156932.html 为基础 ,修改部分代码实现演示

2.操作

(1)修改消息生产者的消息生产类的 rabbitTemplate 模板发送消息方法 ,同步则使用 convertSendAndReceive(),参数与异步非阻塞操作的一样。

源码

  1. package com.example.rabbitmqproducer1004.rabbitmqFactory;
  2.  
  3. import com.example.rabbitmqproducer1004.config.RabbitmqConfig;
  4. import org.slf4j.Logger;
  5. import org.slf4j.LoggerFactory;
  6. import org.springframework.amqp.rabbit.connection.CorrelationData;
  7. import org.springframework.amqp.rabbit.core.RabbitTemplate;
  8. import org.springframework.beans.factory.annotation.Autowired;
  9. import org.springframework.context.annotation.Bean;
  10. import org.springframework.stereotype.Component;
  11.  
  12. import java.util.UUID;
  13.  
  14. /**
  15. * 消息生产类
  16. */
  17. @Component
  18. //实现接口
  19. public class SendMessage {
  20.  
  21. //需要设置回调方法,获取消费结果才需要实现 RabbitTemplate.ConfirmCallback 接口,
  22. //public class SendMessage implements RabbitTemplate.ConfirmCallback {
  23. Logger logger = LoggerFactory.getLogger(this.getClass());
  24.  
  25. //======================================================================
  26. // /**
  27. // * 方法一:设置回调方法,获取消费结果,
  28. // * <p>
  29. // * 缺点是:必须手动配置RabbitTemplate模板 ,代码量大
  30. // */
  31. //
  32. // //存储 rabbitmq模板的临时变量
  33. // private final RabbitTemplate rabbitTemplate;
  34. //
  35. // /**
  36. // * 构造注入rabbitmq模板,这样可以设置回调方法,获取消费结果,但是必须手动配置RabbitTemplate模板
  37. // */
  38. // @Autowired
  39. // public SendMessage(RabbitTemplate rabbitTemplate) {
  40. // this.rabbitTemplate = rabbitTemplate;
  41. // //设置确认回调的方法,参数类型为ConfirmCallback
  42. // this.rabbitTemplate.setConfirmCallback(this);
  43. // }
  44. //
  45. // /**
  46. // * 回调方法,获取消费结果
  47. // *
  48. // * @param correlationData 关联数据
  49. // * @param b 消息是否被消费成功,成功为true ,失败为false
  50. // * @param s 原因 ,消费成功则返回null,否则返回失败原因
  51. // */
  52. // @Override
  53. // public void confirm(CorrelationData correlationData, boolean b, String s) {
  54. // logger.warn("回调的连接数据:" + correlationData);
  55. // if (correlationData != null) {
  56. // //CorrelationData [id=1bcab025-2b4c-4f74-a22d-41007e30f551]
  57. // logger.warn("获取correlationData的id值:" + correlationData.getId());
  58. // }
  59. // //1bcab025-2b4c-4f74-a22d-41007e30f551
  60. // if (b) {
  61. // logger.warn("回调结果:消息消费成功");
  62. // } else {
  63. // logger.warn("回调结果:失败。原因:" + s);
  64. // }
  65. // }
  66.  
  67. //========================================================================
  68. // /**
  69. // * 方法二 :不需要获取获取消费结果,只需要发送即可
  70. // *
  71. // * 优点:自动装配,代码量少
  72. // */
  73. @Autowired
  74. private RabbitTemplate rabbitTemplate;
  75.  
  76. //========================================================================
  77.  
  78. /**
  79. * 发送消息
  80. * <p>
  81. * 参数是消息内容
  82. */
  83. public void send(String message) {
  84. logger.warn("发送消息,内容:" + message);
  85. /**
  86. * 方法一:异步操作,不等待消费者端返回处理结果,设置在回调操作的关联数据,用于识别是哪一条消息和确认是否执行成功
  87. */
  88. //// 实例关联数据对象,使用UUID随机数 作为 回调id
  89. // CorrelationData correlationData = new CorrelationData(UUID.randomUUID().toString());
  90. //// 发送消息 ,参数分别是 : 指定的交换机名字 、指定的路由关键字、消息字符串、关联数据
  91. // rabbitTemplate.convertAndSend(RabbitmqConfig.EXCHANG_1,RabbitmqConfig.ROUTINGKEY_1,message,correlationData);
  92. /**
  93. * 方法二:异步操作,不等待消费者端返回处理结果,且在消息回调操作的关联数据为null,如果不做回调操作,则建议这样使用
  94. */
  95. // //发送消息 ,参数分别是 : 指定的交换机名字 、指定的路由关键字、消息字符串
  96. // rabbitTemplate.convertAndSend(RabbitmqConfig.EXCHANG_1, RabbitmqConfig.ROUTINGKEY_1, message);
  97. /**
  98. * 方法三:同步操作,等待消费者端返回处理结果
  99. */
  100. Object dd = rabbitTemplate.convertSendAndReceive(RabbitmqConfig.EXCHANG_1, RabbitmqConfig.ROUTINGKEY_1, message);
  101. logger.warn("你大爷的,有么有延迟?是不是同步阻塞?");
  102. logger.warn("结果是什么???==" + dd);
  103.  
  104. }
  105. }

(2)修改消息消费者的监听方法 ,可设置任意类型返回值,但是在生产者端需要解析,我这里使用 字符串,休眠3秒

源码

  1. package com.example.rabbitmqconsumer1002.rabbitmqListener;
  2.  
  3. import com.example.rabbitmqconsumer1002.config.RabbitConfig;
  4. import org.slf4j.Logger;
  5. import org.slf4j.LoggerFactory;
  6. import org.springframework.amqp.rabbit.annotation.RabbitHandler;
  7. import org.springframework.amqp.rabbit.annotation.RabbitListener;
  8. import org.springframework.stereotype.Component;
  9.  
  10. /**
  11. * 消息监听类--发短信
  12. */
  13. //注册bean
  14. @Component
  15. //设置需要监听的消息队列
  16. @RabbitListener(queues = RabbitConfig.QUEUE_1)
  17. public class SendMessageListener {
  18. Logger logger = LoggerFactory.getLogger(getClass());
  19.  
  20. //消息事件处理--有返回结果
  21. @RabbitHandler
  22. public String sendMessage(String msg) throws InterruptedException {
  23. logger.warn("我是端口1002的消费者,收到信息:" + msg);
  24. logger.warn("休眠3秒");
  25. Thread.sleep(3000);
  26. logger.warn("休眠结束");
  27. return "发送成功,是同步阻塞的么?";
  28. }
  29. }

(3)启动工程

访问网址     http://localhost:1004/mq?msg=你大爷,帮我发短信3999

查看消费者端控制台打印

查看生产者端控制台打印

生产者端等待了3秒后才收到结果

3.如果换成10秒会怎么样?

修改消息消费者的监听方法

启动工程后 访问 网址  http://localhost:1004/mq?msg=你大爷,帮我发短信3999

查看消费者端控制台打印

查看生产者端控制台打印

打印控制台源码

  1. 2020-06-19 02:40:17.100 WARN http-nio-1004-exec-4 | com.example.rabbitmqproducer1004.rabbitmqFactory.SendMessage | 发送消息,内容:你大爷,帮我发短信3999
  2. 2020-06-19 02:40:17.111 INFO http-nio-1004-exec-4 | org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler | Initializing ExecutorService
  3. 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]
  4. 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
  5. 2020-06-19 02:40:22.137 WARN http-nio-1004-exec-4 | com.example.rabbitmqproducer1004.rabbitmqFactory.SendMessage | 你大爷的,有么有延迟?是不是同步阻塞?
  6. 2020-06-19 02:40:22.137 WARN http-nio-1004-exec-4 | com.example.rabbitmqproducer1004.rabbitmqFactory.SendMessage | 结果是什么???==null
  7. 2020-06-19 02:40:27.158 WARN pool-1-thread-4 | org.springframework.amqp.rabbit.core.RabbitTemplate | Reply received after timeout for 1
  8. 2020-06-19 02:40:27.163 WARN pool-1-thread-4 | org.springframework.amqp.rabbit.listener.ConditionalRejectingErrorHandler | Execution of Rabbit message listener failed.
  9. org.springframework.amqp.rabbit.listener.exception.ListenerExecutionFailedException: Listener threw exception
  10. at org.springframework.amqp.rabbit.listener.AbstractMessageListenerContainer.wrapToListenerExecutionFailedExceptionIfNeeded(AbstractMessageListenerContainer.java:1651) ~[spring-rabbit-2.1.7.RELEASE.jar:2.1.7.RELEASE]
  11. at org.springframework.amqp.rabbit.listener.AbstractMessageListenerContainer.doInvokeListener(AbstractMessageListenerContainer.java:1555) ~[spring-rabbit-2.1.7.RELEASE.jar:2.1.7.RELEASE]
  12. at org.springframework.amqp.rabbit.listener.AbstractMessageListenerContainer.actualInvokeListener(AbstractMessageListenerContainer.java:1478) ~[spring-rabbit-2.1.7.RELEASE.jar:2.1.7.RELEASE]
  13. at org.springframework.amqp.rabbit.listener.AbstractMessageListenerContainer.invokeListener(AbstractMessageListenerContainer.java:1466) ~[spring-rabbit-2.1.7.RELEASE.jar:2.1.7.RELEASE]
  14. at org.springframework.amqp.rabbit.listener.AbstractMessageListenerContainer.doExecuteListener(AbstractMessageListenerContainer.java:1461) ~[spring-rabbit-2.1.7.RELEASE.jar:2.1.7.RELEASE]
  15. at org.springframework.amqp.rabbit.listener.AbstractMessageListenerContainer.executeListener(AbstractMessageListenerContainer.java:1410) ~[spring-rabbit-2.1.7.RELEASE.jar:2.1.7.RELEASE]
  16. at org.springframework.amqp.rabbit.listener.DirectMessageListenerContainer$SimpleConsumer.callExecuteListener(DirectMessageListenerContainer.java:996) [spring-rabbit-2.1.7.RELEASE.jar:2.1.7.RELEASE]
  17. at org.springframework.amqp.rabbit.listener.DirectMessageListenerContainer$SimpleConsumer.handleDelivery(DirectMessageListenerContainer.java:956) [spring-rabbit-2.1.7.RELEASE.jar:2.1.7.RELEASE]
  18. at com.rabbitmq.client.impl.ConsumerDispatcher$5.run(ConsumerDispatcher.java:149) [amqp-client-5.4.3.jar:5.4.3]
  19. at com.rabbitmq.client.impl.ConsumerWorkService$WorkPoolRunnable.run(ConsumerWorkService.java:104) [amqp-client-5.4.3.jar:5.4.3]
  20. at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149) [na:1.8.0_221]
  21. at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624) [na:1.8.0_221]
  22. at java.lang.Thread.run(Thread.java:748) [na:1.8.0_221]
  23. Caused by: org.springframework.amqp.AmqpRejectAndDontRequeueException: Reply received after timeout
  24. at org.springframework.amqp.rabbit.core.RabbitTemplate.onMessage(RabbitTemplate.java:2535) ~[spring-rabbit-2.1.7.RELEASE.jar:2.1.7.RELEASE]
  25. at org.springframework.amqp.rabbit.listener.DirectReplyToMessageListenerContainer.lambda$setMessageListener$1(DirectReplyToMessageListenerContainer.java:115) ~[spring-rabbit-2.1.7.RELEASE.jar:2.1.7.RELEASE]
  26. at org.springframework.amqp.rabbit.listener.AbstractMessageListenerContainer.doInvokeListener(AbstractMessageListenerContainer.java:1552) ~[spring-rabbit-2.1.7.RELEASE.jar:2.1.7.RELEASE]
  27. ... 11 common frames omitted
  28. 2020-06-19 02:40:27.164 ERROR pool-1-thread-4 | org.springframework.amqp.rabbit.listener.DirectReplyToMessageListenerContainer | Failed to invoke listener
  29. org.springframework.amqp.rabbit.listener.exception.ListenerExecutionFailedException: Listener threw exception
  30. at org.springframework.amqp.rabbit.listener.AbstractMessageListenerContainer.wrapToListenerExecutionFailedExceptionIfNeeded(AbstractMessageListenerContainer.java:1651) ~[spring-rabbit-2.1.7.RELEASE.jar:2.1.7.RELEASE]
  31. at org.springframework.amqp.rabbit.listener.AbstractMessageListenerContainer.doInvokeListener(AbstractMessageListenerContainer.java:1555) ~[spring-rabbit-2.1.7.RELEASE.jar:2.1.7.RELEASE]
  32. at org.springframework.amqp.rabbit.listener.AbstractMessageListenerContainer.actualInvokeListener(AbstractMessageListenerContainer.java:1478) ~[spring-rabbit-2.1.7.RELEASE.jar:2.1.7.RELEASE]
  33. at org.springframework.amqp.rabbit.listener.AbstractMessageListenerContainer.invokeListener(AbstractMessageListenerContainer.java:1466) ~[spring-rabbit-2.1.7.RELEASE.jar:2.1.7.RELEASE]
  34. at org.springframework.amqp.rabbit.listener.AbstractMessageListenerContainer.doExecuteListener(AbstractMessageListenerContainer.java:1461) ~[spring-rabbit-2.1.7.RELEASE.jar:2.1.7.RELEASE]
  35. at org.springframework.amqp.rabbit.listener.AbstractMessageListenerContainer.executeListener(AbstractMessageListenerContainer.java:1410) ~[spring-rabbit-2.1.7.RELEASE.jar:2.1.7.RELEASE]
  36. at org.springframework.amqp.rabbit.listener.DirectMessageListenerContainer$SimpleConsumer.callExecuteListener(DirectMessageListenerContainer.java:996) [spring-rabbit-2.1.7.RELEASE.jar:2.1.7.RELEASE]
  37. at org.springframework.amqp.rabbit.listener.DirectMessageListenerContainer$SimpleConsumer.handleDelivery(DirectMessageListenerContainer.java:956) [spring-rabbit-2.1.7.RELEASE.jar:2.1.7.RELEASE]
  38. at com.rabbitmq.client.impl.ConsumerDispatcher$5.run(ConsumerDispatcher.java:149) [amqp-client-5.4.3.jar:5.4.3]
  39. at com.rabbitmq.client.impl.ConsumerWorkService$WorkPoolRunnable.run(ConsumerWorkService.java:104) [amqp-client-5.4.3.jar:5.4.3]
  40. at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149) [na:1.8.0_221]
  41. at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624) [na:1.8.0_221]
  42. at java.lang.Thread.run(Thread.java:748) [na:1.8.0_221]
  43. 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. mybatis的dao层和service层的编码设计的配置

    /** 书写pojo类------>dao接口------>resources下建立同路径的dao.xml------>配置applicationContext.xml文件 **/ ...

  2. Groovy获取Bean两种方式(奇淫技巧操作)

    前言:请各大网友尊重本人原创知识分享,谨记本人博客:南国以南i 背景: 在Java代码中当我们需要一个Bean对象,通常会使用spring中@Autowired注解,用来自动装配对象. 在Groovy ...

  3. [源码解析] PyTorch 分布式(16) --- 使用异步执行实现批处理 RPC

    [源码解析] PyTorch 分布式(16) --- 使用异步执行实现批处理 RPC 目录 [源码解析] PyTorch 分布式(16) --- 使用异步执行实现批处理 RPC 0x00 摘要 0x0 ...

  4. Python __new__ 方法解释与使用

    解释 我们通常把 __init__ 称为构造方法,这是从其他语言借鉴过来的术语. 其实,用于构建实例的是特殊方法 __new__:这是个类方法(使用特殊方式处理,因此不必使用 @classmethod ...

  5. 【web】php文件包含(利用phpinfo)

    Docker搭建复现环境 地址:https://github.com/vulhub/vulhub/tree/master/php/inclusion ps. github单独下载一个文件夹的方法: 安 ...

  6. Eclipse切换不同版本的jdk

    var会在java1.8中报错,安装10版本以上的jdk可以解决问题,但是安装后Eclipse无法正常工作,后来发现是Eclipse没有切换版本,在网上找了好多教程都是切换系统变量,后来我发现可以直接 ...

  7. 一台电脑如何安装多个版本的JDK及其说明

    一.安装多个版本的JDK及其说明 1.准备多个版本的JDK 需要请点击下载 2.安装过程,除了根据需要更改安装路径,其他"傻瓜式"安装("下一步"),各版本都是 ...

  8. TensorFlow.NET机器学习入门【1】开发环境与类型简介

    项目开发环境为Visual Studio 2019 + .Net 5 创建新项目后首先通过Nuget引入相关包: SciSharp.TensorFlow.Redist是Google提供的TensorF ...

  9. AT2664 [AGC017A] Biscuits 题解

    Content 有一个长度为 \(n\) 的数列 \(a\).你希望从中选出一些数,使得这些数的和对 \(2\) 取模后的结果为 \(P\).求方案数. 数据范围:\(1\leqslant n\leq ...

  10. wordpress页面F12时源码多出的内容在index.php header.php找不到

    wordpress页面按F12时源码多出的内容在index.php header.php找不到 比如类似这样的内容: <div style="position:absolute;fil ...