Tejeshwar Singh
Tejeshwar Singh

Reputation: 143

Spring AMQP - Channel transacted vs publisher confirms

I have a Jersey application in which I am using spring amqp library to publish messages to rabbitMQ exchanges. I am using CachingConnectionFactory in my rabbit template and initially Channel-Transacted was set to false. I noticed that some messages were not actually published to the exchange, so I changed the channel-transacted value to true.

On doing this, my publishing function started taking 500ms (It was 5ms while the channel transacted was false). Is there something I am missing here because 500ms is way too much.

As an alternative, I tried setting publisherConfirms to true and added a ConfirmCallback. I haven't yet benchmarked this, but would like to know if this will have better performance as compared to channel-transacted, given the sole purpose of this application is to publish a message to an exchange in RabbitMQ?

Also, if I go with publisherConfirms, I would like to implement retries in case of failures or at least be able to throw exceptions. With channel-transacted, I will get exception in case of failures, but the latency is high in that case. I am not sure how to implement retries with publisherConfirms.

I tried retries with publisher confirms but my code just hangs.

Here's my code:

CompleteMessageCorrelationData.java

public class CompleteMessageCorrelationData extends CorrelationData {

    private final Message message;
    private final int retryCount;

    public CompleteMessageCorrelationData(String id, Message message, int retryCount) {
        super(id);
        this.message = message;
        this.retryCount = retryCount;
    }

    public Message getMessage() {
        return this.message;
    }

    public int getRetryCount() {
        return this.retryCount;
    }

    @Override
    public String toString() {
        return "CompleteMessageCorrelationData [id=" + getId() + ", message=" + this.message + "]";
    }

}

Setting up the CachingConnectionFactory:

private static CachingConnectionFactory factory = new CachingConnectionFactory("host");
static {
    factory.setUsername("rmq-user");
    factory.setPassword("rmq-password");
    factory.setChannelCacheSize(50);
    factory.setPublisherConfirms(true);
}
private final RabbitTemplate rabbitTemplate = new RabbitTemplate(factory);
rabbitTemplate.setConfirmCallback((correlation, ack, reason) -> {
        if (correlation != null && !ack) {
            CompleteMessageCorrelationData data = (CompleteMessageCorrelationData)correlation;
            log.info("Received nack for message: " + data.getMessage() + " for reason : " + reason);
            int counter = data.getRetryCount();
            if (counter < Integer.parseInt(max_retries)){
                this.rabbitTemplate.convertAndSend(data.getMessage().getMessageProperties().getReceivedExchange(),
                        data.getMessage().getMessageProperties().getReceivedRoutingKey(),
                        data.getMessage(), new CompleteMessageCorrelationData(id, data.getMessage(), counter++));
            } else {
                log.error("Max retries exceeded for message: " + data.getMessage());
            }
        }
    });

Publishing the message:

rabbitTemplate.convertAndSend(exchangeName, routingKey, message, new CompleteMessageCorrelationData(id, message, 0));

So, in short :

  1. Am I doing something wrong with Channel-transacted that the latency is so high?

  2. If I were to implement publisherConfirms instead, along with retries, what's wrong with my approach and will it perform better than channel transacted, considering there is no other job this application has other than publishing messages to rabbitmq?

Upvotes: 4

Views: 3527

Answers (1)

Gary Russell
Gary Russell

Reputation: 174584

As you have found, transactions are expensive and significantly degrade performance; 500ms seems high, though.

I don't believe publisher confirms will help much. You still have to wait for the round-trips to the broker, before releasing the servlet thread. Publisher confirms are useful when you send a bunch of messages and then wait for all the confirms to come back; but when you are only sending one message and then waiting for the confirm, it likely won't be much faster than using a transaction.

You could try it, though, but the code is a bit complex, especially if you want to handle exceptions, which you get for "free" with transactions.

Upvotes: 0

Related Questions