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

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

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

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

现在,我一步步来分析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. JavaScript获取html表单值验证后跳转网页中的关键点

    关键代码: 1.表单部分 <form action="Depart.jsp" name="myform" method="post" ...

  2. day19 进程管理

    day19 进程管理 什么是进程,什么是线程 1.什么是程序 一般情况下,代码,安装包等全部都是应用程序 2.什么是进程 进程(Process)是计算机中的程序关于某数据集合上的一次运行活动,是系统进 ...

  3. vue开发多页面应用 - hash模式和history模式

    我们知道vue可以快速开发web单页应用,而且官方为我们提供了自己的应用脚手架vue-cli,我们只需要下载脚手架,安装依赖后就可以启动vue应用雏形. 这得益与webpack的依赖追踪,各种资源后缀 ...

  4. OC Swift 走马灯效果

    我们常见走马灯样式的功能,下面整理一下 Object-C 与 Swift 的实现代码 OC UILabel *label3 = [[UILabel alloc] initWithFrame:CGRec ...

  5. 转 Android 多线程:手把手教你使用AsyncTask

    转自:https://www.jianshu.com/p/ee1342fcf5e7 前言 多线程的应用在Android开发中是非常常见的,常用方法主要有: 继承Thread类 实现Runnable接口 ...

  6. JavaIO——System对IO的支持、序列化

    1.系统类对IO的支持 在我们学习PriteWriter.PrintStream里面的方法print.println的时候是否观察到其与我们之前一直使用的系统输出很相似呢?其实我们使用的系统输出就是采 ...

  7. jenkins的sonarqube之代码检测的两种方法

    #:sonarqube下载地址,我们安装6.7  高版本已经不支持MySQL和Mariadb(最小3G内存) https://www.sonarqube.org/downloads/ #:安装文档 h ...

  8. OC-封装,继承,多态

    主要内容概括 标号 主题 内容 一 封装 面向对象三大特性;封装的概念/原因/好处/原则 二 *getter和setter setter / getter方法;注意点 三 自定义代码段 如何自定义代码 ...

  9. Quartz使用AutoFac依赖注入问题小结

    theme: channing-cyan highlight: a11y-dark 背景 最近在做一个需求,就是在Job中捕捉异常,然后通过邮件或者消息的方式推送给指定人员,在需求实现的过程中遇到的一 ...

  10. pipeline 结构设计

    目录 一.pipeline步骤 二.案例 pipeline详解 只生成一次制品 不同环境部署 系统集成测试 指定版本部署 一.pipeline步骤 当团队开始设计第一个pipeline时,该如何下手呢 ...