如何配置死信队列
配置业务队列,绑定到业务交换机上
为业务队列配置死信交换机和路由key
为死信交换机配置死信队列

死信队列的应用场景
通过上面的信息,我们已经知道如何使用死信队列了,那么死信队列一般在什么场景下使用呢?

一般用在较为重要的业务队列中,确保未被正确消费的消息不被丢弃,一般发生消费异常可能原因主要有由于消息信息本身存在错误导致处理异常,处理过程中参数校验异常,或者因网络波动导致的查询异常等等,当发生异常时,当然不能每次通过日志来获取原消息,然后让运维帮忙重新投递消息(没错,以前就是这么干的= =)。通过配置死信队列,可以让未正确处理的消息暂存到另一个队列中,待后续排查清楚问题后,编写相应的处理代码来处理死信消息,这样比手工恢复数据要好太多了。

#配置rabbitmq服务器的ip地址
spring.rabbitmq.host=192.168.200.106
#rabbitmq的端口号
spring.rabbitmq.port=5672
#rabbitmq的账号
spring.rabbitmq.username=root
#rabbitmq的密码
spring.rabbitmq.password=root
#rabbitmq的虚拟机
spring.rabbitmq.virtual-host=/
#发送确认
#NONE 禁用发布模式,是默认值
#CORRELATED 发布消息成功到交换机后会触发回调方法,交互
#SIMPLE
spring.rabbitmq.publisher-confirm-type=correlated
#路由失败回调
spring.rabbitmq.publisher-returns=true
#true消息路由失败通知监听者,false将消息丢弃
spring.rabbitmq.template.mandatory=true

spring.rabbitmq.listener.type=simple
spring.rabbitmq.listener.simple.acknowledge-mode=manual
spring.rabbitmq.listener.simple.default-requeue-rejected=false

rabbitmq的注入配置

package com.qiangqiang.RabbitMQ.dead_confirm;

import org.springframework.amqp.core.*;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Component;

import java.util.HashMap;
import java.util.Map;

/**
* \* Created with IntelliJ IDEA.
* \* @author: xiyue
* \* Date: 2020/12/10
* \* Time: 11:22
* \* To change this template use File | Settings | File Templates.
* \* Description:
* \
*/
@Component
public class RabbitMQConfig {
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";
public static final String DEAD_LETTER_EXCHANGE = "dead.letter.demo.simple.deadletter.exchange";
public static final String DEAD_LETTER_QUEUEA_ROUTING_KEY = "dead.letter.demo.simple.deadletter.queuea.routingkey";
public static final String DEAD_LETTER_QUEUEB_ROUTING_KEY = "dead.letter.demo.simple.deadletter.queueb.routingkey";
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
@Bean("businessQueueA")
public Queue businessQueueA(){
Map<String, Object> args = new HashMap<>(2);
//x-dead-letter-exchange 这里声明当前队列绑定的死信交换机
args.put("x-dead-letter-exchange", DEAD_LETTER_EXCHANGE);
//x-dead-letter-routing-key 这里声明当前队列的死信路由key
args.put("x-dead-letter-routing-key", DEAD_LETTER_QUEUEA_ROUTING_KEY);
return QueueBuilder.durable(BUSINESS_QUEUEA_NAME).withArguments(args).build();
}

// 声明业务队列B
@Bean("businessQueueB")
public Queue businessQueueB(){
Map<String, Object> args = new HashMap<>(2);
//x-dead-letter-exchange 这里声明当前队列绑定的死信交换机
args.put("x-dead-letter-exchange", DEAD_LETTER_EXCHANGE);
//x-dead-letter-routing-key 这里声明当前队列的死信路由key
args.put("x-dead-letter-routing-key", DEAD_LETTER_QUEUEB_ROUTING_KEY);
return QueueBuilder.durable(BUSINESS_QUEUEB_NAME).withArguments(args).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);
}

}

消费者

package com.qiangqiang.RabbitMQ.dead_confirm;

import com.rabbitmq.client.Channel;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;

import java.io.IOException;

import static com.qiangqiang.RabbitMQ.dead_confirm.RabbitMQConfig.BUSINESS_QUEUEA_NAME;
import static com.qiangqiang.RabbitMQ.dead_confirm.RabbitMQConfig.BUSINESS_QUEUEB_NAME;

/**
* \* Created with IntelliJ IDEA.
* \* @author: xiyue
* \* Date: 2020/12/10
* \* Time: 11:50
* \* To change this template use File | Settings | File Templates.
* \* Description:
* \
*/
@Component
public class BusinessMessageReceiver {

@RabbitListener(queues = BUSINESS_QUEUEA_NAME)
public void receiveA(Message message, Channel channel) throws IOException{
String msg = new String(message.getBody());
Boolean ack = true;

try {
if (msg.contains("deadletter")){
throw new RuntimeException("dead letter exception");
}
} catch (RuntimeException e) {
ack = false;
}
if(!ack){
System.out.println("A消息消费发生异常,error msg:{}");
//这里签收拒绝了.Nack可以一次拒绝接受多条消息,也可以设置是否扔回去原队列
channel.basicNack(message.getMessageProperties().getDeliveryTag(),false,false);
}else{
channel.basicAck(message.getMessageProperties().getDeliveryTag(),false);
}
}

@RabbitListener(queues = BUSINESS_QUEUEB_NAME)
public void receiveB(Message message, Channel channel) throws IOException {
System.out.println("收到业务消息B:" + new String(message.getBody()));
channel.basicAck(message.getMessageProperties().getDeliveryTag(), false);
}


}

死信队列配置

package com.qiangqiang.RabbitMQ.dead_confirm;

import com.rabbitmq.client.Channel;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;

import java.io.IOException;

import static com.qiangqiang.RabbitMQ.dead_confirm.RabbitMQConfig.DEAD_LETTER_QUEUEA_NAME;
import static com.qiangqiang.RabbitMQ.dead_confirm.RabbitMQConfig.DEAD_LETTER_QUEUEB_NAME;

@Component
public class DeadLetterMessageReceiver {


@RabbitListener(queues = DEAD_LETTER_QUEUEA_NAME)
public void receiveA(Message message, Channel channel) throws IOException {
System.out.println("收到死信消息A:" + new String(message.getBody()));
System.out.println("死信消息A"+message.getMessageProperties());
channel.basicAck(message.getMessageProperties().getDeliveryTag(), false);
}

@RabbitListener(queues = DEAD_LETTER_QUEUEB_NAME)
public void receiveB(Message message, Channel channel) throws IOException {
System.out.println("收到死信消息B:" + new String(message.getBody()));
channel.basicAck(message.getMessageProperties().getDeliveryTag(), false);
}
}

Controller层发送消息

package com.qiangqiang.RabbitMQ.dead_confirm;

import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import static com.qiangqiang.RabbitMQ.dead_confirm.RabbitMQConfig.BUSINESS_EXCHANGE_NAME;

@RequestMapping("rabbitmq")
@RestController
public class RabbitMQMsgController {

@Autowired
private RabbitTemplate rabbitTemplate;

@RequestMapping("sendmsg")
public void sendMsg(String msg){
rabbitTemplate.convertSendAndReceive(BUSINESS_EXCHANGE_NAME,"",msg);
}
}