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

最新文章

  1. Centos允许root远程登录设置
  2. tornado学习笔记14 HTTP1ServerConnection分析
  3. 阅读笔记 火球——UML大战需求分析 2
  4. java 如何在pdf中生成表格
  5. 远程连接数据库(通过pgAdmin)
  6. Cmakelist.txt
  7. DNS递归和迭代原理
  8. Oracle查看和修改连接数(进程/会话/并发等等)
  9. Ubuntu 16.04.3 安装jenkins
  10. scrapy之日志等级
  11. python django基础一web框架的本质
  12. 【容斥+组合数】Massage @2018acm徐州邀请赛 E
  13. ABBYY PDF Transformer+安装教程
  14. Oracle关于自连接、左外连接、右外连接、全连接
  15. sbt介绍与构建Scala项目
  16. iOS7,iOS8和iOS9的区别
  17. CDLinux制作成U盘启动
  18. Freemarker-2.3.22 Demo - No01_获取模板并直接输出
  19. iOS:CoreData数据库的使用三(数据库和tableView表格一起使用)
  20. python大数据挖掘系列之淘宝商城数据预处理实战

热门文章

  1. 使用自定义验证组件库扩展 Windows 窗体
  2. jQery的方法
  3. jQuery实现的手风琴效果
  4. tomcat Can't create cache file!
  5. 7处ff与ie中读写html、css相关属性的不同之处
  6. iOS UIWebView与WKWebView使用详解
  7. 一款基于jquery超炫的图片切换特效
  8. RTMP规范 消息与消息块
  9. QSignalMapper类处理多信号关联同一个槽的方法(2)
  10. MapReduce调度与执行原理系列文章