消息队列是后端开发的核心组件,用于异步处理、削峰填谷、服务解耦。RabbitMQ 是目前使用最广泛的消息中间件之一,这篇文章带你从零上手。
一、为什么要用消息队列
场景:用户注册后发送邮件
java
// 同步方式:注册完等邮件发完才返回
public void register(User user) {
userMapper.insert(user); // 10ms
emailService.sendEmail(user); // 2000ms(等待邮件发送)
return "注册成功";
}
// 总耗时:~2010ms
java
// 异步方式:注册完丢到消息队列就返回
public void register(User user) {
userMapper.insert(user); // 10ms
rabbitTemplate.convertAndSend("email", user); // 5ms
return "注册成功";
}
// 总耗时:~15ms 立即返回,邮件异步发送
| 场景 | 同步 | 异步(消息队列) |
|---|---|---|
| 注册发邮件 | 2秒 | 15ms ✅ |
| 秒杀/抢购 | 可能崩溃 | 平稳削峰 ✅ |
| 订单处理 | 耦合严重 | 服务解耦 ✅ |
二、核心概念
Producer → Exchange → Queue → Consumer
(生产者) (交换机) (队列) (消费者)
| 概念 | 说明 |
|---|---|
| Producer | 发送消息的一方(如注册服务) |
| Exchange | 消息路由器,决定消息发到哪个队列 |
| Queue | 存储消息的队列 |
| Consumer | 消费消息的一方(如邮件服务) |
三、环境准备
1. 安装 RabbitMQ(Windows)
下载安装后,访问 http://localhost:15672 (默认账号:guest/guest)
2. 引入依赖
xml
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-amqp</artifactId>
</dependency>
3. 配置
yaml
spring:
rabbitmq:
host: localhost
port: 5672
username: guest
password: guest
virtual-host: /
listener:
simple:
acknowledge-mode: manual # 手动确认
prefetch: 1 # 每次只消费一条
四、实战:注册发送邮件
1. 配置队列和交换机
java
@Configuration
public class RabbitConfig {
// 队列
@Bean
public Queue emailQueue() {
return QueueBuilder.durable("email.queue").build();
}
// 交换机
@Bean
public DirectExchange emailExchange() {
return new DirectExchange("email.exchange");
}
// 绑定:队列通过 routing key 绑定到交换机
@Bean
public Binding emailBinding() {
return BindingBuilder.bind(emailQueue())
.to(emailExchange())
.with("email.routing");
}
}
2. 发送消息
java
@Service
public class UserService {
@Autowired
private RabbitTemplate rabbitTemplate;
public void register(User user) {
// 1. 保存用户到数据库
userMapper.insert(user);
// 2. 发送消息到队列(异步发邮件)
Map<String, Object> msg = new HashMap<>();
msg.put("email", user.getEmail());
msg.put("username", user.getUsername());
msg.put("type", "welcome");
rabbitTemplate.convertAndSend(
"email.exchange", // 交换机
"email.routing", // routing key
msg // 消息内容
);
System.out.println("注册成功,邮件已投递到队列");
}
}
3. 消费消息
java
@Component
@Slf4j
public class EmailConsumer {
@RabbitListener(queues = "email.queue")
public void handleEmail(Message message, Channel channel) throws IOException {
long deliveryTag = message.getMessageProperties().getDeliveryTag();
try {
// 获取消息内容
Map<String, Object> msg = (Map<String, Object>)
new ObjectInputStream(
new ByteArrayInputStream(message.getBody())
).readObject();
String email = (String) msg.get("email");
String username = (String) msg.get("username");
// 模拟发送邮件
System.out.println("发送欢迎邮件给: " + email);
Thread.sleep(1000); // 模拟邮件发送耗时
// 手动确认(告诉 RabbitMQ 这条消息处理完了)
channel.basicAck(deliveryTag, false);
} catch (Exception e) {
log.error("邮件发送失败", e);
// 失败后,重回队列或进入死信队列
channel.basicNack(deliveryTag, false, true);
}
}
}
五、交换机类型
1. Direct Exchange(直连)
根据 routing key 精确匹配:
java
// 队列绑定 routing key = "email"
// 消息 routing key = "email" ✅ 匹配
// 消息 routing key = "sms" ❌ 不匹配
2. Topic Exchange(主题)
支持通配符匹配:
java
// 队列绑定 routing key = "log.*"
// 消息 routing key = "log.info" ✅ 匹配
// 消息 routing key = "log.error" ✅ 匹配
// 消息 routing key = "user.log" ❌ 不匹配
// * 代表一个单词,# 代表多个单词
// 队列绑定 "log.#"
// 能匹配 log.info、log.error、log.user.info
3. Fanout Exchange(广播)
发送给所有绑定的队列,忽略 routing key:
java
// 适合群发通知、广播消息
rabbitTemplate.convertAndSend("notice.exchange", "", message);
六、消息可靠性保障
1. 生产者确认(确保消息到达交换机)
yaml
spring:
rabbitmq:
publisher-confirm-type: correlated # 开启确认
publisher-returns: true # 开启回退
java
@Bean
public RabbitTemplate rabbitTemplate(ConnectionFactory factory) {
RabbitTemplate template = new RabbitTemplate(factory);
template.setConfirmCallback((data, ack, cause) -> {
if (!ack) {
System.out.println("消息发送到交换机失败: " + cause);
}
});
return template;
}
2. 消费者手动确认(确保消息被处理)
java
// 处理成功 → 确认
channel.basicAck(deliveryTag, false);
// 处理失败 → 重回队列重试
channel.basicNack(deliveryTag, false, true);
// 处理失败 → 丢弃消息(进入死信队列)
channel.basicNack(deliveryTag, false, false);
3. 死信队列(处理失败的消息)
java
@Bean
public Queue deadLetterQueue() {
return QueueBuilder.durable("email.dead.letter").build();
}
@Bean
public Queue emailQueue() {
Map<String, Object> args = new HashMap<>();
args.put("x-dead-letter-exchange", "dead.letter.exchange");
args.put("x-dead-letter-routing-key", "dead.letter.routing");
return QueueBuilder.durable("email.queue").withArguments(args).build();
}
七、应用场景总结
| 场景 | 推荐交换机 | 说明 |
|---|---|---|
| 业务消息(注册、下单) | Direct | 一对一精确路由 |
| 日志收集 | Topic | 按级别/模块分类 |
| 系统通知广播 | Fanout | 所有服务同时收到 |
| 延迟任务 | 死信队列 | 订单超时取消 |
总结
RabbitMQ 是后端开发的必备中间件。记住三条原则:
- 异步处理提升响应速度------耗时操作丢到队列,立即返回
- 手动确认保证不丢消息------处理成功才 ack
- 死信队列兜底------处理失败的消息有地方去
如果对你有帮助,欢迎点赞、评论、关注【张老师技术栈】,持续分享 Java/Python/爬虫 实战干货。