目录
官方地址 4.1.11. Configuring the Broker
通过rabbitAdmin设置Headers Exchange
官方地址 4.1.11. Configuring the Broker
RabbitAdmin是什么角色?
AMQP规范中指定了如何配置queues, exchanges, and bindings on the broker.这部分操作表现在org.springframework.amqp.core包下的AmqpAdmin接口(适配0.8及以上规范),
RabbitMQ 实现类是
org.springframework.amqp.rabbit.core包下的RabbitAdmin
。
总结一下就是:AmqpAdmin接口定义了如何配置queues,exchanges,and bindings on the broker.RabbitAdmin
实现了这个接口,做了具体操作。即RabbitAdmin
的作用是声明队列,交换机在broker上的绑定关系。
AmqpAdmin定义了哪些接口?
public interface AmqpAdmin {
// Exchange Operations
// 声明交换机
void declareExchange(Exchange exchange);
// 删除交换机
void deleteExchange(String exchangeName);
// Queue Operations
/**
*声明队列,队列名字自动生成
* 队列属性 exclusive=true, autoDelete=true, and durable=false
*/
Queue declareQueue();
/**
* 声明队列 返回队列名称
* name属性为空的队列称为 AnonymousQueue,匿名队列会生成一个uuid名称,
* durable=false,exclusive=true,autoDelete=true
* 2.1以后,匿名内部类会将x-queue-master-locator设置为client-local
*/
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);
/**获取队列属性
*队列消息count 消费者count ...
*/
Properties getQueueProperties(String queueName);
}
默认实现类RabbitAdmin
基于spring xml的配置:
<rabbit:connection-factory id="connectionFactory"/>
<rabbit:admin id="amqpAdmin" connection-factory="connectionFactory"/>
当CachingConnectionFactory设置为CHANNEL模式时,RabbitAdmin会自动在同一个上下文applicationContext中延迟(当第一个连接连通broker时)声明队列,交换机和绑定关系。
通过rabbitAdmin设置忽略声明异常以及监听异常信息
默认情况下,RabbitAdmin
会在有异常发生时直接停止所有的组件声明,这会导致一个队列的声明异常影响后续队列的声明。为了避免这种情况,可以将RabbitAdmin的属性
ignore-declaration-exceptions设置为true,这个设置使得RabbitAdmin可以打印异常日志然后接着声明其他元素。在java中这个属性key为ignoreDeclarationExceptions,在异常发生时,会发布一个DeclarationExceptionEvent事件,这个事件是ApplicationEvent
类型的,可以通过容器中的ApplicationListener
来进行监听,事件源包含了admin的映射,声明的元素以及抛出的异常。
通过rabbitAdmin设置Headers Exchange
<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以后,可以通过rabbitAdmin将交换机设置一个internal属性(默认为false),当这个属性设置为true时,RabbitMq不会让客户端使用这个交换机,这在设置死信交换机或者
exchange-to-exchange binding的时候非常有用,这样可以不用担心别人往这个交换机上发布消息。
通过java配置AMQP 基础骨架
@Configuration
public abstract class AbstractStockAppRabbitConfiguration {
@Bean
public ConnectionFactory 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 MessageConverter jsonMessageConverter() {
return new Jackson2JsonMessageConverter();
}
@Bean
public TopicExchange marketDataExchange() {
return new TopicExchange("app.stock.marketdata");
}
// additional code omitted for brevity
}
基于AbstractStockAppRabbitConfiguration 配置的server端配置。项目启动时会自动声明TopicExchange
和Queue
,server端没有绑定TopicExchange和Queue,这部分由client端来完成,server端的这个队列会绑定到默认的交换机上。
@Configuration
public class RabbitServerConfiguration extends AbstractStockAppRabbitConfiguration {
@Bean
public Queue stockRequestQueue() {
return new Queue("app.stock.request");
}
}
基于AbstractStockAppRabbitConfiguration 配置的client端配置
@Configuration
public class RabbitClientConfiguration extends AbstractStockAppRabbitConfiguration {
@Value("${stocks.quote.pattern}")
private String marketDataRoutingKey;
/**
* 通过AmqpAdmin的declareQueue声明一个队列
@Bean
public Queue marketDataQueue() {
return amqpAdmin().declareQueue();
}
/**
* 绑定market data exchange 监听任何匹配routing key的消息
*/
@Bean
public Binding marketDataBinding() {
return BindingBuilder.bind(
marketDataQueue()).to(marketDataExchange()).with(marketDataRoutingKey);
}
// additional code omitted for brevity
}
1.6提供了建造者模式创建队列和交换机
@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();
}
2.2版本优化了建造者模式
@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()
.masterLocator(MasterLocator.minMasters)
.singleActiveConsumer()
.build();
}
@Bean
public DirectExchange ex() {
return ExchangeBuilder.directExchange("ex.with.alternate")
.durable(true)
.alternate("alternate")
.build();
}
批量声明组件
@Configuration
public static class Config {
@Bean
public ConnectionFactory 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.2版本为Declarables添加了getDeclarablesByType
方法,在声明listener container时更加方便。
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;
}
有条件的声明(非启动声明)
默认的,所有的queues, exchanges, and bindings 都是被所有的RabbitAdmin
实例以auto-startup="true"的方式在容器中声明。2.1.9以后,RabbitAdmin
有一个新的属性explicitDeclarationsOnly
(默认false),当这个属性设置为true时,rabbitadmin只会在声明哪些被明确指定要声明的beans。每个元素都实现了Declarable,Declarable有两个方法,一个是shouldDeclare()
,一个是getDeclaringAdmins(),rabbitadmin通过这两个方法来决定是否声明这个bean。
一个配置例子:
<!-- 声明三个admin -->
<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" />
<!-- 由admin1和admin2声明 -->
<rabbit:queue id="declaredByAdmin1AndAdmin2" declared-by="admin1, admin2" />
<!-- 由admin1声明 -->
<rabbit:queue id="declaredByAdmin1Only" declared-by="admin1" />
<!-- 由admin3声明,但由于自动声明为false,所以不会被声明 -->
<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>
2.0版本如果同时为一个元素设置了id和name,那么name不会被注册为别名,如果想要将queue和exchange注册为同一个名字,那么必须提供id。如果只有一个name,那么bean在binding时仍然可以通过name来影射,如果名字中由ApEL表达式,那么bean在binding时不可以通过name来影射。