First message to RabbitMQ queue causes channel shutdown - rabbitmq

The first message to my queue always fails.
From the second one, everything works just fine!
Not sure if that's readable so :
Created new connection: rabbitConnectionFactory#1b940034:0/SimpleConnection#2c52fbff [delegate=amqp://guest#10.0.0.10:5672/, localPort= 36370]
Channel shutdown: channel error; protocol method: #method<channel.close>(reply-code=406, reply-text=PRECONDITION_FAILED - inequivalent arg 'auto_delete' for exchange 'rabbitmq_exchange' in vhost '/': received 'false' but current is 'true', class-id=40, method-id=10)
I'm not sure why this is happening, because I launch this on a fresh VM (AWS EC2 instance) every single time. How could "current be true"?
I suppose something is badly configured in the Spring Boot publisher:
Not sure if that's readable so :
#Configuration
public class RabbitMqConfig {
#Bean
Queue queue() {
return new Queue(System.getenv("RABBITMQ_QUEUE_NAME"), true,false, false);
}
#Bean
DirectExchange exchange() {
return new DirectExchange(System.getenv("RABBITMQ_EXCHANGE_NAME"), true, false);
}
#Bean
Binding binding(Queue queue, DirectExchange exchange) {
return BindingBuilder.bind(queue).to(exchange).with(System.getenv("RABBITMQ_ROUTING_KEY"));
}
#Bean
public MessageConverter jsonMessageConverter(){
return new Jackson2JsonMessageConverter();
}
public AmqpTemplate rabbitTemplate(ConnectionFactory connectionFactory) {
final RabbitTemplate rabbitTemplate = new RabbitTemplate(connectionFactory);
rabbitTemplate.setMessageConverter(jsonMessageConverter());
return rabbitTemplate;
}
}
So what's going wrong here?
Thanks!

The error is quite clear...
Channel shutdown: channel error; protocol method: #method(reply-code=406, reply-text=PRECONDITION_FAILED - inequivalent arg 'auto_delete' for exchange 'rabbitmq_exchange' in vhost '/': received 'false' but current is 'true', class-id=40, method-id=10)
When the connection is first opened, the framework looks for queues/bindings etc and declares them. If a queue exists already, it must have the same properties/arguments.
#Bean
Queue queue() {
return new Queue(System.getenv("RABBITMQ_QUEUE_NAME"), true, false, false);
}
Presumably, on the consumer side, it is being declared with different properties...
#Bean
Queue queue() {
return new Queue(System.getenv("RABBITMQ_QUEUE_NAME"), true, false, true);
}
(the auto_delete is inequivalent).
They must be the same.

Define a queue as below.
declare queue name=YourQueName durable=false --vhost="YourVirtualHostName" -u UsernameOfYourQueue -p PasswordOfYourQueue

In my case the queue was first created with wrong configurations, so each time I relaunched my application this error where shown.
Deleting the queue and letting the application recreated it, with the new configurations, solved this problem.

Related

RabbitMQ CachingConnectionFactory and publisherReturns configuration

A continuation of Make Spring RabbitMQ fail on missing exchange
I register MessageListenerContainer for multiple queues.
Where and how should I configure channel returnListener? - I thing the way I have done it is wrong. I inserted CachingConnectionFactory configuration into createQueueBMessageListener(...) - method responsible for creating one of multiple MessageListeners.
1. How should be CachingConnectionFactory additional configuration done Spring and Rabbit way? By now I didn't configure it in Java (only by application.properties and admins in K8S environment). I only injected ConnectionFactory and set it as connectionFactory in SimpleMessageListenerContainer (as in createQueueAMessageListener(...)), I even didn't know it's CachingConnectionFactory.
Is there something like CachingConnectionFactoryConfigurer?
2. Why is ReturnListener.handleReturn(..) not executed? ChannelListener.onCreate(...) is executed.
3. Checking missing exchange exception in cachingConnectionFactory.setCloseExceptionLogger and doing System.exit(1) there seems wrong to me, isn't it? But this is all I managed to do by now. I want to application not start when there is no exchange during binding creation. When I throw exception there application still starts. ReturnListener.handleReturn seems a better place for it, but it isn't executed when configured as below.
4. How can I stop Spring Application Context gracefully instead of System.exit(1)? - throwing exception doesn't stop Application Context. How to make RabbitMq fail to start in such situation? - when a creation of #Bean Binding at Spring Application Context start fails.
#Bean
MessageListenerContainer createQueueAMessageListener(SimpleRabbitListenerContainerFactory rabbitListenerContainerFactory,
ConnectionFactory connectionFactory) {
SimpleMessageListenerContainer container = rabbitListenerContainerFactory.createListenerContainer();
container.setConnectionFactory(connectionFactory);
container.setQueueNames("queueA");
MessageConverter jsonMessageConverter = null;
container.setMessageListener(new MessageListenerAdapter(new Object(), jsonMessageConverter));
return container;
}
#Bean
MessageListenerContainer createQueueBMessageListener(SimpleRabbitListenerContainerFactory rabbitListenerContainerFactory,
ConnectionFactory connectionFactory,
CachingConnectionFactory cachingConnectionFactory) {
// I think configuring CachingConnectionFactory here is a lame, isn't it? Of course connectionFactory is redundant now, I left it to show how was it done earlier.
// Where and how should I add listeners to CachingConnectionFactory?
cachingConnectionFactory.setPublisherConfirmType(CachingConnectionFactory.ConfirmType.CORRELATED);
cachingConnectionFactory.setPublisherReturns(true);
cachingConnectionFactory.addChannelListener(new ChannelListener() {
#Override
public void onCreate(final Channel channel, final boolean transactional) {
log.info("channelListener onCreate - this is executed");
channel.addReturnListener(new ReturnListener() {
#Override
public void handleReturn(final int replyCode, final String replyText, final String exchange, final String routingKey,
final AMQP.BasicProperties properties,
final byte[] body) throws IOException
{
log.info("!!! Why is this not executed ?!!! handleReturn replyCode: " + replyCode + " replyText: " + replyText);
}
});
}
});
cachingConnectionFactory.addConnectionListener(new ConnectionListener() {
#Override
public void onCreate(final Connection connection) {
log.info("connectionListener onCreate - this is executed" + connection);
}
});
cachingConnectionFactory.setCloseExceptionLogger(new ConditionalExceptionLogger() {
#Override
public void log(final Log logger, final String message, final Throwable t) {
try {
logger.error(message + ": " + t.getMessage());
if (t.getMessage().contains("reply-code=404, reply-text=NOT_FOUND")) {
// throw new RuntimeException(); it doesn't stop Spring ApplicationContext from starting
log.error("Executing System.exit(1) command.");
// System.exit(1);
}
} catch (Exception e) {
log.error("err in listener ", e);
}
}
});
SimpleMessageListenerContainer container = rabbitListenerContainerFactory.createListenerContainer();
container.setConnectionFactory(connectionFactory);
container.setQueueNames("queueB");
MessageConverter jsonMessageConverter = null;
container.setMessageListener(new MessageListenerAdapter(new Object(), jsonMessageConverter));
return container;
}
#Bean
MessageListenerContainer createQueueCMessageListener(SimpleRabbitListenerContainerFactory rabbitListenerContainerFactory,
ConnectionFactory connectionFactory) {
SimpleMessageListenerContainer container = rabbitListenerContainerFactory.createListenerContainer();
container.setConnectionFactory(connectionFactory);
container.setQueueNames("queueC");
MessageConverter jsonMessageConverter = null;
container.setMessageListener(new MessageListenerAdapter(new Object(), jsonMessageConverter));
return container;
}
// I think configuring CachingConnectionFactory here is a lame, isn't it?
It is not "lame"; that is the normal way of configuring beans with additional properties not exposed directly by Boot.
It should be called; have you tried debugging?
Why don't you do what I advised here Make Spring RabbitMQ fail on missing exchange - it's much simpler.
close() it - but, since you are using Spring Boot, it will do that for you - it registers a JVM shutdown hook that closes the context.
EDIT
Binding to a non-existent exchange will fail; you just need to force it to happen before the application is fully initialized, e.g. in an ApplicationRunner.
#SpringBootApplication
public class So70212347Application {
public static void main(String[] args) {
SpringApplication.run(So70212347Application.class, args);
}
#Bean
Binding binding() {
return new Binding("foo", DestinationType.QUEUE, "doesn't exist", "foo", null);
}
#Bean
Queue queue() {
return new Queue("foo");
}
#Bean
ApplicationRunner runner(ConnectionFactory cf) {
return args -> {
cf.createConnection().close();
};
}
}
Created new connection: rabbitConnectionFactory#6a0cbc6f:0/SimpleConnection#6cd164a6 [delegate=amqp://guest#127.0.0.1:5672/, localPort= 62884]
Shutdown Signal: channel error; protocol method: #method<channel.close>(reply-code=404, reply-text=NOT_FOUND - no exchange 'doesn't exist' in vhost '/', class-id=50, method-id=20)
Shutdown Signal: channel error; protocol method: #method<channel.close>(reply-code=404, reply-text=NOT_FOUND - no exchange 'doesn't exist' in vhost '/', class-id=50, method-id=20)
Shutdown Signal: channel error; protocol method: #method<channel.close>(reply-code=404, reply-text=NOT_FOUND - no exchange 'doesn't exist' in vhost '/', class-id=50, method-id=20)
Shutdown Signal: channel error; protocol method: #method<channel.close>(reply-code=404, reply-text=NOT_FOUND - no exchange 'doesn't exist' in vhost '/', class-id=50, method-id=20)
Shutdown Signal: channel error; protocol method: #method<channel.close>(reply-code=404, reply-text=NOT_FOUND - no exchange 'doesn't exist' in vhost '/', class-id=50, method-id=20)
Error starting ApplicationContext. To display the conditions report re-run your application with 'debug' enabled.
Application run failed
java.lang.IllegalStateException: Failed to execute ApplicationRunner
at org.springframework.boot.SpringApplication.callRunner(SpringApplication.java:761)
at org.springframework.boot.SpringApplication.callRunners(SpringApplication.java:748)
at org.springframework.boot.SpringApplication.run(SpringApplication.java:309)
at org.springframework.boot.SpringApplication.run(SpringApplication.java:1301)
at org.springframework.boot.SpringApplication.run(SpringApplication.java:1290)
at com.example.demo.So70212347Application.main(So70212347Application.java:16)
Caused by: org.springframework.amqp.AmqpIOException: java.io.IOException
at org.springframework.amqp.rabbit.support.RabbitExceptionTranslator.convertRabbitAccessException(RabbitExceptionTranslator.java:70)
at org.springframework.amqp.rabbit.connection.RabbitAccessor.convertRabbitAccessException(RabbitAccessor.java:113)
at org.springframework.amqp.rabbit.core.RabbitTemplate.doExecute(RabbitTemplate.java:2192)
at org.springframework.amqp.rabbit.core.RabbitTemplate.execute(RabbitTemplate.java:2138)
at org.springframework.amqp.rabbit.core.RabbitTemplate.execute(RabbitTemplate.java:2118)
at org.springframework.amqp.rabbit.core.RabbitAdmin.initialize(RabbitAdmin.java:691)
at org.springframework.amqp.rabbit.core.RabbitAdmin.lambda$null$10(RabbitAdmin.java:619)
at org.springframework.retry.support.RetryTemplate.doExecute(RetryTemplate.java:329)
at org.springframework.retry.support.RetryTemplate.execute(RetryTemplate.java:209)
at org.springframework.amqp.rabbit.core.RabbitAdmin.lambda$afterPropertiesSet$11(RabbitAdmin.java:618)
at org.springframework.amqp.rabbit.connection.CompositeConnectionListener.lambda$onCreate$0(CompositeConnectionListener.java:38)
at java.base/java.util.concurrent.CopyOnWriteArrayList.forEach(CopyOnWriteArrayList.java:807)
at org.springframework.amqp.rabbit.connection.CompositeConnectionListener.onCreate(CompositeConnectionListener.java:38)
at org.springframework.amqp.rabbit.connection.CachingConnectionFactory.createConnection(CachingConnectionFactory.java:730)
at com.example.demo.So70212347Application.lambda$0(So70212347Application.java:33)
at org.springframework.boot.SpringApplication.callRunner(SpringApplication.java:758)
... 5 common frames omitted
Caused by: java.io.IOException: null
at com.rabbitmq.client.impl.AMQChannel.wrap(AMQChannel.java:129)
at com.rabbitmq.client.impl.AMQChannel.wrap(AMQChannel.java:125)
at com.rabbitmq.client.impl.AMQChannel.exnWrappingRpc(AMQChannel.java:147)
at com.rabbitmq.client.impl.ChannelN.queueBind(ChannelN.java:1077)
at com.rabbitmq.client.impl.ChannelN.queueBind(ChannelN.java:46)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.base/java.lang.reflect.Method.invoke(Method.java:566)
at org.springframework.amqp.rabbit.connection.CachingConnectionFactory$CachedChannelInvocationHandler.invoke(CachingConnectionFactory.java:1157)
at com.sun.proxy.$Proxy47.queueBind(Unknown Source)
at org.springframework.amqp.rabbit.core.RabbitAdmin.declareBindings(RabbitAdmin.java:870)
at org.springframework.amqp.rabbit.core.RabbitAdmin.lambda$initialize$12(RabbitAdmin.java:694)
at org.springframework.amqp.rabbit.core.RabbitTemplate.invokeAction(RabbitTemplate.java:2227)
at org.springframework.amqp.rabbit.core.RabbitTemplate.doExecute(RabbitTemplate.java:2186)
... 18 common frames omitted
Caused by: com.rabbitmq.client.ShutdownSignalException: channel error; protocol method: #method<channel.close>(reply-code=404, reply-text=NOT_FOUND - no exchange 'doesn't exist' in vhost '/', class-id=50, method-id=20)
...

Rabbitmq + Spring AMQP: resend msg but no ack return

I use spring amqp to send msg, and add the resend logic if ack=false, with the same rabbitTemplate
#Bean
public RabbitTemplate customRabbitTemplate(ConnectionFactory connectionFactory) {
RabbitTemplate rabbitTemplate = new RabbitTemplate(connectionFactory);
rabbitTemplate.setMessageConverter(jackson2JsonMessageConverter());
rabbitTemplate.setMandatory(true);
rabbitTemplate.setConfirmCallback((correlationData, ack, cause) -> {
RabbitMetaMessage metaMessage = (RabbitMetaMessage) mqMsgUtil.getMetaMsg(cacheKey);
//1 receive ack
if (ack) {
// send success
mqMsgUtil.setMsgSuccess(cacheKey);
SUCESS_SEND = true;
// send failed
} else {
reSendMsg(cacheKey, metaMessage, rabbitTemplate);
}
});
public void reSendMsg(String msgId, RabbitMetaMessage rabbitMetaMessage,RabbitTemplate rabbitTemplate) {
rabbitTemplate.convertAndSend(rabbitMetaMessage)
.....
}
I can "1 receive ack" when I send msg first time, but when in seSendMsg and send msg again with RabbitTemplate, I can not receive ack again. How this happans?
Sending a message on the ack callback is not allowed; it causes a deadlock in the client library. You need to do the re-send on a different thread.
In the next release (2.1) we have changed the code to invoke the callback on a different thread to avoid the user code having to do it. See AMQP-817 and its linked answer.

What is the use case of BrokerService in ActiveMQ and how to use it correctly

I am new about ActiveMQ. I'm trying to study and check how it works by checking the example code provided by Apache at this link:-
http://activemq.apache.org/how-should-i-implement-request-response-with-jms.html
public class Server implements MessageListener {
private static int ackMode;
private static String messageQueueName;
private static String messageBrokerUrl;
private Session session;
private boolean transacted = false;
private MessageProducer replyProducer;
private MessageProtocol messageProtocol;
static {
messageBrokerUrl = "tcp://localhost:61616";
messageQueueName = "client.messages";
ackMode = Session.AUTO_ACKNOWLEDGE;
}
public Server() {
try {
//This message broker is embedded
BrokerService broker = new BrokerService();
broker.setPersistent(false);
broker.setUseJmx(false);
broker.addConnector(messageBrokerUrl);
broker.start();
} catch (Exception e) {
System.out.println("Exception: "+e.getMessage());
//Handle the exception appropriately
}
//Delegating the handling of messages to another class, instantiate it before setting up JMS so it
//is ready to handle messages
this.messageProtocol = new MessageProtocol();
this.setupMessageQueueConsumer();
}
private void setupMessageQueueConsumer() {
ActiveMQConnectionFactory connectionFactory = new ActiveMQConnectionFactory(messageBrokerUrl);
Connection connection;
try {
connection = connectionFactory.createConnection();
connection.start();
this.session = connection.createSession(this.transacted, ackMode);
Destination adminQueue = this.session.createQueue(messageQueueName);
//Setup a message producer to respond to messages from clients, we will get the destination
//to send to from the JMSReplyTo header field from a Message
this.replyProducer = this.session.createProducer(null);
this.replyProducer.setDeliveryMode(DeliveryMode.NON_PERSISTENT);
//Set up a consumer to consume messages off of the admin queue
MessageConsumer consumer = this.session.createConsumer(adminQueue);
consumer.setMessageListener(this);
} catch (JMSException e) {
System.out.println("Exception: "+e.getMessage());
}
}
public void onMessage(Message message) {
try {
TextMessage response = this.session.createTextMessage();
if (message instanceof TextMessage) {
TextMessage txtMsg = (TextMessage) message;
String messageText = txtMsg.getText();
response.setText(this.messageProtocol.handleProtocolMessage(messageText));
}
//Set the correlation ID from the received message to be the correlation id of the response message
//this lets the client identify which message this is a response to if it has more than
//one outstanding message to the server
response.setJMSCorrelationID(message.getJMSCorrelationID());
//Send the response to the Destination specified by the JMSReplyTo field of the received message,
//this is presumably a temporary queue created by the client
this.replyProducer.send(message.getJMSReplyTo(), response);
} catch (JMSException e) {
System.out.println("Exception: "+e.getMessage());
}
}
public static void main(String[] args) {
new Server();
}
}
My confusion about the messageBrokerUrl = "tcp://localhost:61616"; You know ActiveMQ service is running on port 61616 by default. Why does this example chooses same port. If I try to run the code thows eception as:
Exception: Failed to bind to server socket: tcp://localhost:61616 due to: java.net.BindException: Address already in use: JVM_Bind
Perhaps if I change the port number, I can execute the code.
Please let me know why it is like this in the example and how to work with BrokerService.
The BrokerService in this example is trying to create an in memory ActiveMQ broker for use in the example. Given the error you are seeing I'd guess you already have an ActiveMQ broker running on the machine that is bound to port 61616 as that's the default port and thus the two are conflicting. You could either stop the external broker and run the example or modify the example to not run the embedded broker and just rely on your external broker instance.
Embedded brokers are great for unit testing or for creating examples that don't require the user to have a broker installed and running.

Rabbit mQ messages moving from ready queue to Unack queue

I have a separate publisher and consumer.I start publisher and publish messages.Now i start consumer and problem is that the messages move from ready queue to unack queue marking the messages as redelivered which i want to avoid.So what i want is it should be marked as redelivered only if i send ack and not on consumer restart or start
Configuration :
#Bean
public org.springframework.amqp.rabbit.connection.Connection mqConnection() {
CloudFactory cloudFactory = new CloudFactory();
Cloud cloud = cloudFactory.getCloud();
return cloud.getServiceConnector("mqservicename", ConnectionFactory.class,
null).createConnection();
}
#Bean
public StatefulRetryOperationsInterceptor interceptor() {
return RetryInterceptorBuilder.stateful().retryOperations(retryTemplate()).recoverer(new RejectAndDontRequeueRecoverer())
.build();
}
#Bean
public SimpleMessageListenerContainer listenerContainer() {
SimpleMessageListenerContainer container = new SimpleMessageListenerContainer();
container.setMessageListener(new MessageListenerAdapter());
container.setAdviceChain(new Advice[] {
interceptor()
});
return container;
}
#Bean
public RetryTemplate retryTemplate(){
Map map=new HashMap<Class<? extends Throwable>, Boolean>();
map.put(CustomException.class, true);
RetryTemplate retryTemplate=new RetryTemplate();
retryTemplate.setRetryPolicy(new SimpleRetryPolicy(3,map));
return retryTemplate;
}
If you are using Spring AMQP, you need to show your configuration when asking questions like this.
If you set the ack mode to MANUAL, you are responsible for the acks. Read the documentation. For AUTO ackmode, the container will ack the message when the listener returns normally.

How to configure rabbitmq reply queues using spring-amqp?

We are trying to make asynchronous call in RabbitMQ using Spring AMQP, could any one please tell me how to configure replyqueue, correlationId, (properties) using spring amqp?
String corrId = java.util.UUID.randomUUID().toString();
BasicProperties props = new BasicProperties
.Builder()
.correlationId(corrId)
.replyTo(replyQueueName)
.build();
channel.basicPublish("", requestQueueName, props, message.getBytes());
I assume you need to use RabbitTemplate:
rabbitTemplate.convertAndSend(requestQueueName, myObj, new MessagePostProcessor() {
Message postProcessMessage(Message message) throws AmqpException {
message.getMessageProperties().setReplyTo(replyQueueName);
return message;
}
}, new CorrelationData(corrId));
HTH