前面几篇记录了收发消息的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的配置文件:

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <beans xmlns="http://www.springframework.org/schema/beans"
  3. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:rabbit="http://www.springframework.org/schema/rabbit"
  4. xsi:schemaLocation="http://www.springframework.org/schema/beans
  5. http://www.springframework.org/schema/beans/spring-beans.xsd
  6. http://www.springframework.org/schema/rabbit
  7. http://www.springframework.org/schema/rabbit/spring-rabbit-1.4.xsd">
  8. <!-- spring-rabbit.xsd的版本要注意,很1.4以前很多功能都没有,要用跟jar包匹配的版本 -->
  9. <bean id="jsonMessageConverter" class="org.springframework.amqp.support.converter.Jackson2JsonMessageConverter" />
  10. <rabbit:connection-factory
  11. id="connectionFactory"
  12. host="${rabbit.host}"
  13. port="${rabbit.port}"
  14. username="${rabbit.username}"
  15. password="${rabbit.password}"
  16. publisher-confirms="true"
  17. />
  18. <rabbit:admin connection-factory="connectionFactory" />
  19. <!-- 给模板指定转换器 --><!-- mandatory必须设置true,return callback才生效 -->
  20. <rabbit:template id="amqpTemplate"   connection-factory="connectionFactory"
  21. confirm-callback="confirmCallBackListener"
  22. return-callback="returnCallBackListener"
  23. mandatory="true"
  24. />
  25. <rabbit:queue name="CONFIRM_TEST" />
  26. <rabbit:direct-exchange name="DIRECT_EX" id="DIRECT_EX" >
  27. <rabbit:bindings>
  28. <rabbit:binding queue="CONFIRM_TEST" />
  29. </rabbit:bindings>
  30. </rabbit:direct-exchange>
  31. <!-- 配置consumer, 监听的类和queue的对应关系 -->
  32. <rabbit:listener-container
  33. connection-factory="connectionFactory" acknowledge="manual" >
  34. <rabbit:listener queues="CONFIRM_TEST" ref="receiveConfirmTestListener" />
  35. </rabbit:listener-container>
  36. </beans>

然后发送方:

  1. import org.springframework.amqp.core.AmqpTemplate;
  2. import org.springframework.beans.factory.annotation.Autowired;
  3. import org.springframework.stereotype.Service;
  4. @Service("publishService")
  5. public class PublishService {
  6. @Autowired
  7. private AmqpTemplate amqpTemplate;
  8. public void send(String exchange, String routingKey, Object message) {
  9. amqpTemplate.convertAndSend(exchange, routingKey, message);
  10. }
  11. }

消费方:

  1. import org.springframework.amqp.core.Message;
  2. import org.springframework.amqp.rabbit.core.ChannelAwareMessageListener;
  3. import org.springframework.stereotype.Service;
  4. import com.rabbitmq.client.Channel;
  5. @Service("receiveConfirmTestListener")
  6. public class ReceiveConfirmTestListener implements ChannelAwareMessageListener {
  7. @Override
  8. public void onMessage(Message message, Channel channel) throws Exception {
  9. try{
  10. System.out.println("consumer--:"+message.getMessageProperties()+":"+new String(message.getBody()));
  11. channel.basicAck(message.getMessageProperties().getDeliveryTag(), false);
  12. }catch(Exception e){
  13. e.printStackTrace();//TODO 业务处理
  14. channel.basicNack(message.getMessageProperties().getDeliveryTag(), false,false);
  15. }
  16. }
  17. }

确认后回调:

  1. import org.springframework.amqp.rabbit.core.RabbitTemplate.ConfirmCallback;
  2. import org.springframework.amqp.rabbit.support.CorrelationData;
  3. import org.springframework.stereotype.Service;
  4. @Service("confirmCallBackListener")
  5. public class ConfirmCallBackListener implements ConfirmCallback{
  6. @Override
  7. public void confirm(CorrelationData correlationData, boolean ack, String cause) {
  8. System.out.println("confirm--:correlationData:"+correlationData+",ack:"+ack+",cause:"+cause);
  9. }
  10. }

失败后return回调:

  1. import org.springframework.amqp.core.Message;
  2. import org.springframework.amqp.rabbit.core.RabbitTemplate.ReturnCallback;
  3. import org.springframework.stereotype.Service;
  4. @Service("returnCallBackListener")
  5. public class ReturnCallBackListener implements ReturnCallback{
  6. @Override
  7. public void returnedMessage(Message message, int replyCode, String replyText, String exchange, String routingKey) {
  8. System.out.println("return--message:"+new String(message.getBody())+",replyCode:"+replyCode+",replyText:"+replyText+",exchange:"+exchange+",routingKey:"+routingKey);
  9. }
  10. }

测试类:

  1. import org.junit.Test;
  2. import org.junit.runner.RunWith;
  3. import org.springframework.beans.factory.annotation.Autowired;
  4. import org.springframework.test.context.ContextConfiguration;
  5. import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
  6. import com.dingcheng.confirms.publish.PublishService;
  7. @RunWith(SpringJUnit4ClassRunner.class)
  8. @ContextConfiguration(locations = {"classpath:application-context.xml"})
  9. public class TestConfirm {
  10. @Autowired
  11. private PublishService publishService;
  12. private static String exChange = "DIRECT_EX";
  13. @Test
  14. public void test1() throws InterruptedException{
  15. String message = "currentTime:"+System.currentTimeMillis();
  16. System.out.println("test1---message:"+message);
  17. //exchange,queue 都正确,confirm被回调, ack=true
  18. publishService.send(exChange,"CONFIRM_TEST",message);
  19. Thread.sleep(1000);
  20. }
  21. @Test
  22. public void test2() throws InterruptedException{
  23. String message = "currentTime:"+System.currentTimeMillis();
  24. System.out.println("test2---message:"+message);
  25. //exchange 错误,queue 正确,confirm被回调, ack=false
  26. publishService.send(exChange+"NO","CONFIRM_TEST",message);
  27. Thread.sleep(1000);
  28. }
  29. @Test
  30. public void test3() throws InterruptedException{
  31. String message = "currentTime:"+System.currentTimeMillis();
  32. System.out.println("test3---message:"+message);
  33. //exchange 正确,queue 错误 ,confirm被回调, ack=true; return被回调 replyText:NO_ROUTE
  34. publishService.send(exChange,"",message);
  35. //        Thread.sleep(1000);
  36. }
  37. @Test
  38. public void test4() throws InterruptedException{
  39. String message = "currentTime:"+System.currentTimeMillis();
  40. System.out.println("test4---message:"+message);
  41. //exchange 错误,queue 错误,confirm被回调, ack=false
  42. publishService.send(exChange+"NO","CONFIRM_TEST",message);
  43. Thread.sleep(1000);
  44. }
  45. }

测试结果:

  1. test1---message:currentTime:1483786948506
  2. test2---message:currentTime:1483786948532
  3. 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
  4. test3---message:currentTime:1483786948536
  5. 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)
  6. confirm--:correlationData:null,ack:false,cause:Channel closed by application
  7. [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)
  8. return--message:currentTime:1483786948536,replyCode:312,replyText:NO_ROUTE,exchange:DIRECT_EX,routingKey:
  9. confirm--:correlationData:null,ack:true,cause:null
  10. test4---message:currentTime:1483786948546
  11. 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)
  12. [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消息确认(发送确认,接收确认)的更多相关文章

  1. 【Spring Boot】Spring Boot之整合RabbitMQ并实现消息的发送和接收

    一.项目配置 1)引入maven坐标 <!--amqp--> <dependency> <groupId>org.springframework.boot</ ...

  2. Queue 消息的发送与接收(PTP 消息传递模型)

    上篇博客写到了JMS两种消息模型(P2P.pub/sub)<JMS两种消息模型>.本篇博客通过一个实例来进一步了解P2P模型. Queue消息的发送与接收--PTP消息传递模型,样例: Q ...

  3. 探索 OpenStack 之(15):oslo.messaging 和 Cinder 中 MessageQueue 消息的发送和接收

    前言:上一篇文章 只是 RabbitMQ 的科普,本文将仔细分析 Cinder 中 RabbitMQ 的各组件的使用.消息的发送和接收等.由于各流程步骤很多,本文只会使用若干流程图来加以阐述,尽量做到 ...

  4. Udp实现消息的发送和接收、以及图片的上传

    //Udp实现消息的发送和接收 import java.io.IOException; import java.net.DatagramPacket; import java.net.Datagram ...

  5. nodejs 数据库操作,消息的发送和接收,模拟同步

    var deasync = require('deasync'); //导入模板 var mysql=require('mysql'); var Stomp = require('stompjs'); ...

  6. msgrcv,msgsnd进程通信,消息的发送和接收

    //进程通信,消息的发送和接收 //client.c #include <unistd.h> #include <sys/types.h> #include <sys/s ...

  7. RabbitMQ消息发布和消费的确认机制

    前言 新公司项目使用的消息队列是RabbitMQ,之前其实没有在实际项目上用过RabbitMQ,所以对它的了解都谈不上入门.趁着周末休息的时间也猛补习了一波,写了两个窗体应用,一个消息发布端和消息消费 ...

  8. 【转载】java实现rabbitmq消息的发送接受

    原文地址:http://blog.csdn.net/sdyy321/article/details/9241445 本文不介绍amqp和rabbitmq相关知识,请自行网上查阅 本文是基于spring ...

  9. 用PHP尝试RabbitMQ(amqp扩展)实现消息的发送和接收

    消费者:接收消息 逻辑:创建连接-->创建channel-->创建交换机-->创建队列-->绑定交换机/队列/路由键-->接收消息 <?php /********* ...

  10. DICOM医学图像处理:DIMSE消息发送与接收“大同小异”之DCMTK fo-dicom mDCM

    背景: 从DICOM网络传输一文开始,相继介绍了C-ECHO.C-FIND.C-STORE.C-MOVE等DIMSE-C服务的简单实现,博文中的代码给出的实例都是基于fo-dicom库来实现的,原因只 ...

随机推荐

  1. 窗体彻底关闭事件FormClosed

    //Application.Exit()是退出整个应用程序 Application.ExitThread();//强制中止调用线程上的所有消息,同样面临其它线程无法正确退出的问题 System.Dia ...

  2. 每日英语:Some Chinese Students Stay Home to Get Ahead

    Li Shan's oldest son was the perfect candidate to join the throngs of Chinese students studying abro ...

  3. Python 按当前日期(年、月、日)创建多级目录的方法

    先看实际效果,现在时间2018.4.26 使用python脚本按照年月日生成多级目录,创建的目录可以将系统生成的日志文件放入其中,方便查阅,代码如下: #!/usr/bin/env python #c ...

  4. destoon的如何显示tag生成的sql语句

    destoon 如何显示tag生成的sql语句 在tag.func.php中第117行加入 echo $query;  就只可以了

  5. Ubuntu下安装JDK1.7

    Ubuntu操作系统下如何手动安装JDK1.7呢?本文是我经历的全过程. Ubuntu版本:12.04 LTS JDK版本:1.7.0_04 安装目录:/usr/local/development/j ...

  6. eclipse 项目中的java文件没有在WEB-INF目录下的classes中 生成相对应的编译后的类

    1.首先确定project->Build Automatically是否勾选上: 2.执行完第一步之后测试一下看是否能编译,如果还是不能,则进行手动编译:  3,进入clean对话框,选择Cle ...

  7. C#播放器控件的常用方法介绍

    右击工具箱->选择项(I)... -> 显示"选择工具箱项" -> COM组件 -> Windows Media Player   wmp.dll 添加 [ ...

  8. [DNS]部署局域网DNS服务器

    This is a step by step tutorial on how to install and configure DNS server for your LAN using bind9. ...

  9. C中结构体的存储分配

    C中结构体的存储分配 对于C语言中结构体所占的存储空间的大小,也一直是笔试面试的常客,今天好好看了一下这方面,以前一直以为很清楚了,今天通过各种实际测试举例,发现原来还是没有搞透彻,好在现在是彻底懂了 ...

  10. 关于JXL读写以及修改EXCEL文件<转>

    首先引用网上的文章,谈谈JXL与POI的区别 POI为apache公司的一个子项目,主要是提供一组操作windows文档的Java API. Java Excel俗称jxl是一开放源码项目,通过它Ja ...