RabbitMQ消息确认(发送确认,接收确认)
前面几篇记录了收发消息的demo,今天记录下关于 消息确认方面的 问题.
下面是几个问题:
1.为什么要进行消息确认?
2.rabbitmq消息确认 机制是什么样的?
3.发送方如何确认消息发送成功?什么样才算发送成功?
4.消费方如何告知rabbitmq消息消费成功或失败?
5.使用spring的代码示例
1.为什么要进行消息确认?
经常会听到丢消息的字眼, 对于前面的demo来说,就存在丢消息的隐患.
发送者没法确认是否发送成功,消费者处理失败也无法反馈.
没有消息确认机制,就会出现消息莫名其妙的没了,也不知道什么情况.
2.rabbitmq消息确认 机制是什么样的?
首先看官网对消息确认的介绍http://www.rabbitmq.com/confirms.html
网上会有很多总结的博客(包括现在看的),很多就是对官网的翻译.所以看资料首先要去官网看看,这很关键.
看上图官网的介绍.唯一保证消息不丢失的是使用事务,但是性能太差,作为补偿,有了消息确认机制.
并说明了开启方法,以及和事务模式不共存.
还写了一个例子,但是点进去那个链接已经失效了,新版的源码上也没有这个例子,我找了最近一版是3.6.7上面还有.
3.发送的消息什么样才算成功或失败? 如何确认?
判断消息成功或失败,其实就是看进行消息确认的时机,因为成功或失败后就会把结果告诉发送方.还是看官方解释:
意思如下:
确认消息不能路由时(exchange确认不能路由到任何queue),进行确认操作(确认失败).如果发送方设置了mandatory模式,则会先调用basic.return方法.
消息可以路由时,当需要发送的队列都发送成功后,进行消息确认.对于持久化的队列,意味着已经写入磁盘,对于镜像队列,意味着所有镜像都接受成功.
至于如何确认的问题,上面已经写了 basic.ack方法
4.消费方如何告知rabbitmq消息消费成功或失败?
如图可知,根据消费方不同的确认模式,确认时机也不同.
自动确认会在消息发送给消费者后立即确认,如果手动则当消费者调用ack,nack,reject几种方法时进行确认.
一般会设置手动模式,业务失败后可以进行一些操作.
5.使用spring的代码示例
下面是一个使用spring整合的代码示例:
首先是rabbitmq的配置文件:
- <?xml version="1.0" encoding="UTF-8"?>
- <beans xmlns="http://www.springframework.org/schema/beans"
- xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:rabbit="http://www.springframework.org/schema/rabbit"
- xsi:schemaLocation="http://www.springframework.org/schema/beans
- http://www.springframework.org/schema/beans/spring-beans.xsd
- http://www.springframework.org/schema/rabbit
- http://www.springframework.org/schema/rabbit/spring-rabbit-1.4.xsd">
- <!-- spring-rabbit.xsd的版本要注意,很1.4以前很多功能都没有,要用跟jar包匹配的版本 -->
- <bean id="jsonMessageConverter" class="org.springframework.amqp.support.converter.Jackson2JsonMessageConverter" />
- <rabbit:connection-factory
- id="connectionFactory"
- host="${rabbit.host}"
- port="${rabbit.port}"
- username="${rabbit.username}"
- password="${rabbit.password}"
- publisher-confirms="true"
- />
- <rabbit:admin connection-factory="connectionFactory" />
- <!-- 给模板指定转换器 --><!-- mandatory必须设置true,return callback才生效 -->
- <rabbit:template id="amqpTemplate" connection-factory="connectionFactory"
- confirm-callback="confirmCallBackListener"
- return-callback="returnCallBackListener"
- mandatory="true"
- />
- <rabbit:queue name="CONFIRM_TEST" />
- <rabbit:direct-exchange name="DIRECT_EX" id="DIRECT_EX" >
- <rabbit:bindings>
- <rabbit:binding queue="CONFIRM_TEST" />
- </rabbit:bindings>
- </rabbit:direct-exchange>
- <!-- 配置consumer, 监听的类和queue的对应关系 -->
- <rabbit:listener-container
- connection-factory="connectionFactory" acknowledge="manual" >
- <rabbit:listener queues="CONFIRM_TEST" ref="receiveConfirmTestListener" />
- </rabbit:listener-container>
- </beans>
然后发送方:
- import org.springframework.amqp.core.AmqpTemplate;
- import org.springframework.beans.factory.annotation.Autowired;
- import org.springframework.stereotype.Service;
- @Service("publishService")
- public class PublishService {
- @Autowired
- private AmqpTemplate amqpTemplate;
- public void send(String exchange, String routingKey, Object message) {
- amqpTemplate.convertAndSend(exchange, routingKey, message);
- }
- }
消费方:
- import org.springframework.amqp.core.Message;
- import org.springframework.amqp.rabbit.core.ChannelAwareMessageListener;
- import org.springframework.stereotype.Service;
- import com.rabbitmq.client.Channel;
- @Service("receiveConfirmTestListener")
- 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()));
- channel.basicAck(message.getMessageProperties().getDeliveryTag(), false);
- }catch(Exception e){
- e.printStackTrace();//TODO 业务处理
- channel.basicNack(message.getMessageProperties().getDeliveryTag(), false,false);
- }
- }
- }
确认后回调:
- import org.springframework.amqp.rabbit.core.RabbitTemplate.ConfirmCallback;
- import org.springframework.amqp.rabbit.support.CorrelationData;
- import org.springframework.stereotype.Service;
- @Service("confirmCallBackListener")
- public class ConfirmCallBackListener implements ConfirmCallback{
- @Override
- public void confirm(CorrelationData correlationData, boolean ack, String cause) {
- System.out.println("confirm--:correlationData:"+correlationData+",ack:"+ack+",cause:"+cause);
- }
- }
失败后return回调:
- import org.springframework.amqp.core.Message;
- import org.springframework.amqp.rabbit.core.RabbitTemplate.ReturnCallback;
- import org.springframework.stereotype.Service;
- @Service("returnCallBackListener")
- public class ReturnCallBackListener implements ReturnCallback{
- @Override
- public void returnedMessage(Message message, int replyCode, String replyText, String exchange, String routingKey) {
- System.out.println("return--message:"+new String(message.getBody())+",replyCode:"+replyCode+",replyText:"+replyText+",exchange:"+exchange+",routingKey:"+routingKey);
- }
- }
测试类:
- import org.junit.Test;
- import org.junit.runner.RunWith;
- import org.springframework.beans.factory.annotation.Autowired;
- import org.springframework.test.context.ContextConfiguration;
- import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
- import com.dingcheng.confirms.publish.PublishService;
- @RunWith(SpringJUnit4ClassRunner.class)
- @ContextConfiguration(locations = {"classpath:application-context.xml"})
- public class TestConfirm {
- @Autowired
- private PublishService publishService;
- private static String exChange = "DIRECT_EX";
- @Test
- public void test1() throws InterruptedException{
- String message = "currentTime:"+System.currentTimeMillis();
- System.out.println("test1---message:"+message);
- //exchange,queue 都正确,confirm被回调, ack=true
- publishService.send(exChange,"CONFIRM_TEST",message);
- Thread.sleep(1000);
- }
- @Test
- public void test2() throws InterruptedException{
- String message = "currentTime:"+System.currentTimeMillis();
- System.out.println("test2---message:"+message);
- //exchange 错误,queue 正确,confirm被回调, ack=false
- publishService.send(exChange+"NO","CONFIRM_TEST",message);
- Thread.sleep(1000);
- }
- @Test
- public void test3() throws InterruptedException{
- String message = "currentTime:"+System.currentTimeMillis();
- System.out.println("test3---message:"+message);
- //exchange 正确,queue 错误 ,confirm被回调, ack=true; return被回调 replyText:NO_ROUTE
- publishService.send(exChange,"",message);
- // Thread.sleep(1000);
- }
- @Test
- public void test4() throws InterruptedException{
- String message = "currentTime:"+System.currentTimeMillis();
- System.out.println("test4---message:"+message);
- //exchange 错误,queue 错误,confirm被回调, ack=false
- publishService.send(exChange+"NO","CONFIRM_TEST",message);
- Thread.sleep(1000);
- }
- }
测试结果:
- test1---message:currentTime:1483786948506
- test2---message:currentTime:1483786948532
- consumer--:MessageProperties [headers={spring_return_correlation=445bc7ca-a5bd-47e2-8ba3-f0448420e441}, 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=DIRECT_EX, receivedRoutingKey=CONFIRM_TEST, deliveryTag=1, messageCount=0]:currentTime:1483786948506
- test3---message:currentTime:1483786948536
- confirm--:correlationData:null,ack:false,cause:channel error; protocol method: #method<channel.close>(reply-code=404, reply-text=NOT_FOUND - no exchange 'DIRECT_EXNO' in vhost '/', class-id=60, method-id=40)
- confirm--:correlationData:null,ack:false,cause:Channel closed by application
- [ERROR] 2017-01-07 19:02:28 org.springframework.amqp.rabbit.connection.CachingConnectionFactory.shutdownCompleted(CachingConnectionFactory.java:281):--> Channel shutdown: channel error; protocol method: #method<channel.close>(reply-code=404, reply-text=NOT_FOUND - no exchange 'DIRECT_EXNO' in vhost '/', class-id=60, method-id=40)
- return--message:currentTime:1483786948536,replyCode:312,replyText:NO_ROUTE,exchange:DIRECT_EX,routingKey:
- confirm--:correlationData:null,ack:true,cause:null
- test4---message:currentTime:1483786948546
- confirm--:correlationData:null,ack:false,cause:channel error; protocol method: #method<channel.close>(reply-code=404, reply-text=NOT_FOUND - no exchange 'DIRECT_EXNO' in vhost '/', class-id=60, method-id=40)
- [ERROR] 2017-01-07 19:02:28 org.springframework.amqp.rabbit.connection.CachingConnectionFactory.shutdownCompleted(CachingConnectionFactory.java:281):--> Channel shutdown: channel error; protocol method: #method<channel.close>(reply-code=404, reply-text=NOT_FOUND - no exchange 'DIRECT_EXNO' in vhost '/', class-id=60, method-id=40)
代码和配置里面,已经都有注释,就不在多说明了.(callback是异步的,所以测试中sleep1秒钟等待下)
总结下就是:
如果消息没有到exchange,则confirm回调,ack=false
如果消息到达exchange,则confirm回调,ack=true
exchange到queue成功,则不回调return
exchange到queue失败,则回调return(需设置mandatory=true,否则不回回调,消息就丢了)
备注:需要说明,spring-rabbit和原生的rabbit-client ,表现是不一样的.
测试的时候,原生的client,exchange错误的话,直接就报错了,是不会到confirmListener和returnListener的
RabbitMQ消息确认(发送确认,接收确认)的更多相关文章
- 【Spring Boot】Spring Boot之整合RabbitMQ并实现消息的发送和接收
一.项目配置 1)引入maven坐标 <!--amqp--> <dependency> <groupId>org.springframework.boot</ ...
- Queue 消息的发送与接收(PTP 消息传递模型)
上篇博客写到了JMS两种消息模型(P2P.pub/sub)<JMS两种消息模型>.本篇博客通过一个实例来进一步了解P2P模型. Queue消息的发送与接收--PTP消息传递模型,样例: Q ...
- 探索 OpenStack 之(15):oslo.messaging 和 Cinder 中 MessageQueue 消息的发送和接收
前言:上一篇文章 只是 RabbitMQ 的科普,本文将仔细分析 Cinder 中 RabbitMQ 的各组件的使用.消息的发送和接收等.由于各流程步骤很多,本文只会使用若干流程图来加以阐述,尽量做到 ...
- Udp实现消息的发送和接收、以及图片的上传
//Udp实现消息的发送和接收 import java.io.IOException; import java.net.DatagramPacket; import java.net.Datagram ...
- nodejs 数据库操作,消息的发送和接收,模拟同步
var deasync = require('deasync'); //导入模板 var mysql=require('mysql'); var Stomp = require('stompjs'); ...
- msgrcv,msgsnd进程通信,消息的发送和接收
//进程通信,消息的发送和接收 //client.c #include <unistd.h> #include <sys/types.h> #include <sys/s ...
- RabbitMQ消息发布和消费的确认机制
前言 新公司项目使用的消息队列是RabbitMQ,之前其实没有在实际项目上用过RabbitMQ,所以对它的了解都谈不上入门.趁着周末休息的时间也猛补习了一波,写了两个窗体应用,一个消息发布端和消息消费 ...
- 【转载】java实现rabbitmq消息的发送接受
原文地址:http://blog.csdn.net/sdyy321/article/details/9241445 本文不介绍amqp和rabbitmq相关知识,请自行网上查阅 本文是基于spring ...
- 用PHP尝试RabbitMQ(amqp扩展)实现消息的发送和接收
消费者:接收消息 逻辑:创建连接-->创建channel-->创建交换机-->创建队列-->绑定交换机/队列/路由键-->接收消息 <?php /********* ...
- DICOM医学图像处理:DIMSE消息发送与接收“大同小异”之DCMTK fo-dicom mDCM
背景: 从DICOM网络传输一文开始,相继介绍了C-ECHO.C-FIND.C-STORE.C-MOVE等DIMSE-C服务的简单实现,博文中的代码给出的实例都是基于fo-dicom库来实现的,原因只 ...
随机推荐
- 在java代码中用xslt处理xml文件
java处理xml文件 import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundExceptio ...
- C# 获取FormData数据
通常的方法是你创建一个 FormData 对象.然后你使用append方法来加入任何额外的key和他们的值.就像这样: var form = new FormData(); form.append(& ...
- python 连接sql server数据库的示例代码
首先,到http://pymssql.sourceforge.net/下载pymssql模块,必须安装这个模块才可以用python连接mysql 以下是sql server的操作代码,需要注意字符集 ...
- 多重连接的数据库管理工具:Navicat Premium
多重连接的数据库管理工具:Navicat Premium 2016-09-26 Navicat Premium(非免费软件)是一个可多重连接的数据库管理工具,它可让你以单一程序同时连接到MySQL.O ...
- 安卓手机数据恢复软件-DiskDigger Pro
以前的手机是支持大容量存储模式,可以被分配到盘符,但是自从手机不支持U盘大容量存储模式只能MTP模式之后,想要做数据恢复麻烦了很多啊! 经过多方查找,终于找到了这个能在手机上用的软件DiskDigge ...
- 访问控制列表-基于IP
[总结] 在交换机1上划分vlan然后把fa0/1接口设置为trunk周围几个接口都设置为access.在RA上划分四个逻辑口(就是小数点的那些接口).然后RA和RB都做ospf就可以ping同. 最 ...
- zookeeper原生API做java客户端
简介 本文是使用apache提供的原生api做zookeeper客户端 jar包 zookeeper-3.4.5.jar Demo package bjsxt.zookeeper.base; im ...
- thrift学习之一-------介绍
thrift 官网: http://thrift.apache.org/ Thrift是一个软件框架,用于支持可扩展的跨语言服务的开发,它无缝的与C++,Java,Python,PHP,Ruby,E ...
- Hadoop计算中的Shuffle过程(转)
Hadoop计算中的Shuffle过程 作者:左坚 来源:清华万博 时间:2013-07-02 15:04:44.0 Shuffle过程是MapReduce的核心,也被称为奇迹发生的地方.要想理解Ma ...
- 设计和开发ETL系统(一)——ETL过程综述
在这部分将按照设计和实现ETL系统的流程展开,将上一个部分的那些子系统按照提取数据.清洗和一致化.向呈现服务器提交以及管理ETL环境等四个方面进行了分类.(是不是说对ETL主要就是掌握这四个方面的内容 ...