DURABLE在queue和topic中的区别

本文深入探讨了DURABLE机制在队列和主题中的应用,解释了其如何确保消息持久性,特别关注了在主题中DURABLE如何保持每条消息的独立副本供多个持久订阅者使用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

DURABLE在queue和topic中的区别


durable是为了防止宕机等异常而导致消息无法及时接收设计的。

这个对queue无太多影响,但对topic影响比较大。

本文引用

http://activemq.apache.org/how-do-durable-queues-and-topics-work.html

 

对queue的影响

Durable queueskeep messages around persistently for any suitable consumer to consumethem. Durable queues do not need to concern themselves with whichconsumer is going to consume the messages at some point in the future.There is just one copy of a message that any consumer in the future canconsume.

 

对topic的影响

Durable topics however are different as they must logically persistan instance of each suitable message for every durable consumer - sinceeach durable consumer gets their own copy of the message.

 

对topic影响举例说明

For example imagine a durable subscriber S starts up subscribing totopic T at time D1. Some publisher sends messages M1, M2, M3 to thetopic and S will receive each of these messages. Then S is stopped andthe publisher continues to send M4, M5.

When S restarts at D2, the publisher sends M6 and M7. Now S willreceive M4, M5 followed by M6 and M7 and all future messages. i.e. Swill receive all messages from M1..M7.

This is the difference between durable and non-durable consuming. IfS were a non-durable consumer then it would only have received M1, M2,M3 and M6, M7 - not M4 and M5. i.e. because the subscription isdurable, S will receive every message sent to T whether the subscriberis running or not. For non-durable topics, only messages delivered tothe topic T when S is running are delivered.

So for durable topic subscription, the JMS provider needs to be ableto identify S when it shuts down and later on in the future reconnects,so it can know what messages to send to it while it was not running.JMS specification dictates that the identification of S is done by acombination of the clientID and the durable subscriber name. This is sothat the JMS connection S uses can have many different durablesubscriptions on different topics or on the same topic with differentselectors - yet the JMS provider can know which message for whichsubscription to keep around for it.

So setting the clientID on a JMS connection is vital (along withusing a sensible durable consumer name) for durable topic subscription.Its not an issue for other QoS

 

在topic模式下,如果设置durable为true,就要设置clientID给 JMS provider,让他来维护记录 每个订阅者接收消息状态。同时topic的订阅者没接收一条消息也要反馈一条成功接收信息给JMS provider。

<think>我们面对的需求是:在Spring RabbitMQ中配置多个交换机多个队列,并建立它们之间的绑定关系。 根据引用[2]引用[3],我们可以采用两种方式:配置类(Java Config)或者注解方式(如@Configuration@Bean)来声明交换机队列,并进行绑定。 此外,引用[1]引用[4]提到Spring Boot可以自动配置RabbitMQ,我们只需在配置文件中进行相应设置,然后通过配置类或注解来声明交换机队列。 下面我们分别介绍两种方式:基于XML配置(传统Spring项目)基于Java配置(Spring Boot项目)。 注意:用户之前的问题是关于XML配置的,但引用中提到了Spring Boot的自动配置,因此我们将分别给出两种方案。 方案一:基于XML配置(适用于传统Spring项目) 方案二:基于Java配置(适用于Spring Boot项目) 由于用户之前使用的是XML配置,我们先从XML配置开始,然后介绍Java配置。 方案一:XML配置多个交换机、队列绑定 步骤: 1. 声明多个交换机(可以是不同类型的交换机) 2. 声明多个队列 3. 将队列与交换机进行绑定,并指定路由键 示例XML配置: ```xml <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:rabbit="http://www.springframework.org/schema/rabbit" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/rabbit http://www.springframework.org/schema/rabbit/spring-rabbit.xsd"> <!-- 连接工厂 --> <rabbit:connection-factory id="connectionFactory" host="localhost" port="5672" username="guest" password="guest"/> <!-- RabbitAdmin用于创建队列、交换机等 --> <rabbit:admin connection-factory="connectionFactory"/> <!-- 声明第一个交换机(直接交换机) --> <rabbit:direct-exchange name="order.exchange"> <rabbit:bindings> <!-- 绑定队列1,指定路由键为order.key1 --> <rabbit:binding queue="order.queue1" key="order.key1"/> <!-- 绑定队列2,指定路由键为order.key2 --> <rabbit:binding queue="order.queue2" key="order.key2"/> </rabbit:bindings> </rabbit:direct-exchange> <!-- 声明第二个交换机(主题交换机) --> <rabbit:topic-exchange name="log.exchange"> <rabbit:bindings> <!-- 绑定队列3,路由键模式为log.* --> <rabbit:binding queue="log.queue" pattern="log.*"/> </rabbit:bindings> </rabbit:topic-exchange> <!-- 声明队列1 --> <rabbit:queue name="order.queue1"/> <!-- 声明队列2 --> <rabbit:queue name="order.queue2"/> <!-- 声明队列3 --> <rabbit:queue name="log.queue"/> <!-- 如果需要,可以配置多个RabbitTemplate,每个对应一个交换机(可选) --> <rabbit:template id="orderTemplate" connection-factory="connectionFactory" exchange="order.exchange"/> <rabbit:template id="logTemplate" connection-factory="connectionFactory" exchange="log.exchange"/> </beans> ``` 方案二:使用Java配置(适用于Spring Boot项目) 在Spring Boot项目中,我们可以通过配置类来声明多个交换机、队列绑定。 步骤: 1. 创建一个配置类(例如RabbitMQConfig) 2. 使用@Bean注解声明交换机 3. 使用@Bean注解声明队列 4. 使用@Bean注解声明绑定,将队列绑定到交换机,并指定路由键(或模式) 示例配置类: ```java import org.springframework.amqp.core.*; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration public class RabbitMQConfig { // 第一个交换机:订单直接交换机 @Bean public DirectExchange orderExchange() { return new DirectExchange("order.exchange"); } // 第二个交换机:日志主题交换机 @Bean public TopicExchange logExchange() { return new TopicExchange("log.exchange"); } // 队列1 @Bean public Queue orderQueue1() { return new Queue("order.queue1"); } // 队列2 @Bean public Queue orderQueue2() { return new Queue("order.queue2"); } // 队列3 @Bean public Queue logQueue() { return new Queue("log.queue"); } // 绑定:将队列1绑定到订单交换机,指定路由键为order.key1 @Bean public Binding bindingOrder1(Queue orderQueue1, DirectExchange orderExchange) { return BindingBuilder.bind(orderQueue1).to(orderExchange).with("order.key1"); } // 绑定:将队列2绑定到订单交换机,指定路由键为order.key2 @Bean public Binding bindingOrder2(Queue orderQueue2, DirectExchange orderExchange) { return BindingBuilder.bind(orderQueue2).to(orderExchange).with("order.key2"); } // 绑定:将日志队列绑定到日志交换机,指定路由模式为log.* @Bean public Binding bindingLog(Queue logQueue, TopicExchange logExchange) { return BindingBuilder.bind(logQueue).to(logExchange).with("log.*"); } } ``` 在Spring Boot中,我们还可以使用属性文件(application.yml)来配置RabbitMQ连接: ```yaml spring: rabbitmq: host: localhost port: 5672 username: guest password: guest ``` 这样,在应用启动时,Spring Boot会自动创建这些交换机、队列绑定(因为我们在配置类中通过@Bean声明了它们)[^1][^4]。 另外,引用[3]提到了一种动态创建队列交换机的方式,我们可以通过读取配置文件来动态创建多个队列交换机。这种方式适用于需要根据配置动态创建的情况,但通常我们使用上述静态配置即可。 总结: 根据需求选择配置方式: 1. 传统Spring项目:使用XML配置 2. Spring Boot项目:使用Java配置类 无论哪种方式,都可以配置多个交换机队列,并建立绑定关系。 相关问题: 1. Spring Boot中如何为不同的交换机设置不同的消息转换器? 2. 如何在RabbitMQ中配置死信交换机(DLX)? 3. 如何实现RabbitMQ消息的延迟队列? 4. 在Spring中如何监听多个RabbitMQ队列? 5. 如何配置RabbitMQ消息的手动确认(ack)? 6. 如何实现RabbitMQ消息的持久化?</think>### Spring RabbitMQ 配置多交换机与队列绑定指南 在 Spring RabbitMQ 中配置多个交换机队列绑定,主要有以下两种实现方式: --- ### 方案一:Java 配置类声明(推荐) 通过 `@Configuration` 类声明交换机队列绑定关系[^2] ```java import org.springframework.amqp.core.*; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration public class RabbitMQConfig { // ========== 订单交换机配置 ========== @Bean public DirectExchange orderExchange() { return new DirectExchange("order.exchange"); } @Bean public Queue orderQueue() { return new Queue("order.queue"); } @Bean public Binding orderBinding() { return BindingBuilder.bind(orderQueue()) .to(orderExchange()) .with("order.key"); } // ========== 日志交换机配置 ========== @Bean public TopicExchange logExchange() { return new TopicExchange("log.exchange"); } @Bean public Queue logQueue() { return new Queue("log.queue"); } @Bean public Binding logBinding() { return BindingBuilder.bind(logQueue()) .to(logExchange()) .with("log.#"); } // ========== 通知交换机配置 ========== @Bean public FanoutExchange notificationExchange() { return new FanoutExchange("notification.exchange"); } @Bean public Queue emailQueue() { return new Queue("email.queue"); } @Bean public Queue smsQueue() { return new Queue("sms.queue"); } @Bean public Binding emailBinding() { return BindingBuilder.bind(emailQueue()) .to(notificationExchange()); } @Bean public Binding smsBinding() { return BindingBuilder.bind(smsQueue()) .to(notificationExchange()); } } ``` --- ### 方案二:XML 配置方式 适用于传统 Spring 项目[^1] ```xml <rabbit:admin connection-factory="connectionFactory"/> <!-- 订单交换机配置 --> <rabbit:direct-exchange name="order.exchange"> <rabbit:bindings> <rabbit:binding queue="order.queue" key="order.key"/> </rabbit:bindings> </rabbit:direct-exchange> <rabbit:queue name="order.queue"/> <!-- 日志交换机配置 --> <rabbit:topic-exchange name="log.exchange"> <rabbit:bindings> <rabbit:binding queue="log.queue" pattern="log.#"/> </rabbit:bindings> </rabbit:topic-exchange> <rabbit:queue name="log.queue"/> <!-- 通知交换机配置 --> <rabbit:fanout-exchange name="notification.exchange"> <rabbit:bindings> <rabbit:binding queue="email.queue"/> <rabbit:binding queue="sms.queue"/> </rabbit:bindings> </rabbit:fanout-exchange> <rabbit:queue name="email.queue"/> <rabbit:queue name="sms.queue"/> ``` --- ### 动态配置(基于配置文件) 通过 YAML 配置动态创建交换机队列[^3] ```yaml spring: rabbitmq: dynamic: bindings: - exchange: order.exchange type: direct queues: - name: order.queue routing-key: order.key - exchange: log.exchange type: topic queues: - name: log.queue routing-pattern: log.# - exchange: notification.exchange type: fanout queues: - name: email.queue - name: sms.queue ``` --- ### Spring Boot 自动配置 在 Spring Boot 中只需声明 Bean,自动创建基础设施[^4] ```java // 生产者示例 @Service public class MessageSender { private final RabbitTemplate rabbitTemplate; public void sendOrder(Order order) { rabbitTemplate.convertAndSend( "order.exchange", // 交换机名称 "order.key", // 路由键 order // 消息内容 ); } public void sendLog(String log) { rabbitTemplate.convertAndSend( "log.exchange", // 另一个交换机 "log.system.error", // 匹配log.#的路由键 log ); } } ``` --- ### 最佳实践建议 1. **交换机类型选择**: - 1:1 精确路由 → Direct Exchange - 1:N 广播路由 → Fanout Exchange - 模式匹配路由 → Topic Exchange 2. **绑定关系设计**: ```java // 多路由键绑定示例 @Bean public Binding multiKeyBinding() { return new Binding("queue.name", Binding.DestinationType.QUEUE, "exchange.name", "key1", null); // 可添加多个绑定 } ``` 3. **消费者配置**: ```java @RabbitListener(queues = {"order.queue", "log.queue"}) public void handleMessages(Message message) { // 同时监听多个队列 } ``` --- ### 常见问题解决方案 1. **交换机未创建错误**: - 确保配置 `@EnableRabbit` - 检查 RabbitAdmin 是否已配置 2. **绑定关系失效**: ```java // 显式声明绑定关系 @Bean public Declarables declarables() { return new Declarables( new DirectExchange("order.exchange"), new Queue("order.queue"), BindingBuilder.bind(new Queue("order.queue")) .to(new DirectExchange("order.exchange")) .with("order.key") ); } ``` 3. **消息路由失败**: - 使用备用交换机配置: ```java @Bean public DirectExchange mainExchange() { return ExchangeBuilder.directExchange("main.exchange") .withArgument("alternate-exchange", "backup.exchange") .build(); } ``` --- ### 相关问题 1. 如何为不同的交换机配置不同的消息转换器? 2. RabbitMQ 如何实现多级交换机路由(如死信交换机)? 3. Spring Boot 中如何动态创建临时队列? 4. 如何为不同的交换机设置独立的消息确认机制? 5. RabbitMQ 如何实现交换机之间的桥接(federation)? 6. 如何在多交换机环境下配置消息的优先级路由? > 提示:在微服务架构中,建议为每个业务域(订单、日志、通知)使用独立的交换机,通过路由键实现精细控制[^1][^3]。生产环境建议配合 Spring Cloud Stream 实现更高级的消息路由功能。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值