说明: RabbitMQ消息的可靠投递
- 在使用 RabbitMQ 的时候,作为消息发送方希望杜绝任何消息丢失或者投递失败场景。
- RabbitMQ 提供了两种方式用来控制消息的投递可靠性模式: confirm 确认模式, return 回退模式。
- Consumer Ack: 表示消费端收到消息后的确认方式。
1. RabbitMQ confirm 确认模式和 return 回退模式
spring:
rabbitmq:
host: 192.168.192.137
username: qmh
password: qmh
virtual-host: /qmh
port: 5672
# publisher-confirms 消息的可靠投递, confirm 确认模式 默认为false
publisher-confirms: true
# publisher-returns 消息的可靠投递, return 回退模式 默认为false
publisher-returns: true
import org.springframework.amqp.core.*;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class RabbitMQConfig {
public static final String DIRECT_EXCHANGE = "springboot_direct_exchange";
public static final String DIRECT_QUEUE_CONFIRM = "springboot_direct_queue_confirm"; // 测试RabbitMQ消息的可靠投递
// 1. 定义交换机
@Bean("DirectExchange")
public Exchange directExchange(){
return ExchangeBuilder.topicExchange(DIRECT_EXCHANGE).durable(true).build();
}
// 2. 定义队列
@Bean("DirectQueueConfirm")
public Queue directQueueConfirm(){
return QueueBuilder.durable(DIRECT_QUEUE_CONFIRM).build();
}
// 3. 绑定交换机与队列
@Bean
public Binding directBindQueueConfirmAndExchange(@Qualifier("DirectQueueConfirm") Queue directQueueConfirm,@Qualifier("DirectExchange") Exchange directExchange){
return BindingBuilder.bind(directQueueConfirm).to(directExchange).with("confirm").noargs();
}
}
import com.itheima.rabbitmqConfig.RabbitMQConfig;
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.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
@SpringBootTest
@RunWith(SpringRunner.class)
public class ProducerTest {
@Autowired
private RabbitTemplate rabbitTemplate;
/**
* 测试消息的可靠传递
* confirm 确认模式
* 当消息发送到exchange后回调confirm方法
*/
@Test
public void directConfirmTest(){
// 定义回调
rabbitTemplate.setConfirmCallback(new RabbitTemplate.ConfirmCallback() {
/**
* @param correlationData 相关配置信息
* @param ack exchange交换机是否成功收到信息
* @param cause 失败原因
*/
@Override
public void confirm(CorrelationData correlationData, boolean ack, String cause) {
System.out.println("confirm running ...");
if (ack){
System.out.println("成功接收");
}else {
System.out.println("接收失败:" + cause);
}
}
});
// 发送消息
rabbitTemplate.convertAndSend(RabbitMQConfig.DIRECT_EXCHANGE,"confirm","Test RabbitMQ confirm success!");
}
/**
* 测试消息的可靠传递 return 回退模式
* 当消息从exchange路由到queue失败后, 则会将消息退回给producer。并执行回调函数returnedMessage
* 步骤:
* -- 开启回退模式 publisher-returns: true
* -- 设置ReturnCallBack回调方法
* -- 设置exchange交换机处理模式:
* 1. 如果消息没有路由到Queue, 则丢弃消息(默认)
* 2. 如果消息没有路由到Queue, 则返回消息给发送方ReturnCallBack.
*/
@Test
public void directReturnTest(){
// 设置交换机处理失败消息的模式
// rabbitTemplate.setMandatory(true);
// 设置ReturnCallBack回调方法
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 running...");
}
});
// 发送消息
rabbitTemplate.convertAndSend(RabbitMQConfig.DIRECT_EXCHANGE,"confirm11","Test RabbitMQ return success!");
}
}
2. RabbitMQ Consumer Ack 模式(下面案例是manual确认方式)
说明: Consumer Ack有三种确认方式
- 自动确认:acknowledge=“none”, (默认使用)
- 手动确认:acknowledge=“manual”, (建议使用)
- 根据异常情况确认:acknowledge=“auto”
spring:
rabbitmq:
host: 192.168.192.137
username: qmh
password: qmh
virtual-host: /qmh
port: 5672
# 开启manual确认模式
listener:
simple:
acknowledge-mode: manual
import com.rabbitmq.client.Channel;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.amqp.rabbit.listener.api.ChannelAwareMessageListener;
import org.springframework.stereotype.Component;
/**
* Consumer Ack 机制 ack指Acknowledge,确认。 表示消费端收到消息后的确认方式。
* 有三种确认方式:
* -- 自动确认:acknowledge="none", 当消息一旦被Consumer接收到,则自动确认收到,并将相应 message 从 RabbitMQ 的消息缓存中移除
* -- 手动确认:acknowledge="manual",需要在业务处理成功后,调用channel.basicAck(),手动签收,如果出现异常,则调用channel.basicNack()方法,让其自动重新发送消息。
* -- 根据异常情况确认:acknowledge="auto",
*/
@Component
public class ListenerConsumerAck implements ChannelAwareMessageListener {
@Override
@RabbitListener(queues = "springboot_direct_queue_confirm")
public void onMessage(Message message, Channel channel) throws Exception {
Thread.sleep(1000);
long deliveryTag = message.getMessageProperties().getDeliveryTag();
try {
System.out.println(new String(message.getBody()));
System.out.println("逻辑业务处理完成...");
//int i = 3/0; //出错
/**
* 手动确认签收 basicAck(long deliveryTag, boolean multiple)
* deliveryTag 标签
* multiple 接收多条信息
*/
channel.basicAck(deliveryTag,true);
}catch (Exception e){
/**
* 签收失败 basicNack(long deliveryTag, boolean multiple, boolean requeue)
* deliveryTag 标签
* multiple 接收多条信息
* requeue true,消息重回Queue,broker重新发送该消息给消费者
*/
channel.basicNack(deliveryTag,true,true);
}
}
}