配置消息代理服务器-AmqpAdmin
AMQP规范描述了在消息代理中如何利用协议来配置队列,交换和绑定。这些操作可以使用org.springframework.amqp.core package中的AmqpAdmin接口来完成,这些操作从0.8版本规范到现在为止是通用的。
AmqpAdmin接口是基于Spring AMQP高度抽象的:
public interfaceAmqpAdmin {// Exchange Operationsvoid declareExchange(Exchange exchange);void deleteExchange(String exchangeName);// Queue OperationsQueue declareQueue();String declareQueue(Queue queue);void deleteQueue(String queueName);void deleteQueue(String queueName, booleanunused, booleanempty);void purgeQueue(String queueName, booleannoWait);// Binding Operationsvoid declareBinding(Binding binding);void removeBinding(Binding binding);Properties getQueueProperties(String queueName);}
无参方法declareQueue()在代理商定义队列,这个队列的名称是自动生成的。自动生成的队列的附件属性还有exclusive=true,autoDelete=true,durable=false。
declareQueue(Queue queue)接收Queue对象作为参数,并且返回queue的名称。如果你想让代理生成名称,这很有帮助。这和AnonymousQueue行车对比,它将生成UUID名称,并且设置exclusive和autoDelete属性为true。如果提供的队列的名称是空的,那么代理在声明队列的时候会生成名称,并且将名称返回给调用者。Queue自身并没有变化。这个方法只能通过RabbitAdmin编码直接调用。它不支持在应用程序上下文中自动生成。使用<rabbit:queue/>name属性设置为空或者不设置会创建AnonymousQueue。这是因为如果连接失败,重新声明的名称将会不一样。所以声明的名称应该固定,因为它们会在上下文的任何地方使用,例如,监听器:
<rabbit:listener-container><rabbit:listener ref="listener" queue-names="#{someQueue.name}"/></rabbit:listener-container>
RabbitMQ对AmqpAdmin的实现是RabbitAdmin,使用XML配置如下:
<rabbit:connection-factory id="connectionFactory"/><rabbit:admin id="amqpAdmin" connection-factory="connectionFactory"/>
当CachingConnectionFactory的缓存模式是CHANNEL,RabbitAdmin的实现将自动迟声明在一个应用上下文的Queues,Exchanges,Bindings。这些组件将在打开与消息代理的连接时声明。有一些方便的命名空间,使得这些变得很方便:
<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>
在上面的这个例子中,我们使用了匿名队列(实质上是框架帮我们生成队列的名称,而不是代理),通过引用来引用它们。当然我们可以在声明队列的时候指定队列的名称,当然这个名称同样作为引用这些bean的标识。例如:
<rabbit:queue name="stocks.trade.queue"/>
提示:
你可以同时提供id和name属性,这样你可以通过id引用这个队列。它同样允许一些标准的Spring特色,例如属性占位符或者SpEL作为Queue的name;这些属性在使用name作为标识符时候不可用。
队列还可以配置一些额外的参数,例如x-message-ttl,x-ha-policy。使用命名空间的支持它们可以通过使用<rabbit:queue-arguments>元素以键值对的方式提供:
<rabbit:queue name="withArguments"><rabbit:queue-arguments><entry key="x-ha-policy" value="all"/></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>
对于混合类型,我们需要为每一个entry提供:
<rabbit:queue name="withArguments"><rabbit:queue-arguments><entry key="x-message-ttl"><value type="java.lang.Long">100</value></entry><entry key="x-ha-policy" value="all"/></rabbit:queue-arguments></rabbit:queue>
在Spring Framework3.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>
重点:
RabbitMQ代理不允许声明参数不一致的队列。例如,如果一个队列已经存在,没有time to live 参数,紧接着你又试图去使用下列参数去声明一个队列 key="x-message-ttl" value="100",将会有异常抛出。
默认的情况下,RabbitAdmin会立马停止处理所有的声明,如果有异常出现。这样将引起连锁反应,例如监听容器因为队列没有声明而导致初始化失败。
这个问题可以通过设置RabbitAdmin的ignore-declaration-failures为true来完成。这一设置将会导致RabbitAdmin记录异常,并且继续声明其他的元素。
自从1.3版本开始,可以配置HeaderExchange...(没明白它的作用)
<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>
如何通过Java来配置AMQP框架,看看下面的例子,使用@Configuration类AbstractStockRabbitConfiguration,它有RabbitClientConfiguration和RabbitServerConfiguration两个子类。AbstractStockRabbitConfiguration的代码如下:
@Configurationpublic abstract class AbstractStockAppRabbitConfiguration {@Beanpublic ConnectionFactory connectionFactory() {CachingConnectionFactory connectionFactory =newCachingConnectionFactory("localhost");connectionFactory.setUsername("guest");connectionFactory.setPassword("guest");returnconnectionFactory;}@Beanpublic RabbitTemplate rabbitTemplate() {RabbitTemplate template = newRabbitTemplate(connectionFactory());template.setMessageConverter(jsonMessageConverter());configureRabbitTemplate(template);returntemplate;}@Beanpublic MessageConverter jsonMessageConverter() {return newJsonMessageConverter();}@Beanpublic TopicExchange marketDataExchange() {return newTopicExchange("app.stock.marketdata");}// additional code omitted for brevity}
在这个例子中,Server的配置如下:
@Configurationpublic class RabbitServerConfiguration extends AbstractStockAppRabbitConfiguration {@Beanpublic Queue stockRequestQueue() {return new Queue("app.stock.request");}}
这是@Configuration类继承链的最末端。最末端结果是当应用启动的时候TopicExchange和Queue将被声明。在Server的配置中没有绑定TopicExchange和Queue,这些将在客户端应用中做。这里队列被绑定到默认的Exchange,这种定义是规范中定义的。
客户端的配置稍微复杂一些:
@Configurationpublic class RabbitClientConfiguration extends AbstractStockAppRabbitConfiguration {@Value("${stocks.quote.pattern}")private String marketDataRoutingKey;@Beanpublic Queue marketDataQueue() {return amqpAdmin().declareQueue();}/*** Binds to the market data exchange. Interested in any stock quotes* that match its routing key.*/@Beanpublic Binding marketDataBinding() {return BindingBuilder.bind(marketDataQueue()).to(marketDataExchange()).with(marketDataRoutingKey);}// additional code omitted for brevity}
条件声明
默认情况下,queues,exchanges和bindings通过RabbitAdmin实例进行声明,RabbitAdmin实例随着应用上下文的启动创建。
注意:
自从1.2版本发以来,条件声明这些元素成为可能。当一个应用连接到多个代理的时候,这会特别有用,你需要指明你要声明元素的代理。
实现Declarable接口的类,代表着这些元素,它提供了两个方法:shouldDeclare()和getDeclaringAdmins()。RabbitAdmin使用这些方法来决定一个实例是否应该在当前连接上处理声明。
这些属性可以在命名空间中进行配置。
<rabbit:admin id="admin1" connection-factory="CF1"/><rabbit:admin id="admin2" connection-factory="CF2"/><rabbit:queue id="declaredByBothAdminsImplicitly"/><rabbit:queue id="declaredByBothAdmins" declared-by="admin1, admin2"/><rabbit:queue id="declaredByAdmin1Only" declared-by="admin1"/><rabbit:queue id="notDeclaredByAny" 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>
注意,auto-declare属性默认情况下为true,如果declare-by属性没有提供,那么所有的RabbitAdmin都将声明这个实体。
同样,可以基于@Configuration进行配置。
@Beanpublic RabbitAdmin admin() {RabbitAdmin rabbitAdmin = newRabbitAdmin(cf1());rabbitAdmin.afterPropertiesSet();returnrabbitAdmin;}@Beanpublic RabbitAdmin admin2() {RabbitAdmin rabbitAdmin = newRabbitAdmin(cf2());rabbitAdmin.afterPropertiesSet();returnrabbitAdmin;}@Beanpublic Queue queue() {Queue queue = newQueue("foo");queue.setAdminsThatShouldDeclare(admin());returnqueue;}@Beanpublic Exchange exchange() {DirectExchange exchange = newDirectExchange("bar");exchange.setAdminsThatShouldDeclare(admin());returnexchange;}@Beanpublic Binding binding() {Binding binding = newBinding("foo", DestinationType.QUEUE, exchange().getName(), "foo",null);binding.setAdminsThatShouldDeclare(admin());returnbinding;}
AMQP规范与SpringAMQP中的消息代理配置
本文详细介绍了AMQP规范下利用SpringAMQP进行消息代理配置的方法,包括队列、交换器和绑定的操作,以及如何通过XML和Java进行配置。还讨论了命名空间、参数配置和条件声明等高级特性。
1976

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



