死信队列其实和普通队列一样,只不过是被正常的业务队列声明并绑定为死信队列,我们可以理解为正常业务队列的助理队列。当正常业务队列的消息在被消费的过程中,如果发生异常,那么此消息需要被记录到日志表中,这个时候可以给业务队列声明绑定一个死信队列,那么这个异常消息可以被路由到死信队列,然后死信队列的消费者监听器可以把死信队列中的消息写到日志表中记录。
看代码:
以下代码为生产者端:
@Configuration
public class RabbitmqConfig {
//业务交换机
@Bean
public DirectExchange defaultExchange() {
return new DirectExchange("testExchange");
}
//业务队列
@Bean
public Queue queue() {
Map<String, Object> map = new HashMap<>();
//x-dead-letter-exchange 这里声明当前队列绑定的死信交换机
map.put("x-dead-letter-exchange", DEAD_LETTER_EXCHANGE);
//x-dead-letter-routing-key 这里声明当前队列的死信路由key
map.put("x-dead-letter-routing-key", DEAD_LETTER_QUEUEA_ROUTING_KEY);
return QueueBuilder.durable("jllqueue").withArguments(map).build();
}
//业务交换机和队列的绑定
@Bean
public Binding binding() {
return BindingBuilder.bind(queue()).to(defaultExchange()).with("testRoutingKey");
}
//-------------------------------以下为死信队列的配置-------------------------------------
String DEAD_LETTER_EXCHANGE = "deadExchange";
//死信交换机
@Bean
public DirectExchange deadExchange() {
return new DirectExchange(DEAD_LETTER_EXCHANGE);
}
//死信队列
@Bean
public Queue deadQueue() {
return new Queue("deadJllQueue", true);
}
String DEAD_LETTER_QUEUEA_ROUTING_KEY = "testDeadRoutingKey";
//私信队列绑定到私信交换机
@Bean
public Binding bindingDead() {
return BindingBuilder.bind(deadQueue()).to(deadExchange()).with(DEAD_LETTER_QUEUEA_ROUTING_KEY);
}
}
以下代码为消费者端:
@Configuration
public class RabbitmqConfig {
@Bean
public DirectRabbitListenerContainerFactory directRabbitListenerContainerFactory(ConnectionFactory connectionFactory){
DirectRabbitListenerContainerFactory directRabbitListenerContainerFactory = new DirectRabbitListenerContainerFactory();
directRabbitListenerContainerFactory.setConnectionFactory(connectionFactory);
//人工确认模式
directRabbitListenerContainerFactory.setAcknowledgeMode(AcknowledgeMode.MANUAL);
//消费异常拒绝重入队列,可以进入私信队列
directRabbitListenerContainerFactory.setDefaultRequeueRejected(false);
return directRabbitListenerContainerFactory;
}
}
/**
* 消费业务
*/
@Component
public class RabbitMqListener {
@RabbitListener(queues = "jllqueue",containerFactory = "directRabbitListenerContainerFactory")
public void consumer2(Message message, Channel channel) throws IOException {
try {
System.out.println("### consumer2-执行业务 ### "+message);
int i = 10/0;
} catch (Exception e) {
channel.basicNack(message.getMessageProperties().getDeliveryTag(),false,false);
return;
}
//手动确认,设置手动确认后,没有下面代码,那么队列中Unacked状态的数据一直存在,直到消费端服务断开时,这些数据会重新进入到Ready状态中重新消费,这样会导致重复消费
channel.basicAck(message.getMessageProperties().getDeliveryTag(),false);
}
@RabbitListener(queues = "deadJllQueue",containerFactory = "directRabbitListenerContainerFactory")
public void receiveB(Message message, Channel channel) throws IOException {
System.out.println("收到死信消息:" + new String(message.getBody()));
channel.basicAck(message.getMessageProperties().getDeliveryTag(), false);
}
}