1、死信队列
1.1、DeadLetter: 是RabbitMQ中的一种消息机制
出现死信消息的可能情况如下
- 消息被否定确认,channel.basicNack 或 channel.basicReject, requeue=false
- 消息在队列的存活时间超过设置的TTL时间
- 消息队列的消息总数已经超过最大的队列长度
1.2、 Dead Letter Pattern 死信模式
当消费者不能处理接收到的消息时候, 将这个消息重新发布到另一个队列中,这个过程中的Exchange称为死信交换机(DLX)、Queue就是死信队列
1.3、死信队列处理流程
- 死信消息实际上就是来设置队列的属性(配置死信队列)
- 队列中若有死信消息,RabbitMQ会自动将这个消息重新发布到设置的Exchange上,进而路由到另外一个队列。
- 监听这个队列中的消息做处理(处理死信队列)
1.4、配置死信队列
- 配置业务队列,绑定到业务交换机上
- 为业务队列配置死信交换机和路由key
- 为死信交换机配置死信队列
1.5、死信交换机的声明周期
- 业务消息被投入业务队列
- 消费者消费业务队列的消息,由于处理过程中发生异常,于是进行了nck或者reject操作
- 被nck或reject的消息由RabbitMQ投递到死信交换机中
- 死信交换机将消息投入相应的死信队列
- 死信队列的消费者消费死信消息
2、实例代码(boot-rabbit)
2.1、pom.xml
org.springframework.boot
spring-boot-starter-amqp
org.springframework.boot
spring-boot-starter-web
org.springframework.boot
spring-boot-starter-test
test
org.springframework.amqp
spring-rabbit-test
test
2.2、application.properties
spring.application.name=springboot-rabbitmq
server.port=8080
#默认地址就是127.0.0.1:5672,如果是服务器的rabbitmq就改下
spring.rabbitmq.host=192.168.174.130
spring.rabbitmq.port=5672
spring.rabbitmq.username=admin
spring.rabbitmq.password=admin
spring.rabbitmq.listener.type=simple
#设置为false,会丢弃消息或者重新发步到死信队列
spring.rabbitmq.listener.simple.default-requeue-rejected=false
#手动签收
spring.rabbitmq.listener.simple.acknowledge-mode=manual
#虚拟主机目录
spring.rabbitmq.virtual-host=/
2.3、RabbitMQConfig
@Configuration
public class RabbitMQconfig {
/**
* //业务Exchange
*/ public static final String BUSINESS_EXCHANGE_NAME="dead.letter.demo.simple.business.exchange";
/**
* //两个业务队列
*/
public static final String BUSINESS_QUEUEA_NAME="dead.letter.demo.simple.business.queuea";
public static final String BUSINESS_QUEUEB_NAME="dead.letter.demo.simple.business.queueb";
/**
* 死信Exchange
*/ public static final String DEAD_LETTER_EXCHANGE="dead.letter.demo.simple.deadletter.exchange";
/**
* 路由key
*/ public static final String DEAD_LETTER_QUEUEA_ROUTING_KEY="dead.letter.demo.simple.deadletter.queuea";
public static final String DEAD_LETTER_QUEUEB_ROUTING_KEY="dead.letter.demo.simple.deadletter.queueb";
/**
* 两个死信队列
*/
public static final String DEAD_LETTER_QUEUEA_NAME="dead.letter.demo.simple.deadletter.queuea";
public static final String DEAD_LETTER_QUEUEB_NAME="dead.letter.demo.simple.deadletter.queueb";
/**
* 声明业务Exchange
*/ @Bean("businessExchange")
public FanoutExchange businessExchange(){
//广播模式
return new FanoutExchange((BUSINESS_EXCHANGE_NAME));
}
/**
* 声明死信Exchange
*/ @Bean("deadLetterExchange")
public DirectExchange deadLetterExchange(){
//点对点模式模式
return new DirectExchange((DEAD_LETTER_EXCHANGE));
}
/**
* 声明业务队列A
* @return
*/
@Bean("businessQueueA")
public Queue businessQueueA(){
HashMap map = new HashMap<>(2);
map.put("x-dead-letter-exchange",DEAD_LETTER_EXCHANGE);
map.put("x-dead-letter-routing-key",DEAD_LETTER_QUEUEA_ROUTING_KEY);
return QueueBuilder.durable(BUSINESS_QUEUEA_NAME).withArguments(map).build();
}
/**
* 声明业务队列B
* @return
*/
@Bean("businessQueueB")
public Queue businessQueueB(){
HashMap map = new HashMap<>(2);
map.put("x-dead-letter-exchange",DEAD_LETTER_EXCHANGE);
map.put("x-dead-letter-routing-key",DEAD_LETTER_QUEUEB_ROUTING_KEY);
return QueueBuilder.durable(BUSINESS_QUEUEB_NAME).withArguments(map).build();
}
/**
* 声明死信队列A
*/ @Bean("deadLetterQueueA")
public Queue deadLetterQueueA(){
return new Queue(DEAD_LETTER_QUEUEA_NAME);
}
/**
* 声明死信队列B
*/ @Bean("deadLetterQueueB")
public Queue deadLetterQueueB(){
return new Queue(DEAD_LETTER_QUEUEB_NAME);
}
/**
* 声明业务队列A绑定关系
*/
@Bean
public Binding businessBindingA(@Qualifier("businessQueueA") Queue queue,
@Qualifier("businessExchange")FanoutExchange exchange){
return BindingBuilder.bind(queue).to(exchange);
}
/**
* 声明业务队列B绑定关系
*/
@Bean
public Binding businessBindingB(@Qualifier("businessQueueB") Queue queue,
@Qualifier("businessExchange")FanoutExchange exchange){
return BindingBuilder.bind(queue).to(exchange);
}
/**
* 声明死信队列A绑定关系
*/
@Bean
public Binding deadLetterBindingA(@Qualifier("deadLetterQueueA")Queue queue,
@Qualifier("deadLetterExchange")DirectExchange exchange) {
return BindingBuilder.bind(queue).to(exchange).with(DEAD_LETTER_QUEUEA_ROUTING_KEY);
}
/**
* 声明死信队列B绑定关系
*/
@Bean
public Binding deadLetterBindingB(@Qualifier("deadLetterQueueB")Queue queue,
@Qualifier("deadLetterExchange")DirectExchange exchange) {
return BindingBuilder.bind(queue).to(exchange).with(DEAD_LETTER_QUEUEB_ROUTING_KEY);
}
}
2.4、controller(MQ生产者)
@RestController
public class SendMessageController {
@Autowired
private RabbitMQconfig rabbitMQconfig;
@Autowired
private RabbitTemplate rabbitTemplate;
public static final String BUSINESS_EXCHANGE_NAME="dead.letter.demo.simple.business.exchange";
@GetMapping("/sendMsg")
public String sendMsg(String msg){
System.out.println("msg{}"+msg);
rabbitTemplate.convertAndSend(BUSINESS_EXCHANGE_NAME,"",msg);
return "success";
}
}
2.5、业务消息消费者
@Component
public class BusinessMessageReceiver {
/**
* //两个业务队列
*/
public static final String BUSINESS_QUEUEA_NAME="dead.letter.demo.simple.business.queuea";
public static final String BUSINESS_QUEUEB_NAME="dead.letter.demo.simple.business.queueb";
/**
* 消费消息
*/
@RabbitListener(queues = BUSINESS_QUEUEA_NAME)
public void receiveA(Message message, Channel channel) throws IOException {
String msg=new String(message.getBody());
System.out.println("BusinessMessageA{}"+msg);
boolean ack=true;
Exception exception=null;
try {
if (msg.contains("deadletter")){
throw new RuntimeException("dead letter exception");
}
} catch (RuntimeException e) {
ack=false;
exception=e;
}
if (!ack){
System.out.println("error msg{ }"+exception.getMessage());
//设置死信消息
channel.basicNack(message.getMessageProperties().getDeliveryTag(),false,false);
}else {
channel.basicAck(message.getMessageProperties().getDeliveryTag(),false);
}
}
/**
* 消息信息B
*/ @RabbitListener(queues = BUSINESS_QUEUEB_NAME)
public void receiveB(Message message,Channel channel) throws IOException {
String msg = new String(message.getBody());
System.out.println("BusinessMessageB{ }"+msg);
channel.basicAck(message.getMessageProperties().getDeliveryTag(),false);
}
}
2.6、死信消息消费者
@Component
public class DeadMessageReceiver {
/**
* 两个死信队列
*/
public static final String DEAD_LETTER_QUEUEA_NAME="dead.letter.demo.simple.deadletter.queuea";
public static final String DEAD_LETTER_QUEUEB_NAME="dead.letter.demo.simple.deadletter.queueb";
@RabbitListener(queues = DEAD_LETTER_QUEUEA_NAME)
public void receiveA(Message message, Channel channel) throws IOException {
System.out.println("DeadMessageA{}"+new String(message.getBody()));
channel.basicAck(message.getMessageProperties().getDeliveryTag(),false);
}
@RabbitListener(queues = DEAD_LETTER_QUEUEB_NAME)
public void receiveB(Message message, Channel channel) throws IOException {
System.out.println("DeadMessageB{}"+new String(message.getBody()));
channel.basicAck(message.getMessageProperties().getDeliveryTag(),false);
}
}