使用消息队列,必须要考虑的问题就是生产者消息发送失败和消费者消息处理失败,这两种情况怎么处理.

生产者发送消息,成功,则确认消息发送成功;失败,则返回消息发送失败信息,再做处理.

消费者处理消息,成功,则消息队列自动删除消息;失败,则消息重新返回队列,等待处理.

对于消费者处理失败的情况,如果仅仅只是让消息重新返回队列,等待处理,那么久有可能会出现很多消息一直无法处理的情况;因此,是否让消息返回队列,还有待商榷.

现在,我一步步来分析RabbitMQ的消息确认(这次的代码同样是在上次的代码基础上做修改,修改后的代码会上传到百度云,后面会有链接地址):

生产者端,修改spring-config.xml,rabbitTemplate增加消息确认和返回错误信息的监听器:

    <rabbit:template id="rabbitTemplate" connection-factory="connectionFactory"
confirm-callback="confirmCallBackListener"
return-callback="returnCallBackListener"
mandatory="true"
/>

消息确认监听器confirmCallBackListener:

@Service("confirmCallBackListener")
public class ConfirmCallBackListener implements ConfirmCallback { @Override
public void confirm(CorrelationData arg0, boolean arg1)
{
System.out.println("确认消息完成..."); // 只确认生产者消息发送成功,消费者是否处理成功不做保证
}
}

消息发送失败返回监听器returnCallBackListener:

@Service("returnCallBackListener")
public class ReturnCallBackListener implements ReturnCallback { @Override
public void returnedMessage(Message message, int replyCode, String replyText, String exchange, String routingKey)
{
System.out.println("消息返回处理中...");
}
}

消费者端,修改spring-config.xml,设置ack方式为手动,增加对应队列的监听器。

<!-- ========================================RabbitMQ========================================= -->
<!-- 连接工厂 -->
<rabbit:connection-factory id="connectionFactory" host="localhost" publisher-confirms="true" virtual-host="/" username="guest" password="guest" />
<!-- 监听器 设置acknowledge="manual" 则开启ack机制 -->
<rabbit:listener-container connection-factory="connectionFactory" acknowledge="manual">
<!-- queues是队列名称,可填多个,用逗号隔开, method是ref指定的Bean调用Invoke方法执行的方法名称 -->
<rabbit:listener queues="test" ref="receiveConfirmTestListener" />
</rabbit:listener-container>
<!-- 队列声明 -->
<rabbit:queue name="test" durable="true" />
<!-- 测试监听处理器 -->
<bean id="receiveConfirmTestListener" class="com.aitongyi.customer.ReceiveConfirmTestListener" />

配置的receiveConfirmTestListener没有指定方法,是因为它实现了接口ChannelAwareMessageListener,代码如下:

public class ReceiveConfirmTestListener implements ChannelAwareMessageListener {

    @Override
public void onMessage(Message message, Channel channel) throws Exception
{
try
{
System.out.println("consumer--:" + message.getMessageProperties() + ":" + new String(message.getBody())); // deliveryTag是消息传送的次数,我这里是为了让消息队列的第一个消息到达的时候抛出异常,处理异常让消息重新回到队列,然后再次抛出异常,处理异常拒绝让消息重回队列
if (message.getMessageProperties().getDeliveryTag() == 1 || message.getMessageProperties().getDeliveryTag() == 2)
{
throw new Exception();
} channel.basicAck(message.getMessageProperties().getDeliveryTag(), false); // false只确认当前一个消息收到,true确认所有consumer获得的消息
}
catch (Exception e)
{
e.printStackTrace(); if (message.getMessageProperties().getRedelivered())
{
System.out.println("消息已重复处理失败,拒绝再次接收...");
channel.basicReject(message.getMessageProperties().getDeliveryTag(), true); // 拒绝消息
}
else
{
System.out.println("消息即将再次返回队列处理...");
channel.basicNack(message.getMessageProperties().getDeliveryTag(), false, true); // requeue为是否重新回到队列
}
}
}
}

启动项目,发送消息,查看测试结果:

打开发送消息页面,点击发送:

在控制台查看结果(抛出的异常信息我没有粘贴出来):

2017-05-17 14:56:21 532 [INFO] c.a.p.c.RabbitController - rabbitmq--收到待发送消息: type[test]-msg[hello world test rabbit!]
2017-05-17 14:56:21 819 [INFO] c.a.p.s.RabbitServiceImpl - rabbitmq--发送消息完成: routingKey[test]-msg[hello world test rabbit!]
consumer--:MessageProperties [headers={spring_return_correlation=776f57cd-1e22-44de-bc11-eb82bc043562}, timestamp=null, messageId=null, userId=null, appId=null, clusterId=null, type=null, correlationId=null, replyTo=null, contentType=text/plain, contentEncoding=UTF-8, contentLength=0, deliveryMode=PERSISTENT, expiration=null, priority=0, redelivered=false, receivedExchange=, receivedRoutingKey=test, deliveryTag=1, messageCount=0]:hello world test rabbit!
消息即将再次返回队列处理...
consumer--:MessageProperties [headers={spring_return_correlation=776f57cd-1e22-44de-bc11-eb82bc043562}, timestamp=null, messageId=null, userId=null, appId=null, clusterId=null, type=null, correlationId=null, replyTo=null, contentType=text/plain, contentEncoding=UTF-8, contentLength=0, deliveryMode=PERSISTENT, expiration=null, priority=0, redelivered=true, receivedExchange=, receivedRoutingKey=test, deliveryTag=2, messageCount=0]:hello world test rabbit!
确认消息完成...
消息已重复处理失败,拒绝再次接收...
consumer--:MessageProperties [headers={spring_return_correlation=776f57cd-1e22-44de-bc11-eb82bc043562}, timestamp=null, messageId=null, userId=null, appId=null, clusterId=null, type=null, correlationId=null, replyTo=null, contentType=text/plain, contentEncoding=UTF-8, contentLength=0, deliveryMode=PERSISTENT, expiration=null, priority=0, redelivered=true, receivedExchange=, receivedRoutingKey=test, deliveryTag=3, messageCount=0]:hello world test rabbit!

上面代码的处理方式是消费者接收到消息后处理消息,第一次处理失败,让消息重回队列,如果重回队列后仍然失败,则拒绝接收消息。这个做法只是一个参考,具体如何做,要根据业务来做改变。

消息发送失败情形展示:将发送的消息队列routingKey,也就是配置的队列名改成一个在RabbitMQ Server不存在的。然后打开发送消息页面,点击发送,结果如下:

2017-05-17 15:12:54 437 [INFO] c.a.p.c.RabbitController - rabbitmq--收到待发送消息: type[test]-msg[hello world test rabbit!]
2017-05-17 15:12:54 439 [INFO] c.a.p.s.RabbitServiceImpl - rabbitmq--发送消息完成: routingKey[test]-msg[hello world test rabbit!]
消息返回处理中...
确认消息完成...

至于为什么还是会打印"确认消息完成...",有兴趣的可以看下源码。

源代码已上传至百度云网盘,欢迎下载阅读,地址:http://pan.baidu.com/s/1eSL2w8M

RabbitMQ学习笔记六:RabbitMQ之消息确认的更多相关文章

  1. rabbitMQ学习笔记(六) topic类型消息。

    上一节中使用了消息路由,消费者可以选择性的接收消息. 但是这样还是不够灵活. 比如某个消费者要订阅娱乐新闻消息 . 包括新浪.网易.腾讯的娱乐新闻.那么消费者就需要绑定三次,分别绑定这三个网站的消息类 ...

  2. rabbitMQ学习笔记(四) 发布/订阅消息

    前面都是一条消息只会被一个消费者处理. 如果要每个消费者都处理同一个消息,rabbitMq也提供了相应的方法. 在以前的程序中,不管是生产者端还是消费者端都必须知道一个指定的QueueName才能发送 ...

  3. [RabbitMQ学习笔记] - 初识RabbitMQ

    RabbitMQ是一个由erlang开发的AMQP的开源实现. 核心概念 Message 消息,消息是不具名的,它由消息头和消息体组成,消息体是不透明的,而消息头则由 一系列的可选属性组成,这些属性包 ...

  4. RabbitMQ学习笔记五:RabbitMQ之优先级消息队列

    RabbitMQ优先级队列注意点: 1.只有当消费者不足,不能及时进行消费的情况下,优先级队列才会生效 2.RabbitMQ3.5以后才支持优先级队列 代码在博客:RabbitMQ学习笔记三:Java ...

  5. 官网英文版学习——RabbitMQ学习笔记(一)认识RabbitMQ

    鉴于目前中文的RabbitMQ教程很缺,本博主虽然买了一本rabbitMQ的书,遗憾的是该书的代码用的不是java语言,看起来也有些不爽,且网友们不同人学习所写不同,本博主看的有些地方不太理想,为此本 ...

  6. RabbitMQ学习笔记(五) Topic

    更多的问题 Direct Exchange帮助我们解决了分类发布与订阅消息的问题,但是Direct Exchange的问题是,它所使用的routingKey是一个简单字符串,这决定了它只能按照一个条件 ...

  7. RabbitMQ学习笔记1-hello world

    安装过程略过,一搜一大把. rabbitmq管理控制台:http://localhost:15672/   默认账户:guest/guest RabbitMQ默认监听端口:5672 JAVA API地 ...

  8. 官网英文版学习——RabbitMQ学习笔记(十)RabbitMQ集群

    在第二节我们进行了RabbitMQ的安装,现在我们就RabbitMQ进行集群的搭建进行学习,参考官网地址是:http://www.rabbitmq.com/clustering.html 首先我们来看 ...

  9. RabbitMQ系列(四)RabbitMQ事务和Confirm发送方消息确认——深入解读

    RabbitMQ事务和Confirm发送方消息确认--深入解读 RabbitMQ系列文章 RabbitMQ在Ubuntu上的环境搭建 深入了解RabbitMQ工作原理及简单使用 RabbitMQ交换器 ...

随机推荐

  1. Zookeeper【概述、安装、原理、使用】

    目录 第1章 Zookeeper入门 1.1 概述 1.2 特点 1.3 数据结构 1.4应用场景 第2章 Zookeep安装 2.1 下载地址 2.2 本地模式安装 1. 安装前准备 2. 配置修改 ...

  2. 轻松理解webpack热更新原理

    一.前言 - webpack热更新 Hot Module Replacement,简称HMR,无需完全刷新整个页面的同时,更新模块.HMR的好处,在日常开发工作中体会颇深:节省宝贵的开发时间.提升开发 ...

  3. Android权限级别(protectionLevel)

    通常情况下,对于需要付费的操作以及可能涉及到用户隐私的操作,我们都会格外敏感. 出于上述考虑以及更多的安全考虑,Android中对一些访问进行了限制,如网络访问(需付费)以及获取联系人(涉及隐私)等. ...

  4. Dubbo管控平台

    2019年初,官方发布了Dubbo管理控制台0.1版本.结构上采取了前后端分离的方式,前端使用Vue和Vuetify分别作为Javascript框架和UI框架,后端采用Spring Boot框架 一. ...

  5. Maven错误收集

    Eclipse 创建项目时报错 Could not resolve archetype org.apache.maven.archetypes:maven-archetype-quickstart:1 ...

  6. 编译工具grdle部署

    目录 一.简介 二.部署 三.测试 一.简介 Gradle 是以 Groovy 语言为基础,面向Java应用为主.基于DSL(领域特定语言)语法的自动化构建工具.在github上,gradle项目很多 ...

  7. 千兆车载以太网TSN网络测试?TSN Box为您焕新

    TSN概述 在汽车领域内,近几年车内网络通讯方式的变革诉求,期望能够有更高的数据传输速率,以及保证实时性的通讯方式引入.例如对于自动驾驶而言,传统的CAN总线已经远远不能满足其对通讯的要求,而基于以太 ...

  8. 按照eslint的规则格式化代码

    1.下载eslint. 2.首选项->设置,然后搜索eslint,点击在setting.json中设置.设置内容如下: "editor.codeActionsOnSave": ...

  9. Java synchonized 同步

    Java语言的关键字,当它用来修饰一个方法或者一个代码块的时候,能够保证在同一时刻最多只有一个线程执行该段代码. 一.当两个并发线程访问同一个对象object中的这个synchronized(this ...

  10. NOAA数据下载方法

    NOAA OneStop https://data.noaa.gov/onestop/about NOAA 数据搜索平台,在一个地方同时搜索NOAA的 Geophysical, oceans, coa ...