下面给你一套 SpringBoot + RabbitMQ 死信队列(DLX)完整可运行代码,包含:
- 普通业务队列
- 死信交换机 + 死信队列
- 消息过期/拒绝自动进死信
- 生产者 + 消费者示例
环境:SpringBoot 2.7+/3.x + AMQP Starter
1. pom.xml 依赖
xml
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-amqp</artifactId>
</dependency>
2. application.yml
yaml
spring:
rabbitmq:
host: localhost
port: 5672
username: guest
password: guest
listener:
simple:
acknowledge-mode: manual # 手动ack
3. RabbitMQ 配置类(核心)
RabbitMQConfig.java
java
import org.springframework.amqp.core.*;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* 死信队列 DLX 配置
*/
@Configuration
public class RabbitMQConfig {
// ==================== 死信交换机、队列 ====================
public static final String DLX_EXCHANGE = "dlx.exchange";
public static final String DLX_QUEUE = "dlx.queue";
public static final String DLX_ROUTING_KEY = "dlx.routing.key";
// 死信交换机
@Bean
public DirectExchange dlxExchange() {
return new DirectExchange(DLX_EXCHANGE);
}
// 死信队列
@Bean
public Queue dlxQueue() {
return QueueBuilder.durable(DLX_QUEUE).build();
}
// 绑定
@Bean
public Binding dlxBinding() {
return BindingBuilder.bind(dlxQueue())
.to(dlxExchange())
.with(DLX_ROUTING_KEY);
}
// ==================== 业务普通队列(绑定死信) ====================
public static final String BUSINESS_EXCHANGE = "business.exchange";
public static final String BUSINESS_QUEUE = "business.queue";
public static final String BUSINESS_ROUTING_KEY = "business.routing.key";
@Bean
public DirectExchange businessExchange() {
return new DirectExchange(BUSINESS_EXCHANGE);
}
// 业务队列:绑定死信交换机
@Bean
public Queue businessQueue() {
return QueueBuilder.durable(BUSINESS_QUEUE)
// 死信交换机
.withArgument("x-dead-letter-exchange", DLX_EXCHANGE)
// 死信路由key
.withArgument("x-dead-letter-routing-key", DLX_ROUTING_KEY)
// 可选:设置队列消息TTL(演示过期进死信)
// .withArgument("x-message-ttl", 10000)
.build();
}
@Bean
public Binding businessBinding() {
return BindingBuilder.bind(businessQueue())
.to(businessExchange())
.with(BUSINESS_ROUTING_KEY);
}
}
4. 生产者(发送消息)
DlxProducer.java
java
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import static com.example.config.RabbitMQConfig.*;
@Component
public class DlxProducer {
@Resource
private RabbitTemplate rabbitTemplate;
/**
* 发送普通消息
*/
public void sendMsg(String msg) {
rabbitTemplate.convertAndSend(
BUSINESS_EXCHANGE,
BUSINESS_ROUTING_KEY,
msg
);
}
/**
* 发送带过期时间的消息(自动进死信)
*/
public void sendExpireMsg(String msg, int ttl) {
rabbitTemplate.convertAndSend(
BUSINESS_EXCHANGE,
BUSINESS_ROUTING_KEY,
msg,
message -> {
// 设置消息TTL
message.getMessageProperties().setExpiration(String.valueOf(ttl));
return message;
}
);
}
}
5. 业务消费者(手动拒绝/失败进死信)
BusinessConsumer.java
java
import com.rabbitmq.client.Channel;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.amqp.support.AmqpHeaders;
import org.springframework.messaging.handler.annotation.Header;
import org.springframework.stereotype.Component;
import static com.example.config.RabbitMQConfig.BUSINESS_QUEUE;
@Component
public class BusinessConsumer {
@RabbitListener(queues = BUSINESS_QUEUE)
public void consume(String msg,
Channel channel,
@Header(AmqpHeaders.DELIVERY_TAG) long tag) throws Exception {
System.out.println("收到业务消息:" + msg);
try {
// 模拟业务异常
// int i = 1 / 0;
// 正常确认
channel.basicAck(tag, false);
System.out.println("正常消费成功");
} catch (Exception e) {
// 拒绝,并且不重回队列 → 进入死信
channel.basicNack(tag, false, false);
System.out.println("消费失败,进入死信队列");
}
}
}
6. 死信队列消费者
DlxConsumer.java
java
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;
import static com.example.config.RabbitMQConfig.DLX_QUEUE;
@Component
public class DlxConsumer {
@RabbitListener(queues = DLX_QUEUE)
public void consumeDlx(String msg) {
System.err.println("【死信队列收到】:" + msg);
// 在这里做:重试、记录日志、人工介入、补偿任务
}
}
7. 测试类
java
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
import javax.annotation.Resource;
@SpringBootTest
public class DlxTest {
@Resource
private DlxProducer producer;
@Test
public void test() {
// 发送普通消息(消费异常会进死信)
producer.sendMsg("测试死信消息");
// 发送5秒过期消息(过期自动进死信)
// producer.sendExpireMsg("5秒后过期进死信", 5000);
}
}
8. 消息变成死信的 3 种场景
- 消息TTL过期
- 消费者 basicNack/reject 且 requeue=false
- 队列满了溢出
满足任意一个,都会自动进入你配置的 DLX 死信队列。