SpringBoot + RabbitMQ 整合配置与案例详解
一、环境准备
1. 安装RabbitMQ
使用Docker安装RabbitMQ(推荐):
docker run -d --name rabbitmq -p 5672:5672 -p 15672:15672 -e RABBITMQ_DEFAULT_USER=admin -e RABBITMQ_DEFAULT_PASS=admin rabbitmq:management
2. 创建SpringBoot项目
使用Spring Initializer创建项目,选择以下依赖:
- Spring Web
- Spring for RabbitMQ
二、基础配置
1. Maven依赖(pom.xml)
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-amqp</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
2. 配置文件(application.yml)
spring:
rabbitmq:
host: localhost
port: 5672
username: admin
password: admin
virtual-host: /
# 生产者确认配置
publisher-confirm-type: correlated # 开启发送确认机制
publisher-returns: true # 开启发送失败退回机制
template:
mandatory: true # 设置交换机处理失败消息的模式
# 消费者配置
listener:
simple:
acknowledge-mode: manual # 手动确认模式
prefetch: 1 # 每次只处理一条消息
concurrency: 1 # 最小并发消费者数量
max-concurrency: 5 # 最大并发消费者数量
三、基础功能实现
1. 交换机和队列配置类
package com.example.rabbitmq.config;
import org.springframework.amqp.core.*;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class RabbitMQConfig {
// 交换机名称
public static final String BOOT_EXCHANGE = "boot_exchange";
// 队列名称
public static final String BOOT_QUEUE = "boot_queue";
// 路由键
public static final String BOOT_ROUTING_KEY = "boot.info";
// 声明交换机
@Bean("bootExchange")
public Exchange bootExchange() {
return ExchangeBuilder.directExchange(BOOT_EXCHANGE)
.durable(true) // 持久化
.build();
}
// 声明队列
@Bean("bootQueue")
public Queue bootQueue() {
return QueueBuilder.durable(BOOT_QUEUE)
.build();
}
// 绑定交换机和队列
@Bean
public Binding bindQueueExchange(
@Qualifier("bootQueue") Queue queue,
@Qualifier("bootExchange") Exchange exchange) {
return BindingBuilder.bind(queue)
.to(exchange)
.with(BOOT_ROUTING_KEY)
.noargs();
}
}
2. 生产者(消息发送)
2.1 配置RabbitTemplate
package com.example.rabbitmq.config;
import org.springframework.amqp.rabbit.connection.ConnectionFactory;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class RabbitTemplateConfig {
@Bean
public RabbitTemplate rabbitTemplate(ConnectionFactory connectionFactory) {
RabbitTemplate rabbitTemplate = new RabbitTemplate(connectionFactory);
// 设置确认回调
rabbitTemplate.setConfirmCallback((correlationData, ack, cause) -> {
if (ack) {
System.out.println("消息发送成功,correlationId: " + correlationData.getId());
} else {
System.out.println("消息发送失败,原因: " + cause);
// 可以在这里进行重试或记录日志
}
});
// 设置返回回调
rabbitTemplate.setReturnCallback((message, replyCode, replyText, exchange, routingKey) -> {
System.out.println("消息投递到队列失败!");
System.out.println("消息: " + new String(message.getBody()));
System.out.println("响应码: " + replyCode);
System.out.println("响应信息: " + replyText);
System.out.println("交换机: " + exchange);
System.out.println("路由键: " + routingKey);
});
return rabbitTemplate;
}
}
2.2 消息发送服务
package com.example.rabbitmq.service;
import com.example.rabbitmq.config.RabbitMQConfig;
import org.springframework.amqp.rabbit.connection.CorrelationData;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.UUID;
@Service
public class MessageService {
@Autowired
private RabbitTemplate rabbitTemplate;
// 发送消息
public void sendMessage(String message) {
// 生成消息ID
CorrelationData correlationData = new CorrelationData(UUID.randomUUID().toString());
System.out.println("发送消息: " + message + ",correlationId: " + correlationData.getId());
// 发送消息
rabbitTemplate.convertAndSend(
RabbitMQConfig.BOOT_EXCHANGE, // 交换机
RabbitMQConfig.BOOT_ROUTING_KEY, // 路由键
message, // 消息内容
correlationData // 关联数据
);
}
}
2.3 控制器
package com.example.rabbitmq.controller;
import com.example.rabbitmq.service.MessageService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class MessageController {
@Autowired
private MessageService messageService;
@GetMapping("/send")
public String sendMessage(@RequestParam String message) {
messageService.sendMessage(message);
return "消息发送成功";
}
}
3. 消费者(消息接收)
package com.example.rabbitmq.consumer;
import com.example.rabbitmq.config.RabbitMQConfig;
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;
@Component
public class MessageConsumer {
@RabbitListener(queues = RabbitMQConfig.BOOT_QUEUE)
public void receiveMessage(String message, Channel channel, Message msg) throws IOException {
try {
System.out.println("接收到消息: " + message);
// 处理业务逻辑
processMessage(message);
// 手动确认消息
channel.basicAck(msg.getMessageProperties().getDeliveryTag(), false);
} catch (Exception e) {
System.out.println("消息处理失败: " + e.getMessage());
// 拒绝消息并重新入队(可以根据业务需要选择是否重新入队)
channel.basicNack(msg.getMessageProperties().getDeliveryTag(), false, false);
}
}
private void processMessage(String message) {
// 模拟业务处理
System.out.println("处理消息中..." + message);
}
}
四、高级特性实现
1. 死信队列配置
package com.example.rabbitmq.config;
import org.springframework.amqp.core.*;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.HashMap;
import java.util.Map;
@Configuration
public class DeadLetterConfig {
// 普通交换机
public static final String NORMAL_EXCHANGE = "normal_exchange";
// 普通队列
public static final String NORMAL_QUEUE = "normal_queue";
// 死信交换机
public static final String DEAD_EXCHANGE = "dead_exchange";
// 死信队列
public static final String DEAD_QUEUE = "dead_queue";
// 路由键
public static final String NORMAL_ROUTING_KEY = "normal.key";
public static final String DEAD_ROUTING_KEY = "dead.key";
// 声明死信交换机
@Bean
public Exchange deadExchange() {
return ExchangeBuilder.directExchange(DEAD_EXCHANGE).durable(true).build();
}
// 声明死信队列
@Bean
public Queue deadQueue() {
return QueueBuilder.durable(DEAD_QUEUE).build();
}
// 声明普通交换机
@Bean
public Exchange normalExchange() {
return ExchangeBuilder.directExchange(NORMAL_EXCHANGE).durable(true).build();
}
// 声明普通队列并绑定死信交换机
@Bean
public Queue normalQueue() {
Map<String, Object> args = new HashMap<>();
// 设置死信交换机
args.put("x-dead-letter-exchange", DEAD_EXCHANGE);
// 设置死信路由键
args.put("x-dead-letter-routing-key", DEAD_ROUTING_KEY);
// 设置队列消息过期时间(10秒)
args.put("x-message-ttl", 10000);
// 设置队列最大长度
args.put("x-max-length", 10);
return QueueBuilder.durable(NORMAL_QUEUE).withArguments(args).build();
}
// 绑定普通队列和普通交换机
@Bean
public Binding normalBinding() {
return BindingBuilder.bind(normalQueue()).to(normalExchange()).with(NORMAL_ROUTING_KEY).noargs();
}
// 绑定死信队列和死信交换机
@Bean
public Binding deadBinding() {
return BindingBuilder.bind(deadQueue()).to(deadExchange()).with(DEAD_ROUTING_KEY).noargs();
}
}
2. 延迟队列实现(基于死信队列)
2.1 发送延迟消息
@Service
public class DelayMessageService {
@Autowired
private RabbitTemplate rabbitTemplate;
// 发送延迟消息
public void sendDelayMessage(String message, long delayMillis) {
// 创建消息属性
MessageProperties properties = new MessageProperties();
// 设置消息过期时间
properties.setExpiration(String.valueOf(delayMillis));
// 创建消息
Message msg = new Message(message.getBytes(), properties);
System.out.println("发送延迟消息: " + message + ",延迟时间: " + delayMillis + "ms");
// 发送到普通队列
rabbitTemplate.send(DeadLetterConfig.NORMAL_EXCHANGE, DeadLetterConfig.NORMAL_ROUTING_KEY, msg);
}
}
2.2 死信队列消费者
@Component
public class DeadLetterConsumer {
@RabbitListener(queues = DeadLetterConfig.DEAD_QUEUE)
public void receiveDeadLetter(String message, Channel channel, Message msg) throws IOException {
try {
System.out.println("接收到死信消息: " + message + ",时间: " + new Date());
// 处理死信消息业务逻辑
processDeadLetter(message);
// 手动确认
channel.basicAck(msg.getMessageProperties().getDeliveryTag(), false);
} catch (Exception e) {
System.out.println("死信消息处理失败: " + e.getMessage());
channel.basicNack(msg.getMessageProperties().getDeliveryTag(), false, false);
}
}
private void processDeadLetter(String message) {
// 模拟业务处理
System.out.println("处理死信消息中..." + message);
}
}
五、消息可靠性保障
1. 生产者端保障
- 确认机制(Publisher Confirms):确保消息到达交换机
- 退回机制(Publisher Returns):确保消息从交换机路由到队列
- 事务机制:通过事务确保消息发送和业务操作的原子性(性能较低,不推荐)
- 本地消息表:将消息持久化到数据库,通过定时任务重试发送失败的消息
2. 服务端保障
- 交换机持久化:
durable=true - 队列持久化:
durable=true - 消息持久化:设置消息投递模式为2(
MessageProperties.PERSISTENT_TEXT_PLAIN) - 高可用集群:部署RabbitMQ集群,避免单点故障
3. 消费者端保障
- 手动确认:
acknowledge-mode: manual,处理完业务后再确认 - 限流:通过
prefetch控制消费者一次性获取的消息数量 - 幂等性处理:确保重复消费不会导致业务异常
六、测试接口
1. 测试基础消息发送
GET http://localhost:8080/send?message=Hello%20RabbitMQ
2. 测试延迟消息(需要添加对应的Controller)
@GetMapping("/sendDelay")
public String sendDelayMessage(@RequestParam String message, @RequestParam long delayMillis) {
delayMessageService.sendDelayMessage(message, delayMillis);
return "延迟消息发送成功,延迟时间:" + delayMillis + "ms";
}
七、常见问题及解决方案
1. 消息丢失
- 检查确认机制是否开启
- 确认交换机、队列、消息是否都设置了持久化
- 检查消费者是否正确确认消息
2. 消息重复消费
- 实现幂等性处理(如基于消息ID去重)
- 使用Redis或数据库记录已处理的消息ID
3. 消息积压
- 增加消费者数量(调整concurrency参数)
- 优化消费者处理逻辑,提高消费速度
- 设置合适的预取数量(prefetch)
4. 死信队列不生效
- 检查TTL设置是否正确
- 确认死信交换机和路由键配置正确
- 确保队列没有消费者
八、启动类
package com.example.rabbitmq;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class RabbitmqApplication {
public static void main(String[] args) {
SpringApplication.run(RabbitmqApplication.class, args);
}
}
通过以上配置和代码,您可以实现一个功能完整的SpringBoot + RabbitMQ应用,包括基础的消息发送接收、可靠性保障、死信队列和延迟队列等高级特性。