什么是可靠性投递?
生产者:作为消息发送方希望杜绝任何消息丢失或者投递失败场景。
RabbitMQ 为我们提供了两种方式用来控制消息的投递可靠性模式。
分别是:
- confirm 确认模式
- return 退回模式
消息投递路线
如下所示是生产者到消费者的模型:
RabbitMQ的整个消息投递的路径
Producer--->RabbitMQ Broker(Server)--->Exchange--->Queue--->Consumer
- 消息从 producer 到 exchange 则会返回一个 confirmCallback 。
- 消息从 exchange 到 queue 投递失败则会返回一个 returnCallback 。
我们将利用这两个 callback 控制消息的可靠性投递。
确认模式
消息从 producer 到 exchange 则会返回一个 confirmCallback ,不论消息是否成功到达exchange ,回调都会执行,只不过返回的bool类型的值是true or false的区别。
代码实现很简单,两步即可
步骤
(1)在XML配置文件中设置 ConnectionFactory 开启 publisher-confirms="true"
(2)在 rabbitTemplate 定义 ConfirmCallBack() 回调函数
代码实现
spring-rabbitmq-producer.xml
<?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:context="http://www.springframework.org/schema/context"
xmlns:rabbit="http://www.springframework.org/schema/rabbit"
xmlns:rabb="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/context
https://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/rabbit
http://www.springframework.org/schema/rabbit/spring-rabbit.xsd">
<!-- 1.加载配置文件-->
<context:property-placeholder location="classpath:rabbitmq.properties"/>
<!-- 2.定义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-confirms="true" 确认模式开启!!! -->
<!-- 3.定义管理交换机、队列-->
<rabbit:admin connection-factory="connectionFactory"/>
<!-- 消息的可靠性投递 -->
<rabbit:queue id="test_queue_confirm" name="test_queue_confirm"></rabbit:queue>
<rabbit:direct-exchange name="test_exchange_confirm">
<rabbit:bindings>
<rabbit:binding queue="test_queue_confirm" key="confirm"></rabbit:binding>
</rabbit:bindings>
</rabbit:direct-exchange>
<!-- 定义rabbitTemplate对象操作可以在代码中方便发送消息-->
<rabbit:template id="rabbitTemplate" connection-factory="connectionFactory"/>
</beans>
ProducerTest
package com.Harmony;
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;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:spring-rabbitmq-producer.xml")
public class ProducerTest {
/**
* 确认模式
* 开启:
* 1. ConnectionFactory 中开启 publisher-confirms="true"
* 2. 在 rabbitTemplate 定义 ConfirmCallBack() 回调函数
*/
@Autowired
private RabbitTemplate rabbitTemplate;
// 2. 定义回调
// 一、确认模式
@Test
public void testConfirm() throws InterruptedException {
rabbitTemplate.setConfirmCallback(new RabbitTemplate.ConfirmCallback() {
/**
*
* @param correlationData: 配置信息,在convertAndSend()中的重载方法有该参数
* @param ack: 代表exchange交换机是否收到了信息,true为成功,false为失败
* @param cause: 失败原因; 如果成功为null
*/
@Override
public void confirm(CorrelationData correlationData, boolean ack, String cause) {
System.out.println(ack);
System.out.println("confirm方法被执行了......");
if (ack) {
System.out.println("接收成功:" + cause);
} else {
System.out.println("接收失败:" + cause);
// 以后可能会做一些处理
}
}
});
// 3. 发送消息
rabbitTemplate.convertAndSend("test_exchange_confirm","confirm","message confirm......");
// 由于在测试种,执行结束线程直接没有了!
Thread.sleep(2000);
}
}
退回模式
当消息从exchange路由到 queue失败后,如果设置了rabbitTemplate.setMandatory(true)参数,则会将消息退 回给producer。并执行回调函数returnedMessage。
步骤
(1)开启回退模式 publisher-returns="true"
(2)设置ReturnCallback
(3)设置Exchange处理消息的模式:
如果消息没有路由到Queue,则会丢弃消息(默认)
如果消息没有路由到Queue,返回给消息发送方
代码实现
spring-rabbitmq-producer.xml
<?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:context="http://www.springframework.org/schema/context"
xmlns:rabbit="http://www.springframework.org/schema/rabbit"
xmlns:rabb="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/context
https://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/rabbit
http://www.springframework.org/schema/rabbit/spring-rabbit.xsd">
<!-- 1.加载配置文件-->
<context:property-placeholder location="classpath:rabbitmq.properties"/>
<!-- 2.定义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-returns="true" />
<!-- publisher-returns="true" 回退模式开启!!! -->
<!-- 3.定义管理交换机、队列-->
<rabbit:admin connection-factory="connectionFactory"/>
<!-- 消息的可靠性投递 -->
<rabbit:queue id="test_queue_confirm" name="test_queue_confirm"></rabbit:queue>
<rabbit:direct-exchange name="test_exchange_confirm">
<rabbit:bindings>
<rabbit:binding queue="test_queue_confirm" key="confirm"></rabbit:binding>
</rabbit:bindings>
</rabbit:direct-exchange>
<!-- 定义rabbitTemplate对象操作可以在代码中方便发送消息-->
<rabbit:template id="rabbitTemplate" connection-factory="connectionFactory"/>
</beans>
ProducerTest
package com.Harmony;
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;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:spring-rabbitmq-producer.xml")
public class ProducerTest {
@Autowired
private RabbitTemplate rabbitTemplate;
@Test
public void testReturn() throws InterruptedException {
// 设置交换机处理失败消息的模式
// 生产者可以拿到之前发送失败的消息
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);
System.out.println(replyCode);
System.out.println(replyText);
System.out.println(exchange);
System.out.println(routingKey);
}
});
// 3. 发送消息
rabbitTemplate.convertAndSend("test_exchange_confirm","confirm","message confirm......");
Thread.sleep(2000);
}
}
总述
➢ 设置ConnectionFactory的publisher-confirms="true" 开启确认模式。
➢ 使用rabbitTemplate.setConfirmCallback设置回调函数。当消息发送到exchange后回 调confirm方法。在方法中判断ack,如果为true,则发送成功,如果为false,则发 送失败,需要处理。
➢ 设置ConnectionFactory的publisher-returns="true" 开启退回模式。
➢ 使用rabbitTemplate.setReturnCallback设置退回函数,当消息从exchange路由到 queue失败后,如果设置了rabbitTemplate.setMandatory(true)参数,则会将消息退 回给producer。并执行回调函数returnedMessage。
➢ 在RabbitMQ中也提供了事务机制,但是性能较差,此处不做讲解。 使用channel下列方法,完成事务控制:
- txSelect(), 用于将当前channel设置成transaction模式
- txCommit(),用于提交事务
- txRollback(),用于回滚事务