- 原生 RabbitMQ Client(最基础)
- Spring Boot + RabbitMQ(企业实际开发 99% 用这个)
一、前提
安装并启动 RabbitMQ,开启:5672 端口。
二、Spring Boot 方式(推荐,企业真实用法)
1. pom 依赖
XML
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-amqp</artifactId>
</dependency>
2. application.yml
XML
spring:
rabbitmq:
host: 127.0.0.1
port: 5672
username: guest
password: guest
virtual-host: /
3. 配置类(交换机、队列、绑定)
java
import org.springframework.amqp.core.*;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class RabbitConfig {
public static final String QUEUE = "test_queue";
public static final String EXCHANGE = "test_exchange";
public static final String ROUTING_KEY = "test.rk";
// 队列
@Bean
public Queue queue() {
return new Queue(QUEUE, true); // 持久化
}
// 交换机
@Bean
public DirectExchange exchange() {
return new DirectExchange(EXCHANGE);
}
// 绑定
@Bean
public Binding binding() {
return BindingBuilder.bind(queue())
.to(exchange())
.with(ROUTING_KEY);
}
}
4. 生产者(发送消息)
java
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
@Service
public class RabbitProducer {
@Resource
private RabbitTemplate rabbitTemplate;
public void send(String msg) {
rabbitTemplate.convertAndSend(
RabbitConfig.EXCHANGE,
RabbitConfig.ROUTING_KEY,
msg
);
System.out.println("发送成功:" + msg);
}
}
}
5. 消费者(监听消息)
java
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;
@Component
public class RabbitConsumer {
@RabbitListener(queues = RabbitConfig.QUEUE)
public void receive(String msg) {
System.out.println("收到消息:" + msg);
// 业务处理
}
}
使用:
java
@Autowired
RabbitProducer producer;
producer.send("Hello RabbitMQ!");
三、原生 Java Client(不依赖 Spring,底层原理)
1. 依赖
java
<dependency>
<groupId>com.rabbitmq</groupId>
<artifactId>amqp-client</artifactId>
<version>5.16.0</version>
</dependency>
2. 生产者
java
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
public class Producer {
private final static String QUEUE = "test_queue";
public static void main(String[] argv) throws Exception {
ConnectionFactory factory = new ConnectionFactory();
factory.setHost("localhost");
factory.setUsername("guest");
factory.setPassword("guest");
try (Connection connection = factory.newConnection();
Channel channel = connection.createChannel()) {
channel.queueDeclare(QUEUE, true, false, false, null);
String message = "原生客户端消息";
channel.basicPublish("", QUEUE, null, message.getBytes());
}
}
}
3. 消费者
java
import com.rabbitmq.client.*;
public class Consumer {
private final static String QUEUE = "test_queue";
public static void main(String[] argv) throws Exception {
ConnectionFactory factory = new ConnectionFactory();
factory.setHost("localhost");
Connection connection = factory.newConnection();
Channel channel = connection.createChannel();
channel.queueDeclare(QUEUE, true, false, false, null);
DeliverCallback deliverCallback = (consumerTag, delivery) -> {
String message = new String(delivery.getBody());
System.out.println("消费:" + message);
};
channel.basicConsume(QUEUE, true, deliverCallback, consumerTag -> {});
}
}
四、关键要点
- 消息确认机制
- 生产者:
publisher-confirm-type: correlated - 消费者:手动 ACK(
acknowledge-mode: manual)防止丢消息
- 生产者:
- 消息持久化:队列、消息都要持久化
- 预取计数 :
prefetch: 1防止消费者被打满 - 重试、死信队列(DLX) 处理失败消息
六、企业生产级、可直接上线的 RabbitMQ 全套可靠方案
消息不丢失 + 不重复消费 + 手动 ACK + 重试 + 死信队列 + 延迟队列 + 幂等 + 高可用配置 ,全部是Spring Boot完整可运行代码。
一、完整 pom.xml
java
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-amqp</artifactId>
</dependency>
二、application.yml(生产级配置)
java
spring:
rabbitmq:
host: 127.0.0.1
port: 5672
username: guest
password: guest
virtual-host: /
# 生产者确认
publisher-confirm-type: correlated # 开启confirm
publisher-returns: true # 开启return
template:
mandatory: true # 消息无法路由返回生产者
# 消费者配置(核心)
listener:
simple:
acknowledge-mode: manual # 手动ACK(关键!防丢消息)
prefetch: 1 # 限流,一次只推1条,处理完再推下一条
retry:
enabled: true # 开启本地重试
max-attempts: 3 # 最多重试3次
initial-interval: 1000 # 间隔1s
三、核心队列结构(业务队列 + 死信队列 + 延迟队列)
java
import org.springframework.amqp.core.*;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class RabbitMQConfig {
// ==================== 业务队列 ====================
public static final String BUSINESS_QUEUE = "business.queue";
public static final String BUSINESS_EXCHANGE = "business.exchange";
public static final String BUSINESS_ROUTING_KEY = "business.rk";
// ==================== 死信队列 ====================
public static final String DLX_QUEUE = "dlx.queue";
public static final String DLX_EXCHANGE = "dlx.exchange";
public static final String DLX_ROUTING_KEY = "dlx.rk";
// ==================== 延迟队列(可选) ====================
public static final String DELAY_QUEUE = "delay.queue";
public static final String DELAY_EXCHANGE = "delay.exchange";
public static final String DELAY_ROUTING_KEY = "delay.rk";
// -------------- 1. 业务队列 --------------
@Bean
public Queue businessQueue() {
return QueueBuilder.durable(BUSINESS_QUEUE)
// 绑定死信交换机
.deadLetterExchange(DLX_EXCHANGE)
.deadLetterRoutingKey(DLX_ROUTING_KEY)
// 消息10分钟过期(可选)
//.ttl(600000)
.build();
}
@Bean
public DirectExchange businessExchange() {
return new DirectExchange(BUSINESS_EXCHANGE, true, false);
}
@Bean
public Binding businessBinding() {
return BindingBuilder.bind(businessQueue())
.to(businessExchange())
.with(BUSINESS_ROUTING_KEY);
}
// -------------- 2. 死信队列 --------------
@Bean
public Queue dlxQueue() {
return QueueBuilder.durable(DLX_QUEUE).build();
}
@Bean
public DirectExchange dlxExchange() {
return new DirectExchange(DLX_EXCHANGE, true, false);
}
@Bean
public Binding dlxBinding() {
return BindingBuilder.bind(dlxQueue())
.to(dlxExchange())
.with(DLX_ROUTING_KEY);
}
// -------------- 3. 延迟队列(TTL + DLX 实现) --------------
@Bean
public Queue delayQueue() {
return QueueBuilder.durable(DELAY_QUEUE)
.deadLetterExchange(BUSINESS_EXCHANGE)
.deadLetterRoutingKey(BUSINESS_ROUTING_KEY)
.build();
}
@Bean
public DirectExchange delayExchange() {
return new DirectExchange(DELAY_EXCHANGE, true, false);
}
@Bean
public Binding delayBinding() {
return BindingBuilder.bind(delayQueue())
.to(delayExchange())
.with(DELAY_ROUTING_KEY);
}
}
四、生产者(带 Confirm + Return 可靠发送)
java
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.connection.CorrelationData;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.stereotype.Service;
import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import java.util.UUID;
@Slf4j
@Service
public class ReliableProducer implements RabbitTemplate.ConfirmCallback, RabbitTemplate.ReturnCallback {
@Resource
private RabbitTemplate rabbitTemplate;
@PostConstruct
public void init() {
rabbitTemplate.setConfirmCallback(this);
rabbitTemplate.setReturnCallback(this);
}
/**
* 发送普通业务消息
*/
public void sendBusinessMsg(String msg) {
CorrelationData correlationData = new CorrelationData(UUID.randomUUID().toString());
rabbitTemplate.convertAndSend(
RabbitMQConfig.BUSINESS_EXCHANGE,
RabbitMQConfig.BUSINESS_ROUTING_KEY,
msg,
correlationData
);
log.info("发送业务消息: {}", msg);
}
/**
* 发送延迟消息(例如 10s 后进入业务队列)
*/
public void sendDelayMsg(String msg, long delayMs) {
CorrelationData data = new CorrelationData(UUID.randomUUID().toString());
rabbitTemplate.convertAndSend(
RabbitMQConfig.DELAY_EXCHANGE,
RabbitMQConfig.DELAY_ROUTING_KEY,
msg,
message -> {
message.getMessageProperties().setExpiration(String.valueOf(delayMs));
return message;
},
data
);
log.info("发送延迟消息: {} 延迟: {}ms", msg, delayMs);
}
// ============ 生产者确认 Confirm ============
@Override
public void confirm(CorrelationData correlationData, boolean ack, String cause) {
String id = correlationData != null ? correlationData.getId() : "";
if (ack) {
log.info("消息已到达交换机: id={}", id);
} else {
log.error("消息未到达交换机: id={}, cause={}", id, cause);
// 可存入DB,定时任务重发
}
}
// ============ 消息无法路由 Return ============
@Override
public void returnedMessage(Message message, int replyCode, String replyText,
String exchange, String routingKey) {
log.error("消息无法路由: exchange={}, rk={}", exchange, routingKey);
// 处理路由失败:记录日志/补偿
}
}
五、消费者(手动 ACK + 重试 + 死信 + 幂等)
核心逻辑:
- 手动 ACK
- 业务异常:NACK 并且不重新入队 → 进入死信
- 消费成功:basicAck
- 消费失败:basicNack
- 幂等:用 Redis/DB 唯一 ID 防重复
java
import com.rabbitmq.client.Channel;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;
import java.nio.charset.StandardCharsets;
@Slf4j
@Component
public class ReliableConsumer {
// ==================== 业务队列消费 ====================
@RabbitListener(queues = RabbitMQConfig.BUSINESS_QUEUE)
public void businessConsumer(Message message, Channel channel) throws Exception {
long deliveryTag = message.getMessageProperties().getDeliveryTag();
String msg = new String(message.getBody(), StandardCharsets.UTF_8);
String msgId = message.getMessageProperties().getMessageId();
try {
// 1. 幂等判断(Redis 分布式锁)
// Boolean lock = redisTemplate.opsForValue().setIfAbsent("mq:msg:" + msgId, "1", 10, TimeUnit.MINUTES);
// if (lock == null || !lock) {
// channel.basicAck(deliveryTag, false);
// return;
// }
// 2. 业务处理
log.info("开始消费业务消息: {}", msg);
// 3. 成功:确认消息
channel.basicAck(deliveryTag, false);
log.info("消费成功 ACK");
} catch (Exception e) {
log.error("消费异常: {}", e.getMessage());
// 异常:拒绝消息,不重新入队 → 进入死信
channel.basicNack(deliveryTag, false, false);
// 如果想重试:channel.basicNack(deliveryTag, false, true);
}
}
// ==================== 死信队列消费(人工/自动修复) ====================
/**
* 死信队列消费(失败3次以上的消息都来这里)
* 用于:告警、人工处理、定时重刷
*/
@RabbitListener(queues = RabbitMQConfig.DLX_QUEUE)
public void dlxConsumer(Message message, Channel channel) throws Exception {
long deliveryTag = message.getMessageProperties().getDeliveryTag();
String msg = new String(message.getBody(), StandardCharsets.UTF_8);
log.error("死信队列收到失败消息: {}", msg);
// 处理策略:
// 1. 记录DB
// 2. 告警
// 3. 人工修复后重发
channel.basicAck(deliveryTag, false);
}
}
六、整套方案保证了什么?
1. 消息绝对不丢失(全链路可靠)
- 生产者:Confirm + Return 确保消息到交换机
- 队列:持久化 durable
- 消息:默认持久化
- 消费者:手动 ACK,处理完才确认
2. 消息不重复消费(幂等)
- 消息唯一 msgId
- Redis 分布式锁 / DB 唯一索引
- 消费前判断是否已处理
3. 失败消息不阻塞、不雪崩
- 异常直接进入死信队列
- 不会无限重试打崩服务
- 可监控死信,人工 / 自动重试
4. 延迟消息(订单超时、支付超时)
- TTL + DLX 实现标准延迟队列
- 不需要插件
5. 流量控制
prefetch: 1消费者限流- 防止消息瞬间打满内存
七、直接复制就能用的测试接口
java
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
@RestController
public class TestController {
@Resource
private ReliableProducer producer;
@GetMapping("/send")
public String send() {
producer.sendBusinessMsg("这是一条可靠消息");
return "发送成功";
}
@GetMapping("/delay")
public String delay() {
producer.sendDelayMsg("这是10秒后执行的延迟消息", 10000);
return "延迟发送成功";
}
}
八、MQ 死信消息自动重试策略
Spring Boot RabbitMQ 标准方案:自动重试 3 次 → 失败进入死信队列 ,无无限循环、无阻塞、可直接复制上线。
核心机制:
- Spring 本地重试(Retry) :只在消费者内存重试,不回 MQ
- 重试失败 → 抛出异常 → 消息进入死信队列(DLX)
- 不会重新入队、不会无限循环、不会爆 CPU
一、application.yml 固定配置
java
spring:
rabbitmq:
host: 127.0.0.1
port: 5672
username: guest
password: guest
virtual-host: /
# 生产者确认(可选但推荐)
publisher-confirm-type: correlated
publisher-returns: true
template:
mandatory: true
listener:
simple:
acknowledge-mode: auto # 重试策略必须用 auto!!
prefetch: 1 # 限流1条,保证稳定
retry:
enabled: true # 开启消费者本地重试
max-attempts: 3 # 最大重试 3 次(1正常+2重试)
initial-interval: 1000 # 第一次重试间隔 1s
multiplier: 2 # 间隔倍数 1s → 2s →4s
stateless: true # 无状态,必须true
二、队列配置(业务队列绑定死信)直接复制
java
import org.springframework.amqp.core.*;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class RabbitRetryAndDlxConfig {
// ====================== 业务队列 ======================
public static final String BUSINESS_QUEUE = "business.retry.queue";
public static final String BUSINESS_EXCHANGE = "business.retry.exchange";
public static final String BUSINESS_ROUTING_KEY = "business.retry.rk";
// ====================== 死信队列 ======================
public static final String DLX_QUEUE = "business.dlx.queue";
public static final String DLX_EXCHANGE = "business.dlx.exchange";
public static final String DLX_ROUTING_KEY = "business.dlx.rk";
// ============== 业务队列(绑定死信)==============
@Bean
public Queue businessQueue() {
return QueueBuilder.durable(BUSINESS_QUEUE)
.deadLetterExchange(DLX_EXCHANGE)
.deadLetterRoutingKey(DLX_ROUTING_KEY)
.build();
}
@Bean
public DirectExchange businessExchange() {
return new DirectExchange(BUSINESS_EXCHANGE);
}
@Bean
public Binding businessBinding() {
return BindingBuilder.bind(businessQueue())
.to(businessExchange())
.with(BUSINESS_ROUTING_KEY);
}
// ============== 死信队列 ==============
@Bean
public Queue dlxQueue() {
return QueueBuilder.durable(DLX_QUEUE).build();
}
@Bean
public DirectExchange dlxExchange() {
return new DirectExchange(DLX_EXCHANGE);
}
@Bean
public Binding dlxBinding() {
return BindingBuilder.bind(dlxQueue())
.to(dlxExchange())
.with(DLX_ROUTING_KEY);
}
}
三、消费者(自动重试 3 次,失败进死信)
java
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;
@Slf4j
@Component
public class RetryConsumer {
/**
* 业务队列消费
* 规则:
* 1. 抛异常 → Spring自动重试3次
* 2. 3次都失败 → 自动进入死信队列
*/
@RabbitListener(queues = RabbitRetryAndDlxConfig.BUSINESS_QUEUE)
public void businessConsume(String msg) {
log.info("开始消费消息:{}", msg);
try {
// 你的业务逻辑
// int i = 1 / 0; // 模拟异常
} catch (Exception e) {
log.error("消费失败,准备重试,异常:{}", e.getMessage());
// 只需要抛出异常,Spring 会自动重试,重试完自动进死信
throw e;
}
}
/**
* 死信队列消费(失败3次以上的消息都来这里)
* 用于:告警、人工处理、定时重刷
*/
@RabbitListener(queues = RabbitRetryAndDlxConfig.DLX_QUEUE)
public void dlxConsume(String msg) {
log.error("【死信队列】消费失败3次以上,请处理:{}", msg);
// 1. 存入DB
// 2. 发送告警(钉钉/企业微信)
// 3. 人工修复
}
}
- 第一次消费失败 → 抛异常
- 间隔 1s 重试第 2 次
- 间隔 2s 重试第 3 次
- 总共 3 次都失败
- Spring 拒绝该消息 → 自动进入死信队列
- 不会回队列、不会循环、不占 CPU、不阻塞