RabbitMQ学习笔记六:RabbitMQ之消息确认
使用消息队列,必须要考虑的问题就是生产者消息发送失败和消费者消息处理失败,这两种情况怎么处理.
生产者发送消息,成功,则确认消息发送成功;失败,则返回消息发送失败信息,再做处理.
消费者处理消息,成功,则消息队列自动删除消息;失败,则消息重新返回队列,等待处理.
对于消费者处理失败的情况,如果仅仅只是让消息重新返回队列,等待处理,那么久有可能会出现很多消息一直无法处理的情况;因此,是否让消息返回队列,还有待商榷.
现在,我一步步来分析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之消息确认的更多相关文章
- rabbitMQ学习笔记(六) topic类型消息。
上一节中使用了消息路由,消费者可以选择性的接收消息. 但是这样还是不够灵活. 比如某个消费者要订阅娱乐新闻消息 . 包括新浪.网易.腾讯的娱乐新闻.那么消费者就需要绑定三次,分别绑定这三个网站的消息类 ...
- rabbitMQ学习笔记(四) 发布/订阅消息
前面都是一条消息只会被一个消费者处理. 如果要每个消费者都处理同一个消息,rabbitMq也提供了相应的方法. 在以前的程序中,不管是生产者端还是消费者端都必须知道一个指定的QueueName才能发送 ...
- [RabbitMQ学习笔记] - 初识RabbitMQ
RabbitMQ是一个由erlang开发的AMQP的开源实现. 核心概念 Message 消息,消息是不具名的,它由消息头和消息体组成,消息体是不透明的,而消息头则由 一系列的可选属性组成,这些属性包 ...
- RabbitMQ学习笔记五:RabbitMQ之优先级消息队列
RabbitMQ优先级队列注意点: 1.只有当消费者不足,不能及时进行消费的情况下,优先级队列才会生效 2.RabbitMQ3.5以后才支持优先级队列 代码在博客:RabbitMQ学习笔记三:Java ...
- 官网英文版学习——RabbitMQ学习笔记(一)认识RabbitMQ
鉴于目前中文的RabbitMQ教程很缺,本博主虽然买了一本rabbitMQ的书,遗憾的是该书的代码用的不是java语言,看起来也有些不爽,且网友们不同人学习所写不同,本博主看的有些地方不太理想,为此本 ...
- RabbitMQ学习笔记(五) Topic
更多的问题 Direct Exchange帮助我们解决了分类发布与订阅消息的问题,但是Direct Exchange的问题是,它所使用的routingKey是一个简单字符串,这决定了它只能按照一个条件 ...
- RabbitMQ学习笔记1-hello world
安装过程略过,一搜一大把. rabbitmq管理控制台:http://localhost:15672/ 默认账户:guest/guest RabbitMQ默认监听端口:5672 JAVA API地 ...
- 官网英文版学习——RabbitMQ学习笔记(十)RabbitMQ集群
在第二节我们进行了RabbitMQ的安装,现在我们就RabbitMQ进行集群的搭建进行学习,参考官网地址是:http://www.rabbitmq.com/clustering.html 首先我们来看 ...
- RabbitMQ系列(四)RabbitMQ事务和Confirm发送方消息确认——深入解读
RabbitMQ事务和Confirm发送方消息确认--深入解读 RabbitMQ系列文章 RabbitMQ在Ubuntu上的环境搭建 深入了解RabbitMQ工作原理及简单使用 RabbitMQ交换器 ...
随机推荐
- nodejs-Path模块
JavaScript 标准参考教程(alpha) 草稿二:Node.js Path模块 GitHub TOP Path模块 来自<JavaScript 标准参考教程(alpha)>,by ...
- 设计和实现OLAP解决方案 [转]
第一讲 简介首先,啥叫数据仓库? 数据仓库就是数据的仓库!用外文说叫Data Warehouse,简称DW. 是不是哐当倒下一片啊,要不咱换个专业点的说法? 数据仓库是一个面向主题的.集成的.相对稳定 ...
- 转 onSaveInstanceState()和onRestoreInstanceState()使用详解
转 https://www.jianshu.com/p/27181e2e32d2 背景 如果系统由于系统约束(而不是正常的应用程序行为)而破坏了Activity,那么尽管实际 Activity实例已经 ...
- oracle 执行计划的获取方法
1.用explain plan for来获取执行计划 explain plan for <sql>; select * from table(dbms_xplan.display()); ...
- 【Matlab】取整函数:fix/round/floor/ceil
fix-向零方向取整.(向中间取整) round-向最近的方向取整.(四舍五入) floor-向负无穷大方向取整.(向下取整) ceil-向正无穷大方向取整.(向上取整)
- Python循环控制
一.比较符 和算术操作符一样,布尔操作符也有操作顺序.在所有算术和比较操作符求值后,Python 先求值 not 操作符,然后是 and 操作符,然后是 or 操作符. 二.if控制 if name ...
- Python绘制柱状图
1.1Python绘制柱状图对应代码如下所示 import matplotlib.pyplot as plt import numpy as np from pylab import mpl mpl. ...
- pwnable_start & ciscn_2019_es_2 & ez_pz_hackover_2016 & pwn2_sctf_2016
花了两天时间做了这四道题,感觉收获很多.但是这种收获感觉写文章写不出自己的思路,就录制了一个视频. pwnable_start 这道题考察了系统调用,shellcode的编写,和动态调试的知识. ci ...
- C语言程序与设计:统计素数并求和
目录 C语言程序与设计:统计素数并求和 1.题目要求 2.分析 3.代码 C语言程序与设计:统计素数并求和 1.题目要求 输入两个正整数 m 和 n(1≤m≤n≤500),统计给定整数 m 和 n 区 ...
- CF127A Wasted Time 题解
Content 平面上有 \(A_1(x_1,y_1),A_2(x_2,y_2),...,A_n(x_n,y_n)\) 共计 \(n\) 个点.你需要依次将 \(A_1\) 连接至 \(A_2\),\ ...