RabbitMQ 是一个开源的消息队列软件(有时也被称为消息代理),它实现了高级消息队列协议(AMQP)。它主要用于应用程序之间,或者软件组件之间的消息通信。通过使用 RabbitMQ,可以实现异步的、可靠的、可扩展的消息处理机制,这使得它成为构建分布式系统时的一个理想选择。
RabbitMQ组件:
Broker
作用:消息代理服务器,核心服务进程。
Exchange
作用:接收生产者消息,按规则路由到队列。
类型:
Direct:精确匹配 Routing Key。
Fanout:广播到所有绑定队列。
Topic:模糊匹配 Routing Key(通配符)。
Headers:基于消息头匹配(较少用)。
Queue
特点:消息的最终存储位置,消费者从队列拉取消息。
属性:可设置持久化、排他性、自动删除等。
Binding
作用:定义 Exchange 和 Queue 的绑定关系(含 Routing Key)。
Producer
特点:消息通过 Exchange 路由,无需直接指定队列。
Consumer
消费模式:
Push 模型:服务端主动推送(默认)。
Pull 模型:需手动调用 basic.get(较少用)。
ACK 机制:手动确认(确保消息不丢失)或自动确认。
Virtual Host
作用:逻辑隔离单元(类似命名空间),含独立的 Exchange、Queue 和权限。
Channel
作用:复用 TCP 连接的轻量级通道,避免频繁创建连接的开销。
插件机制
管理插件:如 rabbitmq-management 提供 Web 管理界面。
扩展插件:如延迟队列(rabbitmq-delayed-message-exchange)。
RabbitMQ的工作流程

1.Producer ⽣产了⼀条消息
2.Producer 连接到RabbitMQBroker, 建⽴⼀个连接(Connection),开启⼀个信道(Channel)
3.Producer 声明⼀个交换机(Exchange), 路由消息
4.Producer 声明⼀个队列(Queue), 存放信息
5.Producer 发送消息⾄RabbitMQ Broker
6.RabbitMQ Broker 接收消息, 并存⼊相应的队列(Queue)中, 如果未找到相应的队列, 则根据⽣产者的配置, 选择丢弃或者退回给⽣产者
RabbitMQ 交换机类型
RabbitMQ 提供了多种类型的交换机(Exchange),每种类型决定了消息如何从生产者路由到一个或多个队列。以下是 RabbitMQ 中主要的交换机类型:
1. 直连交换机(Direct Exchange)
功能:直连交换机根据消息携带的路由键(Routing Key)和绑定键(Binding Key)是否完全匹配来决定将消息路由到哪个队列。
使用场景:当需要基于不同条件将消息分发给不同的消费者时非常有用。例如,日志系统中可以根据严重性级别(如 info、warning、error)将消息路由到不同的目的地。
2. 主题交换机(Topic Exchange)
功能:主题交换机允许使用通配符进行模式匹配路由键来路由消息。其中 * 匹配一个单词,# 匹配零个或多个单词。
使用场景:适合于复杂的消息路由逻辑,尤其是当需要灵活地路由消息到多个消费者时。比如,路由所有来自美国或者欧洲的订单信息,可以使用类似 *.us.* 或 *.eu.* 的模式。
3. 扇形交换机(Fanout Exchange)
功能:扇形交换机忽略路由键,直接将消息广播到所有绑定到该交换机的队列上。
使用场景:当你想向多个消费者同时发送相同的消息时非常适合。例如,实时更新或广播通知系统。
4. 头信息交换机(Headers Exchange)
功能:头信息交换机允许你匹配消息头部属性而不是路由键来进行消息路由。它提供了更大的灵活性,因为可以设置复杂的匹配规则,包括AND和OR操作。
使用场景:适用于需要更复杂过滤条件的消息路由,特别是在需要考虑多于一个参数的情况下。
5. 默认交换机
虽然严格意义上不被视为一种独立类型,但 RabbitMQ 提供了一个默认的无名交换机(也称为“匿名”交换机)。当你在声明队列时没有指定交换机,消息会通过这个默认交换机直接路由到指定名称的队列。这实际上是一个直连交换机,它的特殊之处在于它是隐式的,且每个队列都自动绑定到它,路由键就是队列的名字。

RabbitMQ使用
1.安装RabbitMQ:RabbitMQ: One broker to queue them all | RabbitMQ
1.引入依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-amqp</artifactId>
</dependency>
2.配置文件(生产者和消费者服务都要配置)
server:
port: 9301
spring:
application:
name: test-service
rabbitmq:
host: 127.0.0.1
port: 5672
username: guest
password: guest
3.配置交换机以及绑定队列
在 RabbitMQConfig 中,我们通过 @Bean 方法定义并返回交换机(Exchange)、队列(Queue)以及绑定(Binding)。这些配置会被 Spring 自动注册到 RabbitMQ 中。
- 交换机名:在配置中定义的交换机名(return new XxxExchange("交换机名"))会被 Spring 注册到 RabbitMQ 中,决定了消息指定发送到配置的哪一个交换机上。
- 队列名:队列名(如 direct.queue、topic.queue 等)也会被注册到 RabbitMQ 中,在后续消费监听中被用于指定监听的队列。
- 绑定:通过 BindingBuilder 将队列绑定到交换机,并指定路由键(Routing Key)或匹配规则。
这些配置的目的是在 RabbitMQ 中创建好交换机和队列,并建立它们之间的关系。
@Configuration
public class RabbitMQConfigure {
// Direct Exchange
@Bean
public DirectExchange directExchange() {
return new DirectExchange("direct.exchange");
}
@Bean
public Queue directQueue() {
return new Queue("direct.queue");
}
@Bean
public Binding directBinding() {
return BindingBuilder.bind(directQueue()).to(directExchange()).with("direct.routingKey");
}
// Topic Exchange
@Bean
public TopicExchange topicExchange() {
return new TopicExchange("topic.exchange");
}
@Bean
public Queue topicQueue() {
return new Queue("topic.queue");
}
@Bean
public Binding topicBinding() {
return BindingBuilder.bind(topicQueue()).to(topicExchange()).with("topic.*");
}
// Fanout Exchange
@Bean
public FanoutExchange fanoutExchange() {
return new FanoutExchange("fanout.exchange");
}
@Bean
public Queue fanoutQueue1() {
return new Queue("fanout.queue1");
}
@Bean
public Queue fanoutQueue2() {
return new Queue("fanout.queue2");
}
@Bean
public Binding fanoutBinding1() {
return BindingBuilder.bind(fanoutQueue1()).to(fanoutExchange());
}
@Bean
public Binding fanoutBinding2() {
return BindingBuilder.bind(fanoutQueue2()).to(fanoutExchange());
}
// Headers Exchange
@Bean
public HeadersExchange headersExchange() {
return new HeadersExchange("headers.exchange");
}
@Bean
public Queue headersQueue() {
return new Queue("headers.queue");
}
@Bean
public Binding headersBinding() {
return BindingBuilder.bind(headersQueue()).to(headersExchange()).where("headerKey").matches("headerValue");
}
}
4.发送消息
在发送消息时,我们使用 RabbitTemplate 的 convertAndSend 方法,并通过第一个参数指定交换机的名称。
public void convertAndSend(String exchange, String routingKey, Object object)
- exchange: 指定消息发送到的交换机名称。
- routingKey: 指定消息的路由键,用于决定消息如何路由到队列。
- message: 要发送的消息内容。
发送消息时,交换机的名称必须与配置中定义的交换机名一致。
路由键的使用方式取决于交换机的类型(如 Direct、Topic、Fanout 等),FanoutExchange不注重路由键,不用设置。而TopicExchange和DirectExchange的区别在于路由键支持模糊匹配,而DirectExchange是直接指定路由键。
@Service
public class MessageSender {
@Autowired
private RabbitTemplate rabbitTemplate;
public void sendDirectMessage(String message) {
rabbitTemplate.convertAndSend("direct.exchange", "direct.routingKey", message);
}
public void sendTopicMessage(String message) {
rabbitTemplate.convertAndSend("topic.exchange", "topic.routingKey", message);
}
public void sendFanoutMessage(String message) {
rabbitTemplate.convertAndSend("fanout.exchange", "", message);
}
public void sendHeadersMessage(String message) {
MessageProperties properties = new MessageProperties();
properties.setHeader("headerKey", "headerValue");
Message msg = new Message(message.getBytes(), properties);
rabbitTemplate.convertAndSend("headers.exchange", "", msg);
}
}
5.接收消息
@RabbitListener(queues = "队列名")
我们使用 @RabbitListener 注解,指定队列名来监听队列收到的消息,而不是交换机,当消息被路由到该队列时,监听方法会自动触发并处理消息。
@Service
public class MessageReceiver {
@RabbitListener(queues = "direct.queue")
public void receiveDirectMessage(String message) {
System.out.println("Received direct message: " + message);
}
@RabbitListener(queues = "topic.queue")
public void receiveTopicMessage(String message) {
System.out.println("Received topic message: " + message);
}
@RabbitListener(queues = "fanout.queue1")
public void receiveFanoutMessage1(String message) {
System.out.println("Received fanout message 1: " + message);
}
@RabbitListener(queues = "fanout.queue2")
public void receiveFanoutMessage2(String message) {
System.out.println("Received fanout message 2: " + message);
}
@RabbitListener(queues = "headers.queue")
public void receiveHeadersMessage(String message) {
System.out.println("Received headers message: " + message);
}
}
6.使用流程总结
Config 配置:定义交换机、队列和绑定关系,这些配置会被注册到 RabbitMQ 中。
发送消息:通过 RabbitTemplate 指定交换机名称和路由键,将消息发送到交换机。
监听消息:通过 @RabbitListener 监听队列。
RabbitMQ回调机制(Publisher Confirms)
RabbitMQ 提供了多种回调机制,包括 消息确认回调(ConfirmCallback) 和 路由失败回调(ReturnsCallback),用于确保生产者消息成功到达Broker。以下是具体的代码实现示例(全局):
1. 配置yml
spring:
rabbitmq:
publisher-confirm-type: correlated # 开启异步 Confirm 回调
publisher-returns: true # 开启 Returns 回调
template:
mandatory: true # 路由失败时触发 ReturnsCallback
2.定义ReturnsCallback和ConfirmCallback
@Slf4j
@Configuration
public class RabbitMQConfig implements ApplicationContextAware {
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
RabbitTemplate rabbitTemplate = applicationContext.getBean(RabbitTemplate.class);
// 设置 ReturnsCallback
rabbitTemplate.setReturnsCallback(returned -> {
log.error("消息路由失败: exchange={}, routingKey={}, replyCode={}, replyText={}",
returned.getExchange(),
returned.getRoutingKey(),
returned.getReplyCode(),
returned.getReplyText()
);
});
// 设置 ConfirmCallback
rabbitTemplate.setConfirmCallback((correlationData, ack, cause) -> {
if (ack) {
log.info("消息成功发送到 Broker, ID: {}", correlationData.getId());
} else {
log.error("消息发送失败, ID: {}, 原因: {}", correlationData.getId(), cause);
}
});
}
}
- ConfirmCallback,ReturnsCallback 是异步的。
- 消息发送后,生产者不会等待返回结果,而是继续执行后续逻辑。
- 只有当 RabbitMQ 返回失败的消息时,才会触发回调。
RabbitMQ实现分区顺序消费
1.配置交换机和队列,多个队列
@Configuration
public class RabbitOrderedConfig {
// 定义直连交换机(Direct Exchange)
@Bean
public DirectExchange orderedExchange() {
return new DirectExchange("ordered.exchange");
}
// 定义多个队列(比如3个分区队列)
@Bean
public Queue orderedQueue1() {
return new Queue("ordered.queue.1", true);
}
@Bean
public Queue orderedQueue2() {
return new Queue("ordered.queue.2", true);
}
@Bean
public Queue orderedQueue3() {
return new Queue("ordered.queue.3", true);
}
// 绑定队列到交换机,并指定路由键(这里用订单ID的哈希取模)
@Bean
public Binding binding1(DirectExchange orderedExchange, Queue orderedQueue1) {
return BindingBuilder.bind(orderedQueue1)
.to(orderedExchange)
.with("1"); // 路由键为1的消息进入队列1
}
@Bean
public Binding binding2(DirectExchange orderedExchange, Queue orderedQueue2) {
return BindingBuilder.bind(orderedQueue2)
.to(orderedExchange)
.with("2"); // 路由键为2的消息进入队列2
}
@Bean
public Binding binding3(DirectExchange orderedExchange, Queue orderedQueue3) {
return BindingBuilder.bind(orderedQueue3)
.to(orderedExchange)
.with("3"); // 路由键为3的消息进入队列3
}
}
2.发送消息时按路由键分区路由
@Service
public class OrderProducer {
@Autowired
private RabbitTemplate rabbitTemplate;
public void sendOrderMessage(Order order) {
// 计算分区键(例如:订单ID取模,确保相同订单进入同一队列)
String routingKey = String.valueOf(order.getOrderId().hashCode() % 3 + 1); // 1, 2, 3
// 发送到直连交换机,并指定路由键
rabbitTemplate.convertAndSend(
"ordered.exchange", // 交换机名称
routingKey, // 路由键(决定进入哪个队列)
order // 消息体
);
}
}
3.为每个队列配置单独消费者,保证分区顺序
concurrency 和 默认队列绑定机制 配置保证了分布式架构的单活跃消费者,保险起见可以使用分布式锁来锁定队列处理的互斥关系
@Component
public class OrderConsumer {
// 监听队列1(单线程消费)
@RabbitListener(queues = "ordered.queue.1", concurrency = "1")
public void processOrder1(Order order) {
System.out.println("队列1分布式锁处理订单: " + order.getOrderId());
}
// 监听队列2(单线程消费)
@RabbitListener(queues = "ordered.queue.2", concurrency = "1")
public void processOrder2(Order order) {
System.out.println("队列2分布式锁处理订单: " + order.getOrderId());
}
// 监听队列3(单线程消费)
@RabbitListener(queues = "ordered.queue.3", concurrency = "1")
public void processOrder3(Order order) {
System.out.println("队列3分布式锁处理订单: " + order.getOrderId());
}
}
RabbitMQ 异步请求-响应模式
服务A:发送消息时指定Reply-To(回调队列)和生成CorrelationId(请求唯一ID)
服务B:处理完成后将结果发回Reply-To队列,携带相同的CorrelationId
服务A:等待监听回调队列,通过CorrelationId匹配响应,并处理
(1)DirectReplyTo 的优势:自动生成临时队列(名称如 amq.rabbitmq.reply-to),无需手动声明。
(2)响应直接通过 RabbitMQ 内部通道返回,跳过交换器路由,性能更高。
服务B的响应发送:使用空字符串 "" 作为交换器名称,表示直接发送到默认的 DirectReplyTo 通道。
(3)replyTo 和 correlationId 由 RabbitMQ 自动传递,无需手动设置。
1.队列配置
@Configuration
public class RabbitConfig {
// ============== 只需配置请求端 ==============
@Bean
public DirectExchange requestExchange() {
return new DirectExchange("request.exchange", true, false);
}
@Bean
public Queue requestQueue() {
return new Queue("request-queue", true);
}
@Bean
public Binding requestBinding() {
return BindingBuilder.bind(requestQueue())
.to(requestExchange())
.with("request.routingKey");
}
// ============== 关键配置 ==============
@Bean
public RabbitTemplate rabbitTemplate(ConnectionFactory connectionFactory) {
RabbitTemplate template = new RabbitTemplate(connectionFactory);
template.setUseDirectReplyToContainer(true); // 启用 DirectReplyTo
template.setReplyTimeout(5000); // 设置超时
return template;
}
}
2.服务A实现
@Service
public class RabbitMqServiceA {
@Autowired
private RabbitTemplate rabbitTemplate;
public void sendRequest() {
String requestMessage = "Hello from ServiceA";
// 发送并接收响应(自动处理 replyTo 和 correlationId)
String response = (String) rabbitTemplate.convertSendAndReceive(
"request.exchange",
"request.routingKey",
requestMessage
);
if (response != null) {
System.out.println("[ServiceA] Received: " + response);
} else {
System.out.println("[ServiceA] Timeout or no response");
}
}
}
3.服务B实现
@Service
public class RabbitMqServiceB {
@RabbitListener(queues = "request-queue")
public String handleRequest(String requestBody) {
System.out.println("[ServiceB] Received: " + requestBody);
// 返回响应
return "Processed: " + requestBody;
}
}
363

被折叠的 条评论
为什么被折叠?



