|
该版本仍在开发中,尚未被视为稳定。请使用最新的稳定版本,使用 Spring AMQP 4.0.0! |
配置代理
AMQP规范描述了该协议如何用于在代理上配置队列、交换和绑定。
这些作(可从0.8及以上规范移植)存在于AmqpAdmin接口org.springframework.amqp.core包。
RabbitMQ 对该类的实现为兔子管理员位于org.springframework.amqp.rabbit.core包。
这AmqpAdmin接口基于使用 Spring AMQP 域抽象,具体如下列表所示:
public interface AmqpAdmin {
// Exchange Operations
void declareExchange(Exchange exchange);
void deleteExchange(String exchangeName);
// Queue Operations
Queue declareQueue();
String declareQueue(Queue queue);
void deleteQueue(String queueName);
void deleteQueue(String queueName, boolean unused, boolean empty);
void purgeQueue(String queueName, boolean noWait);
// Binding Operations
void declareBinding(Binding binding);
void removeBinding(Binding binding);
Properties getQueueProperties(String queueName);
}
另见范围作。
这getQueueProperties()方法返回一些关于队列的有限信息(消息计数和消费者计数)。
返回的属性键以常量形式存在于兔子模板 (QUEUE_NAME,QUEUE_MESSAGE_COUNT和QUEUE_CONSUMER_COUNT).
RabbitMQ REST API 提供了更多关于队列信息对象。
无arg声明队列()方法在经纪人上定义了一个自动生成的队列名称。
该自动生成队列的附加属性包括exclusive=true,autoDelete=true和durable=false.
这声明队列(队列队列)方法取队列对象,返回声明队列的名称。
如果名称所提供的财产队列是空的字符串,经纪人用生成的名称声明队列。
该名字会被退回给来电者。
该名称也被添加到实际名称的属性队列.
你只能通过调用兔子管理员径直。
当管理员在应用上下文中声明式定义队列时使用自动声明,你可以将name属性设置为(空字符串)。
经纪人随后创建名称。
从2.1版本开始,监听器容器可以使用此类队列。
更多信息请参见容器和代理命名队列。""
这与匿名队列其中框架生成唯一的(UUID)名称和集合耐用自false和独家,自动删除自true.
一个<兔子:队列/>其中空(或缺失)名称属性总是生成匿名队列.
看匿名队列理解原因匿名队列优先于经纪人生成的队列名称,以及
如何控制名称的格式。
从2.1版本开始,匿名队列声明时参数为Queue.X_QUEUE_LEADER_LOCATOR设置为客户端-本地默认。
这确保了队列在应用连接的节点上被声明。
声明式队列必须有固定名称,因为它们可能在上下文中的其他地方被引用——例如在
以下示例中显示的监听者:
<rabbit:listener-container>
<rabbit:listener ref="listener" queue-names="#{someQueue.name}" />
</rabbit:listener-container>
RabbitMQ 对该接口的实现是兔子管理员,当它通过 Spring XML 配置时,类似于以下示例:
<rabbit:connection-factory id="connectionFactory"/>
<rabbit:admin id="amqpAdmin" connection-factory="connectionFactory"/>
当缓存连接工厂缓存模式为渠道(默认值),该兔子管理员实现会自动懒惰声明队列、交换和绑定,在同一节点内声明应用上下文.
这些组成部分被声明为连接向经纪人开放。
有些命名空间特性使得这一点非常方便——例如,
在股票示例应用中,我们有以下内容:
<rabbit:queue id="tradeQueue"/>
<rabbit:queue id="marketDataQueue"/>
<fanout-exchange name="broadcast.responses"
xmlns="http://www.springframework.org/schema/rabbit">
<bindings>
<binding queue="tradeQueue"/>
</bindings>
</fanout-exchange>
<topic-exchange name="app.stock.marketdata"
xmlns="http://www.springframework.org/schema/rabbit">
<bindings>
<binding queue="marketDataQueue" pattern="${stocks.quote.pattern}"/>
</bindings>
</topic-exchange>
在前面的例子中,我们使用匿名队列(实际上是框架内部生成的队列,而非经纪人生成的队列),并用ID来指代。 我们也可以声明带有显式名称的队列,这些队列在上下文中也作为其 bean 定义的标识符。 以下示例配置了一个带有显式名称的队列:
<rabbit:queue name="stocks.trade.queue"/>
你可以同时提供两者身份证和名称属性。
这允许你用一个独立于队列名称的ID来指代队列(例如在绑定中)。
它还支持标准的 Spring 功能(如属性占位符和队列名称的 SpEL 表达式)。
当你用名字作为豆子标识时,这些功能就无法使用。 |
队列可以通过额外参数配置——例如,x-message-ttl.
当你使用命名空间支持时,它们以地图参数-名称/参数-值对的定义,定义方式为<rabbit:queue-arguments>元素。
以下示例展示了如何实现:
<rabbit:queue name="withArguments">
<rabbit:queue-arguments>
<entry key="x-dead-letter-exchange" value="myDLX"/>
<entry key="x-dead-letter-routing-key" value="dlqRK"/>
</rabbit:queue-arguments>
</rabbit:queue>
默认情况下,参数被假定为字符串。 对于其他类型的论元,你必须提供类型。 以下示例展示了如何指定类型:
<rabbit:queue name="withArguments">
<rabbit:queue-arguments value-type="java.lang.Long">
<entry key="x-message-ttl" value="100"/>
</rabbit:queue-arguments>
</rabbit:queue>
在提供混合类型的参数时,必须为每个元素提供类型。 以下示例展示了如何实现:
<rabbit:queue name="withArguments">
<rabbit:queue-arguments>
<entry key="x-message-ttl">
<value type="java.lang.Long">100</value>
</entry>
<entry key="x-dead-letter-exchange" value="myDLX"/>
<entry key="x-dead-letter-routing-key" value="dlqRK"/>
</rabbit:queue-arguments>
</rabbit:queue>
在 Spring Framework 3.2 及以后版本中,这一点可以更简洁地声明如下:
<rabbit:queue name="withArguments">
<rabbit:queue-arguments>
<entry key="x-message-ttl" value="100" value-type="java.lang.Long"/>
<entry key="x-ha-policy" value="all"/>
</rabbit:queue-arguments>
</rabbit:queue>
当你使用 Java 配置时,Queue.X_QUEUE_LEADER_LOCATOR论证作为一类属性支持,通过setLeaderLocator()方法队列类。
从2.1版本开始,匿名队列声明时将该属性设置为客户端-本地默认。
这确保队列在应用连接的节点上被声明。
RabbitMQ 代理不允许在参数不匹配的情况下声明队列。
例如,如果队列已经存在于活着的时候你尝试用(例如)来声明它key=“x-message-ttl” value=“100”,抛出一个例外。 |
默认情况下,兔子管理员一旦出现异常,立即停止处理所有声明。
这可能导致下游问题,比如监听器容器因未声明错误队列(错误之后定义的)而未能初始化。
这种行为可以通过设置忽略-声明-异常归属为true在兔子管理员实例。
该选项指示兔子管理员记录异常并继续声明其他元素。
在配置兔子管理员使用 Java 时,该性质称为ignoreDeclarationExceptions.
这是一个适用于所有元素的全局设置。
队列、交换和绑定具有类似的属性,仅适用于这些元素。
在1.6版本之前,这一属性仅在IOException发生在通道上,比如电流与期望属性不匹配时。
此特性适用于任何例外,包括超时例外以及其他。
此外,任何声明例外都会导致发布DeclarationExceptionEvent,即ApplicationEvent可以被任意 吞噬ApplicationListener在语境中。
该事件包含对管理员的引用、正在声明的元素以及可投掷.
头部交换
从1.3版本开始,你可以配置头部交换以便在多个头部匹配。
你也可以指定是否必须匹配任何或所有的头部。
以下示例展示了如何实现:
<rabbit:headers-exchange name="headers-test">
<rabbit:bindings>
<rabbit:binding queue="bucket">
<rabbit:binding-arguments>
<entry key="foo" value="bar"/>
<entry key="baz" value="qux"/>
<entry key="x-match" value="all"/>
</rabbit:binding-arguments>
</rabbit:binding>
</rabbit:bindings>
</rabbit:headers-exchange>
从1.6版本开始,你可以配置交流带有内部标志(默认为false)以及交换通过兔子管理员(如果在应用上下文中存在的话)。
如果内部旗帜是true对于交换,RabbitMQ不允许客户端使用交换机。
这对于死信交换或交换间绑定非常有用,因为你不希望使用交换
直接由出版商提供。
想了解如何用 Java 配置 AMQP 基础设施,请查看 Stock 示例应用,
其中@Configuration类摘要StockRabbit配置,而该 又 有RabbitClientConfiguration和RabbitServerConfiguration子。
以下列表显示了摘要StockRabbit配置:
@Configuration
public abstract class AbstractStockAppRabbitConfiguration {
@Bean
public CachingConnectionFactory connectionFactory() {
CachingConnectionFactory connectionFactory =
new CachingConnectionFactory("localhost");
connectionFactory.setUsername("guest");
connectionFactory.setPassword("guest");
return connectionFactory;
}
@Bean
public RabbitTemplate rabbitTemplate() {
RabbitTemplate template = new RabbitTemplate(connectionFactory());
template.setMessageConverter(jsonMessageConverter());
configureRabbitTemplate(template);
return template;
}
@Bean
public Jackson2JsonMessageConverter jsonMessageConverter() {
return new Jackson2JsonMessageConverter();
}
@Bean
public TopicExchange marketDataExchange() {
return new TopicExchange("app.stock.marketdata");
}
// additional code omitted for brevity
}
在 Stock 应用中,服务器的配置方式如下@Configuration类:
@Configuration
public class RabbitServerConfiguration extends AbstractStockAppRabbitConfiguration {
@Bean
public Queue stockRequestQueue() {
return new Queue("app.stock.request");
}
}
这是整个继承链的终点@Configuration类。
最终结果是话题交流和队列在申请启动时向经纪人声明。
没有约束话题交流在服务器配置中,这在客户端应用中完成。
然而,股票请求队列会自动绑定到AMQP默认交易所。
这种行为由规范定义。
客户端@Configuration课程稍微有趣一些。
其声明如下:
@Configuration
public class RabbitClientConfiguration extends AbstractStockAppRabbitConfiguration {
@Value("${stocks.quote.pattern}")
private String marketDataRoutingKey;
@Bean
public Queue marketDataQueue() {
return amqpAdmin().declareQueue();
}
/**
* Binds to the market data exchange.
* Interested in any stock quotes
* that match its routing key.
*/
@Bean
public Binding marketDataBinding() {
return BindingBuilder.bind(
marketDataQueue()).to(marketDataExchange()).with(marketDataRoutingKey);
}
// additional code omitted for brevity
}
客户端通过声明队列()方法AmqpAdmin.
它通过外部化的路由模式将队列绑定到市场数据交换中,并外部化为属性文件。
队列和交换的构建器 API
1.6 版本引入了一个方便的流 API 配置队列和交换当使用 Java 配置时,对象。
以下示例展示了如何使用它:
@Bean
public Queue queue() {
return QueueBuilder.nonDurable("foo")
.autoDelete()
.exclusive()
.withArgument("foo", "bar")
.build();
}
@Bean
public Exchange exchange() {
return ExchangeBuilder.directExchange("foo")
.autoDelete()
.internal()
.withArgument("foo", "bar")
.build();
}
请参见 Javadocorg.springframework.amqp.core.QueueBuilder和org.springframework.amqp.core.ExchangeBuilder更多信息请见。
从2.0版本开始,交换构建器现在默认地创建持久交换,以符合个体的简单构造函数摘要交流类。
要与建造者进行非持久交换,使用.durable(false)在召唤之前.build().
这durable()不再提供无参数的方法。
2.2版本引入了流流API,用于添加“知名”的交换和队列参数......
@Bean
public Queue allArgs1() {
return QueueBuilder.nonDurable("all.args.1")
.ttl(1000)
.expires(200_000)
.maxLength(42)
.maxLengthBytes(10_000)
.overflow(Overflow.rejectPublish)
.deadLetterExchange("dlx")
.deadLetterRoutingKey("dlrk")
.maxPriority(4)
.lazy()
.leaderLocator(LeaderLocator.minLeaders)
.singleActiveConsumer()
.build();
}
@Bean
public DirectExchange ex() {
return ExchangeBuilder.directExchange("ex.with.alternate")
.durable(true)
.alternate("alternate")
.build();
}
声明交换、队列和绑定的集合
你可以将 的集合包裹起来可宣告对象 (队列,交换和捆绑)可申报者对象。
这兔子管理员检测此类豆子(以及离散的可宣告在应用上下文中,并且每当建立连接(最初或连接失败后)时,都会在代理上声明所包含的对象。
以下示例展示了如何实现:
@Configuration
public static class Config {
@Bean
public CachingConnectionFactory cf() {
return new CachingConnectionFactory("localhost");
}
@Bean
public RabbitAdmin admin(ConnectionFactory cf) {
return new RabbitAdmin(cf);
}
@Bean
public DirectExchange e1() {
return new DirectExchange("e1", false, true);
}
@Bean
public Queue q1() {
return new Queue("q1", false, false, true);
}
@Bean
public Binding b1() {
return BindingBuilder.bind(q1()).to(e1()).with("k1");
}
@Bean
public Declarables es() {
return new Declarables(
new DirectExchange("e2", false, true),
new DirectExchange("e3", false, true));
}
@Bean
public Declarables qs() {
return new Declarables(
new Queue("q2", false, false, true),
new Queue("q3", false, false, true));
}
@Bean
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public Declarables prototypes() {
return new Declarables(new Queue(this.prototypeQueueName, false, false, true));
}
@Bean
public Declarables bs() {
return new Declarables(
new Binding("q2", DestinationType.QUEUE, "e2", "k2", null),
new Binding("q3", DestinationType.QUEUE, "e3", "k3", null));
}
@Bean
public Declarables ds() {
return new Declarables(
new DirectExchange("e4", false, true),
new Queue("q4", false, false, true),
new Binding("q4", DestinationType.QUEUE, "e4", "k4", null));
}
}
在2.1之前的版本中,你可以声明多个可宣告通过定义类型的豆子来实现实例催收<可申报>.
这在某些情况下可能导致不良副作用,因为管理员必须对整体进行迭代收藏<?>豆。 |
2.2 版本增加了getDeClarablesByType方法可申报者;例如,在宣告监听器容器豆时,这可以作为方便。
public SimpleMessageListenerContainer container(ConnectionFactory connectionFactory,
Declarables mixedDeclarables, MessageListener listener) {
SimpleMessageListenerContainer container = new SimpleMessageListenerContainer(connectionFactory);
container.setQueues(mixedDeclarables.getDeclarablesByType(Queue.class).toArray(new Queue[0]));
container.setMessageListener(listener);
return container;
}
有条件声明
默认情况下,所有队列、交换和绑定都由所有用户声明兔子管理员实例(假设它们有auto-startup=“true”)在应用语境中。
从2.1.9版本开始,兔子管理员拥有一个新房产仅限明确声明(即false默认情况下;当true管理员只会声明那些被明确配置为由该管理员声明的豆子。
| 从1.2版本开始,你可以有条件地声明这些元素。 当应用程序连接多个代理并需要指定某个元素应通过哪个代理声明时,这尤其有用。 |
表示这些元素的类实现了可宣告,该方法有两种:应宣告()和getDeclaringAdmins().
这兔子管理员利用这些方法判断某个实例是否应在其上处理声明连接.
这些属性作为命名空间中的属性可用,如下示例所示:
<rabbit:admin id="admin1" connection-factory="CF1" />
<rabbit:admin id="admin2" connection-factory="CF2" />
<rabbit:admin id="admin3" connection-factory="CF3" explicit-declarations-only="true" />
<rabbit:queue id="declaredByAdmin1AndAdmin2Implicitly" />
<rabbit:queue id="declaredByAdmin1AndAdmin2" declared-by="admin1, admin2" />
<rabbit:queue id="declaredByAdmin1Only" declared-by="admin1" />
<rabbit:queue id="notDeclaredByAllExceptAdmin3" auto-declare="false" />
<rabbit:direct-exchange name="direct" declared-by="admin1, admin2">
<rabbit:bindings>
<rabbit:binding key="foo" queue="bar"/>
</rabbit:bindings>
</rabbit:direct-exchange>
默认情况下,自动宣告属性为true且,如果宣告者如果没有被供给(或为空),则所有兔子管理员实例声明对象(只要管理员的自动启动属性为true、默认值,以及管理员的仅限显式声明属性为假)。 |
同样,你也可以用基于Java的@Configuration以达到同样的效果。
在下面的例子中,分量声明为管理员1但不是管理员2:
@Bean
public RabbitAdmin admin1() {
return new RabbitAdmin(cf1());
}
@Bean
public RabbitAdmin admin2() {
return new RabbitAdmin(cf2());
}
@Bean
public Queue queue() {
Queue queue = new Queue("foo");
queue.setAdminsThatShouldDeclare(admin1());
return queue;
}
@Bean
public Exchange exchange() {
DirectExchange exchange = new DirectExchange("bar");
exchange.setAdminsThatShouldDeclare(admin1());
return exchange;
}
@Bean
public Binding binding() {
Binding binding = new Binding("foo", DestinationType.QUEUE, exchange().getName(), "foo", null);
binding.setAdminsThatShouldDeclare(admin1());
return binding;
}
关于身份证和名称属性
这名称属性<兔子:队列/>和<兔子:交换/>元素反映经纪人中实体的名称。
对于队列,如果名称省略后创建一个匿名队列(参见匿名队列).
在2.0之前的版本中,名称也以Bean的名字别名注册(类似于名称上<豆/>元素)。
这带来了两个问题:
-
它阻止了同名队列和交换的声明。
-
如果别名包含 SpEL 表达式,则无法解析 (
#{…}).
从2.0版本开始,如果你声明其中一个元素同时具有身份证 以及一个名称属性,这个名字不再被宣告为豆子名字别名。
如果你想声明队列并与同伴交换名称你必须提供一个身份证.
如果元素只有 一个,则没有变化名称属性。
豆子仍可被名称——例如,在具有约束力的声明中。
然而,如果名称包含 SpEL,你仍然无法引用它——你必须提供身份证供参考参考。
匿名队列
一般来说,当您需要一个唯一命名的、独占的自动删除队列时,我们建议你使用匿名队列而非由经纪人定义的队列名称(使用""队列名称导致经纪人生成队列
名字)。
这是因为:
-
队列实际上是在与经纪人建立连接时声明的。 这发生在豆子被制作并接线很久之后。 使用队列的豆子需要知道它的名字。 事实上,当应用启动时,代理可能根本没有运行。
-
如果与代理的连接因某种原因丢失,管理员会重新声明
匿名队列同名。 如果使用经纪人声明的队列,队列名称会改变。
你可以控制队列名称的格式,由匿名队列实例。
默认情况下,队列名称前缀为spring.gen-随后是 base64 表示UUID——例如:spring.gen-MRBv9sqISkuCiPfOYfpo4g.
你可以提供一个匿名队列.命名策略构造函数参数中的实现。
以下示例展示了如何实现:
@Bean
public Queue anon1() {
return new AnonymousQueue();
}
@Bean
public Queue anon2() {
return new AnonymousQueue(new AnonymousQueue.Base64UrlNamingStrategy("something-"));
}
@Bean
public Queue anon3() {
return new AnonymousQueue(AnonymousQueue.UUIDNamingStrategy.DEFAULT);
}
第一个 bean 生成一个队列名称,前缀为spring.gen-随后是 base64 表示UUID——
例:spring.gen-MRBv9sqISkuCiPfOYfpo4g.
第二个 bean 生成一个队列名称,前缀为东西-随后是 base64 表示UUID.
第三个豆子仅使用UUID(无base64转换)生成名称——例如,F20C818A-006B-4416-BF91-643590FEDB0e.
base64编码使用RFC 4648中的“URL和文件名安全字母表”。
后方填充字符()被移除。=
你可以提供自己的命名策略,比如在队列名称中包含其他信息(比如应用程序名称或客户端主机)。
你可以在使用 XML 配置时指定命名策略。
这命名策略属性存在于<兔子:排队>元素
对于实现匿名队列.命名策略.
以下示例展示了如何以多种方式指定命名策略:
<rabbit:queue id="uuidAnon" />
<rabbit:queue id="springAnon" naming-strategy="uuidNamer" />
<rabbit:queue id="customAnon" naming-strategy="customNamer" />
<bean id="uuidNamer" class="org.springframework.amqp.core.AnonymousQueue.UUIDNamingStrategy" />
<bean id="customNamer" class="org.springframework.amqp.core.AnonymousQueue.Base64UrlNamingStrategy">
<constructor-arg value="custom.gen-" />
</bean>
第一个例子产生了如下名称spring.gen-MRBv9sqISkuCiPfOYfpo4g.
第二个例子用字符串表示UUID来创建名称。
第三个例子产生了如下名称custom.gen-MRBv9sqISkuCiPfOYfpo4g.
你也可以提供自己的命名策略。
从2.1版本开始,匿名队列声明时参数为Queue.X_QUEUE_LEADER_LOCATOR设置为客户端-本地默认。
这确保了队列在应用连接的节点上被声明。
你可以通过呼叫恢复到之前的行为queue.setLeaderLocator(null)在构建实例之后。
恢复自动删除声明
通常,兔子管理员(s) 仅恢复在应用上下文中声明为 BEANS 的队列/交换/绑定;如果有任何此类声明被自动删除,连接丢失时经纪人将会删除这些声明。
连接重新建立后,管理员将重新声明这些实体。
通常,通过调用创建的实体admin.declareQueue(...),admin.declareExchange(...)和admin.declareBinding(...)将无法恢复。
从2.4版本开始,管理员拥有了一个新的属性redeclareManualDeclarations;什么时候true管理员会在应用上下文中恢复这些实体以及豆子。
如果deleteQueue(...),deleteExchange(...)或移除绑定(...)被叫去。
当队列和交换被删除时,相关绑定会从可恢复实体中移除。
终于,打电话resetAllManualDeclarations()将阻止任何先前申报实体的追回。