I want to create a queue with empty name so that the name can be generated by RabbitMQ -
var queue = QueueBuilder
.durable("")
.exclusive()
.autoDelete().build
var binding = BindingBuilder.bind(queue).to(exchange).with(bindingKey).noargs();
Declarables d = new Declarables(queue, binding);
But then calling getActualName returns: spring.gen-vuiRwjOmRkihAE8C72rbmw_awaiting_declaration
d.getDeclarablesByType(Queue.class).get(0).getActualName();
while in rabbitMQ the name is: amq.gen-wpaYnybu9vOdD5v2ej66IQ
In spring-amqp core the Queue constructor declares:
public Queue(String name, boolean durable, boolean exclusive, boolean autoDelete,
@Nullable Map<String, Object> arguments) {
super(arguments);
Assert.notNull(name, "'name' cannot be null");
this.name = name;
this.actualName = StringUtils.hasText(name) ? name
: (Base64UrlNamingStrategy.DEFAULT.generateName()
"_awaiting_declaration");
this.durable = durable;
this.exclusive = exclusive;
this.autoDelete = autoDelete;
}
Why is spring Queue using Base64UrlNamingStrategy and adding "awaiting_declaration" when we want the rabbitMQ name? How can we get the rabbitMQ name and not the spring generated name?
Reason for this use-case is because of race-condition on queues: "When auto-delete or exclusive queues use well-known (static) names, in case of client disconnection and immediate reconnection there will be a natural race condition between RabbitMQ nodes that will delete such queues and recovering clients that will try to re-declare them. This can result in client-side connection recovery failure or exceptions, and create unnecessary confusion or affect application availability."
https://www.rabbitmq.com/queues.html#properties
Spring suggests using broker-based queues which can result in race condition: https://docs.spring.io/spring-amqp/docs/current/reference/html/#containers-and-broker-named-queues
EDIT: We are are not initiating the connection ourselves, but the admin bean initiates it after d.setAdminsThatShouldDelcare(admin)
public Declarables someEventsDeclarables(
@Qualifier("rabbitAdmin") RabbitAdmin admin,
@Qualifier("AmqpExchange") Exchange exchange
) {
final var bindingKey = somePrefix ".*." someSuffix
final var cfg = new OurEventsDeclarables(
exchange,
"", // no queue name - RabbitMq generates it
bindingKey,
true
);
final var declarables = cfg.declarables();
for (Declarable d : declarables.getDeclarables()) {
d.setAdminsThatShouldDeclare(admin);
admin.declareQueue();
}
return declarables;
}
Running the integration-test which uses the queue results in
org.springframework.amqp.rabbit.listener.BlockingQueueConsumer$DeclarationException: Failed to declare queue(s):[spring.gen-QUh8ffN0TimELGG_kF1wFw_awaiting_declaration]
CodePudding user response:
It is not clear what and where you do with that Queue
object.
You must have a RabbitAdmin
bean in the application context. As well as all those Declarable
instances has to be declared as beans as well. Then when application context starts, that RabbitAdmin
takes all the Declarable
beans and declares them against the broker. The further logic around Queue
is like this:
DeclareOk declareOk = channel.queueDeclare(queue.getName(), queue.isDurable(),
queue.isExclusive(), queue.isAutoDelete(), queue.getArguments());
if (StringUtils.hasText(declareOk.getQueue())) {
queue.setActualName(declareOk.getQueue());
}
Just creating variables for those types does not initiate any connection to the broker to perform objects creation and bindings between them.
CodePudding user response:
You are using admin.declareQueue()
(and discarding its result) instead of admin.declareQueue(Queue queue)
.
The first method just declares a broker named queue; the update to the actual name requires the Queue
object (so it can update the name).