I am new to RabbitMQ and currently working on RabbitMQ publish confirms acknowledgments in my web application. On every new request, sending message to RabbitMQ server to process it asynchronously and also enabled confirms to get acknowledgments from the RMQ server.
When trying in local, on the first request it is fine but on subsequent requests, getting following error "Only one ConfirmCallback is supported by each RabbitTemplate".
After some research, if I add a check (template.isConfirmListener()) before setting confirm callback, not getting error. But how can we override Confirm Callbacks for different requests if required? Is this expected behaviour or am I am doing anything wrong here?
Environment:
Please find the code as below.
Controller file
package com.example.apis;
import java.util.UUID;
import org.springframework.amqp.rabbit.connection.CorrelationData;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.amqp.rabbit.core.RabbitTemplate.ConfirmCallback;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ResponseBody;
@Controller
public class ProducerController {
@Autowired
private RabbitTemplate template;
private String exchange = "test";
private String routingKey = "test";
@GetMapping("/send")
@ResponseBody
public String sendRequestToRMQ() {
template.convertAndSend(exchange, routingKey, "Test Message", getCorrelationData());
template.setMandatory(true);
template.setConfirmCallback(new ConfirmCallback() {
@Override
public void confirm(CorrelationData correlationData, boolean ack, String cause) {
System.out.println("Confirm Callback!");
System.out.println(correlationData.getId());
System.out.println(ack);
}
});
return "Success";
}
@GetMapping("/send1")
@ResponseBody
public String sendRequestToRMQ2() {
template.convertAndSend(exchange, routingKey, "Test Message", getCorrelationData());
template.setMandatory(true);
template.setConfirmCallback(new ConfirmCallback() {
@Override
public void confirm(CorrelationData correlationData, boolean ack, String cause) {
System.out.println("Confirm Callback!");
System.out.println(correlationData.getId());
System.out.println(ack);
}
});
return "Success";
}
private CorrelationData getCorrelationData() {
return new CorrelationData(UUID.randomUUID().toString());
}
}
Configuration file
package com.example.conf;
import org.springframework.amqp.rabbit.connection.CachingConnectionFactory;
import org.springframework.amqp.rabbit.connection.ConnectionFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class ConnectionConfig {
@Value("${spring.rabbitmq.host:localhost}")
public String host;
@Value("${spring.rabbitmq.port:5672}")
public int port;
@Value("${spring.rabbitmq.username:guest}")
public String username;
@Value("${spring.rabbitmq.password:guest}")
public String password;
@Value("${spring.rabbitmq.virtual-host:/}")
public String virtualHost;
@Bean
public ConnectionFactory getConnectionFactory(){
CachingConnectionFactory factory=new CachingConnectionFactory();
factory.setHost(host);
factory.setPort(port);
factory.setUsername(username);
factory.setPassword(password);
factory.setVirtualHost(virtualHost);
factory.setPublisherConfirms(true);
factory.setPublisherReturns(true);
return factory;
}
}
Please help me. Thanks
No, it is expected behavior: only one ConfirmCallback
can be injected into the RabbitTemplate
. You need consider to do that only once, somewhere in the @PostConstrcut
, but not for every single request. Of course, you might need to revise your logic since now a ConfirmCallback
is going to be shared for all the request initiated into RabbitTemplate
.
You may consider some smart logic in your common ConfirmCallback
to store request in the map based on the getCorrelationData()
and restore that entry from the callback method.