🛠️ 一、准备工作(只需 3 分钟)
你需要:
- JDK 17
- IDEA
- RabbitMQ(本地或 Docker)
- 一个邮箱用于测试邮件发送
💡 没有 RabbitMQ? 用 Docker 一行命令启动:
docker run -d -p 5672:5672 -p 15672:15672 --name rabbitmq rabbitmq:3.11-management
💻 二、RabbitMQ 核心概念(简单理解)
三个核心组件:
- Producer(生产者):发送消息的程序
- Queue(队列):存储消息的地方
- Consumer(消费者):处理消息的程序
交换机类型(4 种):
- Direct:精确匹配路由键
- Topic:通配符匹配路由键
- Fanout:广播,忽略路由键
- Headers:根据消息头匹配
📦 三、Spring Boot 集成 RabbitMQ(完整代码)
步骤 1:添加依赖(pom.xml)
XML
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- RabbitMQ Starter -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-amqp</artifactId>
</dependency>
<!-- 邮件发送 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-mail</artifactId>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
</dependency>
</dependencies>
步骤 2:配置 RabbitMQ(application.yml)
XML
spring:
rabbitmq:
host: localhost
port: 5672
username: guest
password: guest
virtual-host: /
# 消息确认配置
publisher-confirm-type: correlated # 发送确认
publisher-returns: true # 消息返回
template:
mandatory: true # 强制确认
# 消费者配置
listener:
simple:
acknowledge-mode: manual # 手动确认
concurrency: 3 # 并发消费者数
max-concurrency: 10 # 最大并发数
# 邮件配置(测试用)
mail:
host: smtp.163.com
username: your-email@163.com
password: your-app-password # 邮箱应用密码
properties:
mail:
smtp:
auth: true
starttls:
enable: true
步骤 3:创建消息实体类(entity/OrderMessage.java)
java
package com.example.rabbitmqdemo.entity;
import java.io.Serializable;
public class OrderMessage implements Serializable {
private static final long serialVersionUID = 1L;
private Long orderId;
private String customerEmail;
private String productName;
private Double amount;
private String orderStatus;
// 构造函数(省略)
public OrderMessage() {}
public OrderMessage(Long orderId, String customerEmail, String productName, Double amount) {
this.orderId = orderId;
this.customerEmail = customerEmail;
this.productName = productName;
this.amount = amount;
this.orderStatus = "CREATED";
}
// getter/setter...
public Long getOrderId() { return orderId; }
public void setOrderId(Long orderId) { this.orderId = orderId; }
public String getCustomerEmail() { return customerEmail; }
public void setCustomerEmail(String customerEmail) { this.customerEmail = customerEmail; }
public String getProductName() { return productName; }
public void setProductName(String productName) { this.productName = productName; }
public Double getAmount() { return amount; }
public void setAmount(Double amount) { this.amount = amount; }
public String getOrderStatus() { return orderStatus; }
public void setOrderStatus(String orderStatus) { this.orderStatus = orderStatus; }
}
步骤 4:创建 RabbitMQ 配置(config/RabbitConfig.java)
java
package com.example.rabbitmqdemo.config;
import org.springframework.amqp.core.*;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class RabbitConfig {
@Value("${mq.exchange.order:order-exchange}")
private String orderExchange;
@Value("${mq.queue.order:order-queue}")
private String orderQueue;
@Value("${mq.routing-key.order:order.created}")
private String orderRoutingKey;
// 声明交换机
@Bean
public DirectExchange orderExchange() {
return new DirectExchange(orderExchange, true, false);
}
// 声明队列
@Bean
public Queue orderQueue() {
return QueueBuilder.durable(orderQueue).build();
}
// 绑定交换机和队列
@Bean
public Binding orderBinding() {
return BindingBuilder.bind(orderQueue())
.to(orderExchange())
.with(orderRoutingKey);
}
}
配置说明:
DirectExchange:直连交换机QueueBuilder.durable():持久化队列BindingBuilder:绑定关系
步骤 5:创建消息生产者(service/OrderProducer.java)
java
package com.example.rabbitmqdemo.service;
import com.example.rabbitmqdemo.entity.OrderMessage;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
@Service
public class OrderProducer {
@Autowired
private RabbitTemplate rabbitTemplate;
@Value("${mq.exchange.order:order-exchange}")
private String orderExchange;
@Value("${mq.routing-key.order:order.created}")
private String orderRoutingKey;
/**
* 发送订单创建消息
*/
public void sendOrderCreatedMessage(OrderMessage orderMessage) {
// 设置消息属性(可选)
rabbitTemplate.convertAndSend(orderExchange, orderRoutingKey, orderMessage);
System.out.println("订单消息已发送:" + orderMessage.getOrderId());
}
}
步骤 6:创建消息消费者(service/OrderConsumer.java)
java
package com.example.rabbitmqdemo.service;
import com.example.rabbitmqdemo.entity.OrderMessage;
import com.rabbitmq.client.Channel;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.amqp.support.AmqpHeaders;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.mail.SimpleMailMessage;
import org.springframework.mail.javamail.JavaMailSender;
import org.springframework.messaging.handler.annotation.Header;
import org.springframework.stereotype.Service;
import java.io.IOException;
@Service
public class OrderConsumer {
@Autowired
private JavaMailSender mailSender;
/**
* 订单创建消息消费者
*/
@RabbitListener(queues = "${mq.queue.order:order-queue}")
public void handleOrderCreated(OrderMessage orderMessage, Channel channel, @Header(AmqpHeaders.DELIVERY_TAG) long deliveryTag) {
try {
System.out.println("收到订单消息:" + orderMessage.getOrderId());
// 1. 发送订单确认邮件
sendOrderEmail(orderMessage);
// 2. 更新用户积分(这里简化)
updateUserPoints(orderMessage.getOrderId());
// 3. 记录日志
logOrderProcessed(orderMessage.getOrderId());
// 手动确认消息
channel.basicAck(deliveryTag, false);
System.out.println("订单消息处理完成:" + orderMessage.getOrderId());
} catch (Exception e) {
System.err.println("处理订单消息失败:" + e.getMessage());
try {
// 拒绝消息,重新入队(这里简化,实际项目可能需要死信队列)
channel.basicNack(deliveryTag, false, true);
} catch (IOException ioException) {
e.printStackTrace();
}
}
}
private void sendOrderEmail(OrderMessage orderMessage) {
try {
SimpleMailMessage message = new SimpleMailMessage();
message.setTo(orderMessage.getCustomerEmail());
message.setSubject("订单创建成功");
message.setText("您的订单 " + orderMessage.getOrderId() +
" 已创建成功,商品:" + orderMessage.getProductName() +
",金额:" + orderMessage.getAmount() + "元");
mailSender.send(message);
System.out.println("订单邮件已发送给:" + orderMessage.getCustomerEmail());
} catch (Exception e) {
System.err.println("发送邮件失败:" + e.getMessage());
}
}
private void updateUserPoints(Long orderId) {
// 模拟更新用户积分
System.out.println("为订单 " + orderId + " 更新用户积分");
}
private void logOrderProcessed(Long orderId) {
// 模拟记录处理日志
System.out.println("订单 " + orderId + " 处理日志已记录");
}
}
步骤 7:创建订单控制器(controller/OrderController.java)
java
package com.example.rabbitmqdemo.controller;
import com.example.rabbitmqdemo.entity.OrderMessage;
import com.example.rabbitmqdemo.service.OrderProducer;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.util.HashMap;
import java.util.Map;
@RestController
@RequestMapping("/api/orders")
public class OrderController {
@Autowired
private OrderProducer orderProducer;
/**
* 创建订单(异步处理)
*/
@PostMapping("/create")
public Map<String, Object> createOrder(@RequestParam String customerEmail,
@RequestParam String productName,
@RequestParam Double amount) {
// 1. 保存订单到数据库(这里简化)
Long orderId = System.currentTimeMillis(); // 模拟生成订单ID
// 2. 创建订单消息
OrderMessage orderMessage = new OrderMessage(orderId, customerEmail, productName, amount);
// 3. 发送消息到 RabbitMQ(异步)
orderProducer.sendOrderCreatedMessage(orderMessage);
// 4. 立即返回,不等待异步处理完成
Map<String, Object> result = new HashMap<>();
result.put("success", true);
result.put("message", "订单创建成功,异步处理中");
result.put("orderId", orderId);
return result;
}
}
▶️ 四、运行验证(30 秒搞定)
1. 启动 RabbitMQ
确保 RabbitMQ 已启动并能访问
2. 启动 Spring Boot 项目
bash
mvn spring-boot:run
3. 测试接口
bash
POST http://localhost:8080/api/orders/create
Content-Type: application/x-www-form-urlencoded
customerEmail=test@example.com&productName=iPhone&amount=5999.00
4. 预期结果
- 接口立即返回:
{"success":true,"message":"订单创建成功,异步处理中","orderId":...} - 控制台打印:
订单消息已发送:... - 消费者处理:
收到订单消息:...→订单邮件已发送给:... - 邮箱收到订单确认邮件
✅ 订单创建响应时间从几秒缩短到几百毫秒!
五、Bonus:消息队列常见坑点
坑 1:消息丢失
- 发送端 :开启
publisher-confirm-type确认发送成功 - 消费端 :手动确认
channel.basicAck(),处理失败时拒绝
坑 2:消息重复
- 消费者:实现幂等性处理(如根据订单ID去重)
- 生产者:避免重复发送
坑 3:死信队列
- 场景:消息处理失败且无法恢复
- 配置:设置死信交换机和队列,人工处理
坑 4:消息顺序
- 问题:RabbitMQ 不保证全局顺序
- 解决方案:相同业务的消息用相同 routing key,确保进入同一队列
六、进阶:延迟消息(订单超时取消)
java
// 添加延迟队列配置
@Bean
public Queue delayQueue() {
return QueueBuilder.durable("order.delay.queue")
.withArgument("x-dead-letter-exchange", orderExchange) // 死信交换机
.withArgument("x-dead-letter-routing-key", "order.timeout") // 死信路由键
.build();
}
// 发送延迟消息(15分钟后触发)
public void sendOrderDelayMessage(OrderMessage orderMessage, long delayTime) {
rabbitTemplate.convertAndSend("order.delay.exchange", "order.delay", orderMessage,
message -> {
message.getMessageProperties().setDelay((int) delayTime);
return message;
});
}
七、写在最后
消息队列,看似高深,实则是 解耦 + 异步 + 削峰 的组合。
记住三句话:
- 异步处理提升响应速度
- 手动确认保证消息不丢
- 幂等处理防止重复消费
学会消息队列,你就掌握了 高并发系统设计的核心技能!