RabbitMQ
1. RabbitMQ简介
- 消息队列的基本概念
消息队列是一种允许应用程序之间异步通信的中间件。它允许一个应用程序(生产者)将消息发送到队列中,而另一个应用程序(消费者)则可以从队列中读取消息。 它具有生产者(消息发送者) 和消费者 (消息接收者)

- 消息队列如何选型
特性 | ActiveMQ | RabbitMQ | RocketMQ | Kafka |
---|---|---|---|---|
单机吞吐量 | 万级 | 万级 | 10 万级 | 10 万级 |
时效性 | 毫秒级 | 微秒级 | 毫秒级 | 毫秒级 |
可用性 | 高(主从) | 高(主从) | 非常高(分布式) | 非常高(分布式) |
消息重复 | 至少一次 | 至少一次 | 至少一次 最多一次 | 至少一次 最多一次 |
消息顺序性 | 有序 | 有序 | 有序 | 分区有序 |
支持主题数 | 千级 | 百万级 | 千级 | 百级,多了性能严重下滑 |
消息回溯 | 不支持 | 不支持 | 支持(按时间回溯) | 支持(按offset回溯) |
管理界面 | 普通 | 普通 | 完善 | 普通 |
- 消息队列应用场景:
-
电子商务订单处理
在电子商务平台中,订单生成后需要经过多个步骤,如库存检查、支付处理、物流安排等。消息队列可以将订单信息异步传递给各个服务,确保订单处理流程的高效和可靠。
-
日志聚合
分布式系统中,各个服务节点会产生大量日志。消息队列可以收集这些日志信息,并将其发送到日志聚合服务进行集中处理和存储,便于后续的分析和监控。
-
用户行为追踪
在网站或应用中,用户的行为(如点击、浏览、购买等)可以被记录并通过消息队列发送到数据处理服务,用于实时分析用户行为,优化用户体验。
-
系统监控和报警
监控系统可以利用消息队列来收集各种监控指标,如CPU使用率、内存使用量等。当检测到异常情况时,可以通过消息队列触发报警机制,通知运维人员。
-
社交网络消息推送 在社交网络平台中,用户生成的内容(如帖子、评论、点赞等)可以通过消息队列推送给相关用户,确保消息的及时性和可靠性。
-
支付网关
支付网关需要处理大量的支付请求,并与银行或支付服务提供商进行交互。消息队列可以用于排队支付请求,确保支付流程的稳定性和安全性。
-
文件处理服务
在需要处理大量文件上传的场景中,可以将文件上传请求发送到消息队列,然后由专门的文件处理服务异步处理这些文件,如图片压缩、视频转码等,提高系统的响应速度和处理能力。
- AMQP协议简介
AMQP(高级消息队列协议)是一个为消息中间件设计的开放标准协议,它支持跨语言和跨平台的消息传递。AMQP采用二进制格式以提高传输效率,提供消息确认和持久化等可靠性保证,以及灵活的路由机制和多种消息模式,如点对点和发布/订阅。它还支持SASL认证和TLS/SSL加密,确保了消息传输的安全性。AMQP的多语言支持和互操作性使其成为企业级应用集成、微服务通信和大规模分布式系统中通信的坚实基础,广泛应用于需要高可靠性和灵活性的场景。

2. RabbitMQ核心组件
Exchange(交换机)类型:Direct、Fanout、Topic、Headers
在AMQP协议中,Exchange(交换机)是消息路由的核心组件,它根据特定的规则将消息路由到一个或多个队列。以下是四种主要的交换机类型:
- Direct Exchange(直连交换机):
直连交换机根据消息的routing key(路由键)将消息路由到匹配的队列。
生产者发送消息时指定一个routing key,消费者在绑定队列到交换机时也指定一个routing key。
如果消息的routing key与队列的routing key完全匹配,消息就会被路由到该队列。
这是最常用的交换机类型,适用于简单的点对点通信场景。
- Fanout Exchange(扇出交换机):
扇出交换机将消息路由到所有绑定的队列,而不考虑routing key。
它就像一个广播,将消息复制并发送到所有绑定的队列。
适用于发布/订阅模式,其中多个消费者都需要接收相同的消息副本。
- Topic Exchange(主题交换机):
主题交换机根据routing key的模式匹配将消息路由到队列。
routing key通常是一个由点(.)分隔的字符串,如"sports.basketball"。
队列可以绑定到一个或多个模式,如"sports.#"(匹配所有以"sports."开头的routing key)或"sports.basketball.#"(匹配所有以"sports.basketball."开头的routing key)。
适用于复杂的路由场景,如多级分类或通配符匹配。
- Headers Exchange(头交换机):
• 头交换机根据消息的header(头部)属性进行路由,而不是routing key。
• 生产者可以在发送消息时添加自定义的header属性,如{"priority":"high"}。
• 队列可以绑定到交换机时指定匹配的header条件,如{x-match:all,priority:"high"}。
• 适用于需要根据消息内容或元数据进行路由的场景。
Queue(队列)与Binding(绑定)
在消息队列系统中,Queue(队列)和Binding(绑定)是两个核心概念,它们共同工作以实现消息的路由和分发。
Queue(队列)
队列是消息的容器,用于存储等待被处理的消息。队列可以有以下特性:
-
持久化:队列可以被设置为持久化,这意味着即使消息队列服务重启,队列中的消息也不会丢失。
-
排他性:队列可以是排他的,意味着它只能被一个消费者使用,或者可以被多个消费者共享。
-
自动删除:当队列不再被使用时,可以设置为自动删除。
-
长度限制:队列可以设置最大长度,超过这个长度后,新的消息可能会被拒绝或旧的消息可能会被丢弃。
Binding(绑定)
绑定是队列和交换机之间的关联关系,它定义了消息如何从交换机路由到队列。绑定包含以下关键元素:
-
Routing Key(路由键):在某些类型的交换机(如Direct和Topic)中,路由键用于确定消息应该被路由到哪个队列。路由键是交换机和队列之间绑定的一部分。
-
Exchange(交换机):绑定定义了队列与哪个交换机相关联。
-
Arguments(参数):绑定可以包含额外的参数,这些参数可以用于配置绑定的行为,例如,设置绑定的优先级。
Message(消息)的结构与属性
在消息队列系统中,Message(消息)是传递信息的基本单元。消息的结构和属性可能因不同的消息队列系统(如RabbitMQ、Apache Kafka等)而略有不同,但通常包含以下几个核心部分:
- 消息体(Payload/Body)
-
描述:消息体是消息的主要内容,包含了需要传输的数据。
-
属性:消息体可以是文本(如JSON、XML、字符串等)、二进制数据(如文件、图像等)或其他任何可以序列化的数据格式。
- 消息头(Headers)
-
描述:消息头包含了一些元数据,用于描述消息的属性或用于路由决策。
-
属性:可以包括消息的优先级、时间戳、特定于应用的元数据(如用户ID、会话ID等)。
- 属性(Properties)
-
描述:属性通常用于指定消息的传输属性,如持久化、延迟等。
-
属性:
-
Delivery Mode:指定消息是否应该被持久化存储。通常有两个值:1(非持久化)和2(持久化)。
-
Priority:指定消息的优先级,用于在队列中确定消息的处理顺序。
-
Correlation ID:用于将消息与特定的事件或操作关联起来,方便追踪。
-
Reply To:指定消息的回复地址,用于请求/响应模式。
-
Expiration:指定消息的生存时间,超过这个时间后消息将被自动删除。
- 路由键(Routing Key)
-
描述:在某些消息队列系统中,路由键用于指定消息的路由路径,特别是在使用Direct、Topic或Fanout类型的交换机时。
-
属性:路由键是字符串类型的,其值取决于消息的发送者和接收者之间的约定。
- 交换机(Exchange)
-
描述:交换机是消息队列中的一个组件,负责接收消息并将它们路由到一个或多个队列。
-
属性:交换机的类型(Direct、Topic、Fanout、Headers)和名称。
- 队列(Queue)
-
描述:队列是存储消息的目的地,消费者从队列中获取消息进行处理。
-
属性:队列的名称和可能的其他队列特定的属性(如持久化、排他性等)。
3. Java集成RabbitMQ
- 引入依赖(如
spring-boot-starter-amqp
或amqp-client
)
java
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-amqp</artifactId>
<version>3.1.5</version>
</dependency>
</dependencies>
- 连接工厂(
ConnectionFactory
)配置
application.yml
xml
spring:
rabbitmq:
host: localhost
port: 5672
username: guest
password: guest
java
import com.rabbitmq.client.ConnectionFactory;
public class RabbitMQConfig {
public static ConnectionFactory createConnectionFactory() {
ConnectionFactory factory = new ConnectionFactory();
// 设置RabbitMQ服务器地址
factory.setHost("localhost");
// 设置RabbitMQ服务器端口,默认是5672
factory.setPort(5672);
// 设置虚拟主机,默认是"/"
factory.setVirtualHost("/");
// 设置用户名,默认是"guest"
factory.setUsername("guest");
// 设置密码,默认是"guest"
factory.setPassword("guest");
// 设置连接超时时间(毫秒)
factory.setConnectionTimeout(30000);
// 设置请求的心跳间隔(秒)
factory.setRequestedHeartbeat(60);
// 设置自动恢复连接
factory.setAutomaticRecoveryEnabled(true);
return factory;
}
}
- 创建连接、通道(
Channel
)与队列声明
java
import org.springframework.amqp.core.Binding;
import org.springframework.amqp.core.Queue;
import org.springframework.amqp.core.TopicExchange;
import org.springframework.amqp.core.BindingBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class RabbitMQConfig {
@Bean
public Queue myQueue() {
return new Queue("myQueue", true);
}
@Bean
public TopicExchange myExchange() {
return new TopicExchange("myExchange");
}
@Bean
public Binding binding(Queue myQueue, TopicExchange myExchange) {
return BindingBuilder.bind(myQueue).to(myExchange).with("my.routing.key");
}
}
4. 消息生产与消费
- 生产者发送消息(
basicPublish
)
直接向队列myQueue
发送消息
java
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class MessageProducer {
@Autowired
private RabbitTemplate rabbitTemplate;
public void sendMessage() {
rabbitTemplate.convertAndSend("myQueue","hello mq!");
}
}
向交换机发送消息
java
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class MessageProducer {
@Autowired
private RabbitTemplate rabbitTemplate;
public void sendMessage() {
rabbitTemplate.convertAndSend("myExchange", "myRoutingKey", "hello mq!");
}
}
- 消费者监听队列(
basicConsume
)
java
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;
@Component
public class MessageListener {
@RabbitListener(queues = "myQueue")
public void receiveMessage(String message) {
System.out.println("Received message: " + message);
}
}
java
@Component
public class MessageListener {
@RabbitListener(bindings = @QueueBinding(
value = @Queue(name = "myQueue", durable = "true"),
exchange = @Exchange(name = "myExchange", type = ExchangeTypes.TOPIC),
key = "my.routing.key"
))
public void receiveMessage(String message) {
System.out.println("Received message: " + message);
}
}
- 消息确认机制(ACK/NACK)
在RabbitMQ中,消息确认机制(也称为ACK/NACK)是一种确保消息可靠传递的重要机制。它允许消息消费者在处理完消息后向消息队列发送确认信号,告知消息已被成功处理。这样可以防止消息丢失,并确保消息至少被消费一次。
消息确认(ACK)
当消费者成功处理一条消息后,它会发送一个ACK(确认)信号回RabbitMQ。RabbitMQ收到ACK后,会从队列中删除该消息。ACK机制确保了消息不会被重复处理,除非消费者处理消息失败。
消息拒绝(NACK)
如果消费者无法处理一条消息(例如,由于消息格式错误或内部错误),它可以发送一个NACK(拒绝)信号回RabbitMQ。RabbitMQ收到NACK后,可以选择将消息重新入队,以便其他消费者可以重新处理,或者将其丢弃,具体取决于NACK的配置。
自动确认与手动确认
RabbitMQ支持两种消息确认模式:
- 自动确认(Auto-ACK):
在这种模式下,消费者从队列中接收消息后,RabbitMQ会自动发送ACK确认,消息会被立即删除。
这种模式简单,但不够灵活,因为它不提供错误处理机制。
- 手动确认(Manual-ACK):
在这种模式下,消费者需要显式地发送ACK或NACK信号。
这种模式更灵活,允许消费者在处理消息后决定是否确认消息,从而实现更复杂的错误处理和消息重试逻辑。
使用Spring AMQP实现手动确认
在Spring AMQP中,你可以通过配置SimpleRabbitListenerContainerFactory
来启用手动确认模式:
java
import org.springframework.amqp.rabbit.config.SimpleRabbitListenerContainerFactory;
import org.springframework.amqp.rabbit.connection.ConnectionFactory;
import org.springframework.amqp.rabbit.listener.RabbitListenerEndpointRegistrar;
import org.springframework.amqp.rabbit.listener.api.ChannelAwareMessageListener;
import org.springframework.amqp.rabbit.listener.api.MessageListener;
import org.springframework.amqp.rabbit.listener.api.SimpleMessageListenerContainer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class RabbitMQConfig {
@Bean
public SimpleRabbitListenerContainerFactory myFactory(ConnectionFactory connectionFactory) {
SimpleRabbitListenerContainerFactory factory = new SimpleRabbitListenerContainerFactory();
factory.setConnectionFactory(connectionFactory);
factory.setAcknowledgeMode(AcknowledgeMode.MANUAL);
factory.setConcurrentConsumers(3);
factory.setMaxConcurrentConsumers(10);
return factory;
}
}
然后,在消费者方法中手动处理ACK:
java
@Component
public class MessageListener {
@Autowired
private RabbitTemplate rabbitTemplate;
@RabbitListener(queues = "myQueue", containerFactory = "myFactory")
public void receiveMessage(String message, Channel channel, @Header("deliveryTag") long deliveryTag) throws IOException {
try {
// 处理消息
System.out.println("Received message: " + message);
// 手动发送ACK
channel.basicAck(deliveryTag, false);
} catch (Exception e) {
// 处理消息失败,发送NACK
channel.basicNack(deliveryTag, false, true);
}
}
}
在这个例子中,我们使用Channel
对象来手动发送ACK或NACK。basicAck
方法用于确认消息,而basicNack
方法用于拒绝消息。
通过使用手动确认机制,你可以更灵活地处理消息,并确保消息的可靠传递。
5. 高级特性与实践
- 消息持久化(
deliveryMode=2
)
将消息和队列信息写入磁盘,确保在 RabbitMQ Broker 重启后消息不丢失。
默认情况下,消息仅存储在内存中,Broker 崩溃即丢失
开启持久化后,消息会被写入磁盘日志文件
是实现 At-Least-Once Delivery(至少一次投递) 的基础
- 死信队列(DLX)与TTL设置
定义交换机 队列和绑定
java
import org.springframework.amqp.core.Binding;
import org.springframework.amqp.core.BindingBuilder;
import org.springframework.amqp.core.DurableQueue;
import org.springframework.amqp.core.Exchange;
import org.springframework.amqp.core.ExchangeBuilder;
import org.springframework.amqp.core.Queue;
import org.springframework.amqp.core.TopicExchange;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class RabbitMQConfig {
@Bean
public Queue mainQueue() {
return new DurableQueue("mainQueue", true);
}
@Bean
public Queue deadLetterQueue() {
return new DurableQueue("deadLetterQueue", true);
}
@Bean
public Exchange mainExchange() {
return ExchangeBuilder.topicExchange("mainExchange").durable(true).build();
}
@Bean
public Exchange deadLetterExchange() {
return ExchangeBuilder.topicExchange("deadLetterExchange").durable(true).build();
}
@Bean
public Binding mainBinding(Queue mainQueue, Exchange mainExchange) {
return BindingBuilder.bind(mainQueue).to(mainExchange).with("main_routing_key");
}
@Bean
public Binding deadLetterBinding(Queue deadLetterQueue, Exchange deadLetterExchange, Queue mainQueue) {
return BindingBuilder.bind(deadLetterQueue).to(deadLetterExchange).with("deadLetter_routing_key");
}
}
配置消费者
java
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;
@Component
public class MessageListener {
@RabbitListener(queues = "mainQueue")
public void receiveMessage(String message) {
System.out.println("Received message: " + message);
// 模拟处理失败
throw new RuntimeException("Failed to process message");
}
}
在application.yml 中配置死信队列(DLX)与TTL设置
xml
spring:
rabbitmq:
listener:
simple:
prefetch: 1
acknowledge-mode: auto
concurrency: 3
max-concurrency: 10
queues:
- name: mainQueue
durable: true
exclusive: false
auto-delete: false
dead-letter-exchange: deadLetterExchange
ttl: 60000 # 设置TTL为60秒
exchanges:
- name: mainExchange
type: topic
durable: true
bindings:
- queue: mainQueue
exchange: mainExchange
routing-key: main_routing_key
- queue: deadLetterQueue
exchange: deadLetterExchange
routing-key: deadLetter_routing_key
6. Spring Boot集成示例
@RabbitListener
注解实现监听
java
@Component
public class MessageListener {
@RabbitListener(bindings = @QueueBinding(
value = @Queue(name = "myQueue", durable = "true"),
exchange = @Exchange(name = "myExchange", type = ExchangeTypes.TOPIC),
key = "my.routing.key"
))
public void receiveMessage(String message) {
System.out.println("Received message: " + message);
}
}
RabbitTemplate
发送消息
java
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class MessageProducer {
@Autowired
private RabbitTemplate rabbitTemplate;
public void sendMessage() {
rabbitTemplate.convertAndSend("myExchange", "myRoutingKey", "hello mq!");
}
}
- 配置类(
@Configuration
)定义Exchange/Queue
java
import org.springframework.amqp.core.Binding;
import org.springframework.amqp.core.Queue;
import org.springframework.amqp.core.TopicExchange;
import org.springframework.amqp.core.BindingBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class RabbitMQConfig {
@Bean
public Queue myQueue() {
return new Queue("myQueue", true);
}
@Bean
public TopicExchange myExchange() {
return new TopicExchange("myExchange");
}
@Bean
public Binding binding(Queue myQueue, TopicExchange myExchange) {
return BindingBuilder.bind(myQueue).to(myExchange).with("my.routing.key");
}
}