文章目录
一、RabbitMQ 概述
1. 定义
RabbitMQ 是基于 AMQP(高级消息队列协议)的开源消息代理和队列服务器。在分布式系统里,它负责在不同组件间传递消息,实现系统解耦、异步处理与流量削峰。
2. 应用场景
- 系统解耦:电商系统中,订单系统和库存系统可通过 RabbitMQ 解耦。订单系统生成订单消息后发送到队列,库存系统从队列获取消息处理库存扣减。
- 异步处理:用户注册时,发送注册邮件这类耗时操作可通过消息队列异步处理,用户注册成功后立即返回结果,邮件发送任务入队等待处理。
- 流量削峰:秒杀活动中,大量用户请求先进入消息队列,系统按一定速率从队列取请求处理,避免系统因高流量崩溃。
二、RabbitMQ 核心概念
1. 生产者(Producer)
消息的发送方,将消息发送到 RabbitMQ 的交换器(Exchange)。
2. 消费者(Consumer)
消息的接收方,从 RabbitMQ 的队列(Queue)获取消息并处理。
3. 交换器(Exchange)
接收生产者消息,并按路由规则将消息路由到一个或多个队列。常见类型有:
- 直连交换器(Direct Exchange):根据消息路由键(Routing Key)和绑定键(Binding Key)精确匹配来路由消息。
- 扇形交换器(Fanout Exchange):将消息广播到所有绑定队列,不考虑路由键。
- 主题交换器(Topic Exchange):依据消息路由键和绑定键的模式匹配规则路由,绑定键可用
*
(匹配一个单词)和#
(匹配零个或多个单词)。 - 头交换器(Headers Exchange):根据消息头部信息(Headers)决定路由。
4. 队列(Queue)
存储交换器路由过来的消息,供消费者获取处理。
5. 绑定(Binding)
交换器和队列的关联关系,通过绑定键将二者绑定,决定消息从交换器到队列的路由。
6. 虚拟主机(Virtual Host)
RabbitMQ 中的逻辑概念,用于隔离不同应用程序,每个虚拟主机有独立的交换器、队列和绑定。
三、RabbitMQ 安装与启动
1. Ubuntu 安装步骤
echo "deb https://packagecloud.io/rabbitmq/rabbitmq-server/ubuntu/ $(lsb_release -sc) main" | sudo tee /etc/apt/sources.list.d/rabbitmq.list
curl -fsSL https://packagecloud.io/rabbitmq/rabbitmq-server/gpgkey | sudo apt-key add -
sudo apt-get update
sudo apt-get install rabbitmq-server
2. 启动与管理
sudo systemctl start rabbitmq-server
sudo systemctl stop rabbitmq-server
sudo systemctl restart rabbitmq-server
sudo systemctl status rabbitmq-server
3. 启用管理界面
sudo rabbitmq-plugins enable rabbitmq_management
启用后,访问 http://localhost:15672
,用默认用户名 guest
和密码 guest
登录。
四、基于 Spring Boot 使用 RabbitMQ(注解方式)
1. 项目准备
在 pom.xml
中添加依赖:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-amqp</artifactId>
</dependency>
2. 配置文件
application.yml
配置:
spring:
rabbitmq:
host: localhost
port: 5672
username: guest
password: guest
3. 生产者代码示例
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class RabbitMQProducer {
@Autowired
private RabbitTemplate rabbitTemplate;
private static final String EXCHANGE_NAME = "directExchange";
private static final String ROUTING_KEY = "directRoutingKey";
public void sendMessage(String message) {
rabbitTemplate.convertAndSend(EXCHANGE_NAME, ROUTING_KEY, message);
System.out.println(" [x] Sent '" + message + "'");
}
}
4. 消费者代码示例
import org.springframework.amqp.rabbit.annotation.*;
import org.springframework.stereotype.Service;
@Service
public class RabbitMQConsumer {
@RabbitListener(bindings = @QueueBinding(
value = @Queue(name = "directQueue", durable = "true"),
exchange = @Exchange(name = "directExchange", type = "direct"),
key = "directRoutingKey"
))
public void receiveMessage(String message) {
System.out.println(" [x] Received '" + message + "'");
}
}
5. 测试代码
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class RabbitMQApplication implements CommandLineRunner {
@Autowired
private RabbitMQProducer producer;
public static void main(String[] args) {
SpringApplication.run(RabbitMQApplication.class, args);
}
@Override
public void run(String... args) throws Exception {
producer.sendMessage("Hello, RabbitMQ with Annotations!");
}
}
五、RabbitMQ 高级特性
1. 消息确认机制
生产者确认
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;
@Service
public class ConfirmProducer {
@Autowired
private RabbitTemplate rabbitTemplate;
public void sendMessageWithConfirm(String message) {
CorrelationData correlationData = new CorrelationData("1");
rabbitTemplate.convertAndSend("directExchange", "directRoutingKey", message, correlationData);
rabbitTemplate.setConfirmCallback((correlationData1, ack, cause) -> {
if (ack) {
System.out.println("Message sent successfully");
} else {
System.out.println("Message sent failed: " + cause);
}
});
}
}
消费者确认
import org.springframework.amqp.rabbit.annotation.AcknowledgeMode;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Service;
@Service
public class ConfirmConsumer {
@RabbitListener(queues = "directQueue", ackMode = AcknowledgeMode.MANUAL)
public void receiveMessage(org.springframework.amqp.core.Message message,
com.rabbitmq.client.Channel channel) throws Exception {
try {
System.out.println("Received message: " + new String(message.getBody()));
channel.basicAck(message.getMessageProperties().getDeliveryTag(), false);
} catch (Exception e) {
channel.basicNack(message.getMessageProperties().getDeliveryTag(), false, true);
}
}
}
2. 消息持久化
import org.springframework.amqp.core.*;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class PersistentConfig {
public static final String PERSISTENT_QUEUE = "persistentQueue";
public static final String PERSISTENT_EXCHANGE = "persistentExchange";
public static final String PERSISTENT_ROUTING_KEY = "persistentRoutingKey";
@Bean
public Queue persistentQueue() {
return new Queue(PERSISTENT_QUEUE, true);
}
@Bean
public DirectExchange persistentExchange() {
return new DirectExchange(PERSISTENT_EXCHANGE, true, false);
}
@Bean
public Binding persistentBinding() {
return BindingBuilder.bind(persistentQueue()).to(persistentExchange()).with(PERSISTENT_ROUTING_KEY);
}
}
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class PersistentProducer {
@Autowired
private RabbitTemplate rabbitTemplate;
public void sendPersistentMessage(String message) {
rabbitTemplate.convertAndSend(PersistentConfig.PERSISTENT_EXCHANGE,
PersistentConfig.PERSISTENT_ROUTING_KEY, message);
}
}
3. 死信队列
当消息被拒绝(basicReject
或 basicNack
)且 requeue
为 false
、消息过期或队列达到最大长度时,消息会进入死信队列。
import org.springframework.amqp.core.*;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class DeadLetterConfig {
public static final String NORMAL_QUEUE = "normalQueue";
public static final String DEAD_LETTER_QUEUE = "deadLetterQueue";
public static final String NORMAL_EXCHANGE = "normalExchange";
public static final String DEAD_LETTER_EXCHANGE = "deadLetterExchange";
public static final String NORMAL_ROUTING_KEY = "normalRoutingKey";
public static final String DEAD_LETTER_ROUTING_KEY = "deadLetterRoutingKey";
@Bean
public Queue normalQueue() {
return QueueBuilder.durable(NORMAL_QUEUE)
.withArgument("x-dead-letter-exchange", DEAD_LETTER_EXCHANGE)
.withArgument("x-dead-letter-routing-key", DEAD_LETTER_ROUTING_KEY)
.build();
}
@Bean
public Queue deadLetterQueue() {
return new Queue(DEAD_LETTER_QUEUE, true);
}
@Bean
public DirectExchange normalExchange() {
return new DirectExchange(NORMAL_EXCHANGE, true, false);
}
@Bean
public DirectExchange deadLetterExchange() {
return new DirectExchange(DEAD_LETTER_EXCHANGE, true, false);
}
@Bean
public Binding normalBinding() {
return BindingBuilder.bind(normalQueue()).to(normalExchange()).with(NORMAL_ROUTING_KEY);
}
@Bean
public Binding deadLetterBinding() {
return BindingBuilder.bind(deadLetterQueue()).to(deadLetterExchange()).with(DEAD_LETTER_ROUTING_KEY);
}
}
4. 延迟队列
可通过消息过期时间和死信队列实现。
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class DelayProducer {
@Autowired
private RabbitTemplate rabbitTemplate;
public void sendDelayMessage(String message, long delay) {
rabbitTemplate.convertAndSend(DeadLetterConfig.NORMAL_EXCHANGE,
DeadLetterConfig.NORMAL_ROUTING_KEY, message, msg -> {
msg.getMessageProperties().setExpiration(String.valueOf(delay));
return msg;
});
}
}
六、RabbitMQ 监控与管理
1. 管理界面监控
通过 http://localhost:15672
可实时监控队列消息数量、消费者连接数、消息速率等信息。
2. 命令行工具监控
sudo rabbitmqctl list_queues
sudo rabbitmqctl list_connections