我在之前的博客提到过,当消费者消息接收到一半的时候挂掉时,可以使用消息接收确认机制进行补救。那在更加极端的场景下,RabbitMQ 挂掉了,又该怎么办呢?
在默认情况下,队列与消息都是非持久化的,想要将队列与消息进行保存,可以使用 RabbitMQ 的持久化机制。
如果队列不设置持久化 ,那么 RabbitMQ 服务重启后,相关的队列数据将会丢失。由于消息是存储在队列中的,所以队列中的消息也会丢失。
// 队列持久化代码
// 参数1 name :队列名
// 参数2 durable :是否持久化
// 参数3 exclusive :仅创建者可以使用的私有队列,断开后自动删除
// 参数4 autoDelete : 当所有消费客户端连接断开后,是否自动删除队列
new Queue(name, durable, exclusive, autoDelete);
如果交换器不设置持久化,那么 RabbitMQ 服务重启后,相关的交换器数据将会丢失,不过消息不会丢失,只是 RabbitMQ 生产者不能正常发送消息。
// 交换机持久化代码
// 参数1 name :交互器名
// 参数2 durable :是否持久化
// 参数3 autoDelete :当所有消费客户端连接断开后,是否自动删除队列
new TopicExchange(name, durable, autoDelete)
我们都知道,队列的持久化只能保证队列本身的数据不会丢失,如果需要保证消息不会丢失则需要消息本身被持久化。
至于消息持久化的设置,由于我们通常使用 rabbitTemplate.convertAndSend(exchange, routeKey, message);
来发送消息,而这种方法已经默认消息是持久化的,所以一般我们不需要设置。
死信队列,又称 dead-letter-exchange(DLX)。当一条消息在一个队列中变成死信后,它会被重新发布到一个交换机中,这个交换机就是 DLX。
配置类
package com.example.consumer.config;
import java.util.HashMap;
import java.util.Map;
import org.springframework.amqp.core.Binding;
import org.springframework.amqp.core.BindingBuilder;
import org.springframework.amqp.core.DirectExchange;
import org.springframework.amqp.core.Queue;
import org.springframework.amqp.rabbit.connection.ConnectionFactory;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* 直连型交换机
* @author 30309
*
*/
@Configuration
public class DirectRabbitConfig {
//队列,名称为DirectQueue
//同时将DirectQueue绑定到死信队列交换机上
@Bean
Queue DirectQueue() {
Map<String, Object> args = new HashMap<>(2);
//交换机标识符
args.put("x-dead-letter-exchange", "DeadExchange");
//绑定键标识符
args.put("x-dead-letter-routing-key", "DeadRoutingKey");
Queue queue = new Queue("DirectQueue", true, false, false, args);
return queue;
}
//直连型交换机,名称为DirectExchange
@Bean
DirectExchange DirectExchange() {
return new DirectExchange("DirectExchange");
}
//将队列和交换机绑定, 并设置用于匹配键:DirectRouting
@Bean
Binding bindingDirect() {
return BindingBuilder.bind(DirectQueue()).to(DirectExchange()).with("DirectRouting");
}
//创建死信队列
@Bean
Queue DeadQueue() {
return new Queue("DeadQueue", true);
}
//创建死信交换机
@Bean
DirectExchange DeadExchange() {
return new DirectExchange("DeadExchange");
}
//死信队列与死信交换机绑定
@Bean
Binding bindingDead() {
return BindingBuilder.bind(DeadQueue()).to(DeadExchange()).with("DeadRoutingKey");
}
@Bean
public RabbitTemplate rabbitTemplate(ConnectionFactory connectionFactory) {
RabbitTemplate rabbitTemplate = new RabbitTemplate(connectionFactory);
// 消息发送失败返回到队列中, 配置文件需要配置 publisher-returns: true
rabbitTemplate.setMandatory(true);
return rabbitTemplate;
}
}
生产者:
package com.example.provider.controller;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* 生产者
* @author 30309
*
*/
@RestController
public class SendMessageController{
@Autowired
RabbitTemplate rabbitTemplate;
@GetMapping("/sendDirectMessage")
public String sendDirectMessage() {
//将消息携带绑定键值DirectRouting发送到交换机DirectExchange
rabbitTemplate.convertAndSend("DirectExchange", "DirectRouting", "Hello World");
return "ok";
}
}
消费者1,连接普通队列,我们设置让它直接拒绝消息
package com.example.consumer.receiver;
import java.io.IOException;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.annotation.RabbitHandler;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;
import com.rabbitmq.client.Channel;
/**
* 消费者1
* @author 30309
*
*/
@Component
public class DirectReceiver1 {
@RabbitListener(queues = "DirectQueue")
@RabbitHandler
public void process(String str,Channel channel, Message message) {
System.out.println("DirectReceiver1消费者收到消息: " + str );
try {
channel.basicReject(message.getMessageProperties().getDeliveryTag(), false);
} catch (IOException e) {
e.printStackTrace();
}
}
}
消费者2,我们让它连接死信队列,可以发现它收到被消费者1拒绝的消息
package com.example.consumer.receiver;
import java.io.IOException;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.annotation.RabbitHandler;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;
import com.rabbitmq.client.Channel;
/**
* 消费者2
* @author 30309
*
*/
@Component
public class DirectReceiver2 {
@RabbitListener(queues = "DeadQueue")
@RabbitHandler
public void process(String str,Channel channel, Message message) {
System.out.println("DirectReceiver2消费者收到消息: " + str );
try {
channel.basicAck(message.getMessageProperties().getDeliveryTag(),false);
} catch (IOException e) {
e.printStackTrace();
}
}
}
参考:springboot实现rabbitmq的持久化和消息确认
RabbitMQ的死信队列详解