在使用rabbitmq的时候,作为消息发送方希望杜绝任何消息丢失或者投递失败的场景,rabbitMq为我们提供了两种方式用来控制消息的投递可靠性模式:
- confirm 确认模式
- return 回退模式
rabbitmq整个消息投递的路径为:生产者(producer)->rabbitmq broker->exchange->queue->consumer
- 消息从 producer到exchange则会返回一个confirmCallBack
- 消息从exchange到queue投递失败则会返回一个returnCallBack
<!-- 定义rabbitmq connectionFactory -->
<rabbit:connection-factory id="connectionFactory" host="${rabbitmq.host}"
port="${rabbitmq.port}"
username="${rabbitmq.username}"
password="${rabbitmq.password}"
virtual-host="${rabbitmq.virtual-host}"
publisher-confirms="true"
publisher-returns="true" />
我们利用spring整合rabbitmq的方式来简单的实践下(spring如何整合rabbitmq,请点击这里 RabbitMQ 入门篇之——Spring整合rabbitmq):
我们先来定义队列和交互机:
<!--消息可靠性投递(生产端)-->
<rabbit:queue id="spring_confirm_queue" name="spring_confirm_queue" auto-delete="true"/>
<rabbit:direct-exchange name="spring_confirm_exchange" id="spring_confirm_exchange" >
<rabbit:bindings>
<rabbit:binding queue="spring_confirm_queue" key="confirm"></rabbit:binding>
</rabbit:bindings>
</rabbit:direct-exchange>
生产者代码:
1.confirm
package com.cjian.rabbitmq.spring_rabbit;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.connection.CorrelationData;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
/**
* @description: confirm 确认模式
* @author: CJ
* @time: 2021/1/22 16:36
*/
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:spring-rabbitmq-producer.xml")
public class ConfirmReturnProducer {
@Autowired
private RabbitTemplate rabbitTemplate;
/*
* 消息发送给交换机后执行ConfirmCallBack
* 确认步骤:
* 1.开启确认模式:connectionFactory 开启 publisher-confirms="true"
* 2.在rabbitTemplate 中定义ConfirmCallBack回调函数
*
* */
@Test
public void testConfirm(){
rabbitTemplate.setConfirmCallback(new RabbitTemplate.ConfirmCallback() {
/**
*
* @param correlationData 相关配置信息
* @param ack 交换机是否成功的收到了消息 true成功 false失败
* @param cause 失败的原因
*/
@Override
public void confirm(CorrelationData correlationData, boolean ack, String cause) {
System.out.println("执行confirm,ack:"+ack);
if(!ack){
System.out.println("成功的接到了消息"+cause);
}else{
System.out.println("接收消息失败"+cause);
}
}
});
//发送一条消息
rabbitTemplate.convertAndSend("spring_confirm_exchange", "confirm", "hello rabbimq confirm");
}
正常发送消息:
模拟下发送异常:
2.return
package com.cjian.rabbitmq.spring_rabbit;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.connection.CorrelationData;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
/**
* @description: return模式
* @author: cWX969834
* @time: 2021/1/22 16:36
*/
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:spring-rabbitmq-producer.xml")
public class ConfirmReturnProducer {
@Autowired
private RabbitTemplate rabbitTemplate;
/*
回退模式:当消息发送给交换机后,交换机路由到队列失败时,才会执行ReturnCallBack
步骤:1.开启回退模式 publisher-returns="true"
2.设置ReturnCallBack
3.设置交换机处理消息的模式:
①如果消息没有路由到队列,则丢弃消息,也是默认的
②如果消息没有路由到队列,返回消息给发送方
*/
@Test
public void testReturn(){
//设置交换机处理失败消息的模式
rabbitTemplate.setMandatory(true);
rabbitTemplate.setReturnCallback(new RabbitTemplate.ReturnCallback() {
/**
*
* @param message 消息对象
* @param replyCode 失败的错误码
* @param replyText 错误信息
* @param exchange 交换机
* @param routingKey 路由键
*/
@Override
public void returnedMessage(Message message, int replyCode, String replyText, String exchange, String routingKey) {
System.out.println("return 执行了");
System.out.println("message:"+message);
System.out.println("replyCode:"+replyCode);
System.out.println("replyText:"+replyText);
System.out.println("exchange:"+exchange);
System.out.println("routingKey:"+routingKey);
}
});
rabbitTemplate.convertAndSend("spring_confirm_exchange", "confirm11", "hello rabbimq return");
}
}
我们直接模拟发送给队列失败的情形:
消息的可靠性投递总结:
- 设置connection-factory的publisher-confirms="true",开启确认模式;
- 使用rabbitTemplate.setConfirmCallback设置回调函数,当消息发送到交换机后回调confirm方法。在方法中判断ack,true:发送成功,false:发送失败;
- 设置connection-factory的publisher-return="true",开启回退模式;
- 使用rabbitTemplate.setReturnCallback设置回退函数,当消息从交换机路由到队列失败后,如果设置了
rabbitTemplate.setMandatory(true)参数,则会将消息退回给生产者,并执行回调函数returnedMessage.
- 在rabbitmq中也提供了事务机制,但是性能较差,此处就不说了。
使用channel.txSelect()用于将当前channel设置成事务模式;
使用channel.txCommit()用于提交事务;
使用channel.txRollBack()用于回滚事务。