Having created a basic spring-amqp setup, I see that for every type of message I want to send (using message POJOs), I need to declare the queue in the spring config.
So for every pair of these:
amqpTemplate.convertAndSend("queue1", new MessageType1(...));
@RabbitListener(queues = "queue1")
public void handleMessage(MessageType1 msg) {...}
I need an entry in the spring config like this:
<rabbit:queue name="queue1"/>
This seems redundant to me. If I think of the queues like HTTP urls, than it's enough to declare a controller with @RequestMapping("/some-url")
, it's not necessary to than also declare /some-url
elsewhere in the config.
If eventually the application will have many types of messages going through the broker, that will just bloat the config.
I see two ways to avoid this:
Automatically declaring the queues for each annotated listener endpoint. i.e if there is an annotated listener endpoint @RabbitListener(queues = "hello")
, then it can be inferred that a queue hello
should be declared. I thought this might already be the case, but when I removed the queue from the context config, I got the following error:
WARN BlockingQueueConsumer - Failed to declare queue:hello
WARN BlockingQueueConsumer - Queue declaration failed; retries left=2
org.springframework.amqp.rabbit.listener.BlockingQueueConsumer$DeclarationException: Failed to declare queue(s):[hello]
at org.springframework.amqp.rabbit.listener.BlockingQueueConsumer.attemptPassiveDeclarations(BlockingQueueConsumer.java:479)
at org.springframework.amqp.rabbit.listener.BlockingQueueConsumer.start(BlockingQueueConsumer.java:400)
at org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer$AsyncMessageProcessingConsumer.run(SimpleMessageListenerContainer.java:1022)
Using different message POJOs on the same queue. So sending both MessageType1
and MessageType2
on queue1
, each reaching a different endpoint, based on payload type. In effect this creates sort of a single "application queue", similar to a database schema:
@RabbitListener(queues = "queue1")
public void handleMessage1(MessageType1 msg) {...}
@RabbitListener(queues = "queue1")
public void handleMessage2(MessageType2 msg) {...}
Trying this approach yielded an error.
Is any of the above currently possible with spring-amqp (1.4.0), and if so how?
It's not currently possible to auto-declare @RabbitListener
queues but it sounds like a reasonable request. Of course, declaring the queue alone might not be enough; while it will be bound to the default exchange ""
with the queue name as a routing key, any more sophisticated binding would require additional configuration anyway.
Using the queue name as a routing key with the default exchange is not considered best practice because it couples the producer and consumer. But I do recognize it is used at times, so please enter a New Feature JIRA Issue and we'll consider it.
BTW, you can also use @Bean
s alongside the listeners to avoid XML config.
Your second request can be implemented by a delegating message listener:
@RabbitListener(queues = "queue1")
public void handleMessage(Object o) {
if (o instanceof Type1) {
delegate.type1((Type1) o);
}
else if (o instanceof Type2) {
delegate.type2((Type2) o);
}
}
I have been thinking of providing such a listener (in a more generic way). Again, please open a new feature issue.
EDIT
Both of these features are now available in the 1.5 release. @QueueBinding
and Class-level @RabbitListener
.