spring rabbit mq enables publisher confirm

Keywords: RabbitMQ Spring curl Attribute

Acknowledgement mechanism

When a producer sends a message, one thing to consider is whether the message has been successfully submitted and whether it has reached the queue. This judgment of AMQP protocol itself has also been designed for this purpose, using the transaction mechanism. However, transaction mechanism has two shortcomings:

  1. slow
  2. block

So now there will be the confirmation mechanism as the title says.

To use the validation mechanism, we first need to define the use of validation patterns. For example, in spring, the code that opens the mode is connectionFactory. setPublisher Confirms (true); in order to make the confirmation mechanism work, the client first sends the confirm.select method frame. Depending on whether the no-wait attribute is set, the broker decides whether to respond with confirm.select-ok accordingly. Once confirm.select method is used on channel, channel will be in confirm mode. Channel in transactional mode can no longer be set to confirm mode, and vice versa.

Principle of confirmation mechanism

Once channel is in confirm mode, both broker and client start counting messages (counting from 1 based on confirm.select). After processing the message, broker confirms it by sending basic.ack on the current channel. The value of the delivery-tag field identifies the serial number of the confirmed message. Broker can also indicate that all messages up to the specified serial number have been correctly processed by broker by setting the multiple field in basic.ack.

** In exceptional cases, broker will not be able to successfully process the corresponding message, and broker will send basic.nack instead of basic.ack. ** In this case, the meaning of each domain value in basic.nack is the same as that in basic.ack, and the value of the requeue domain should be ignored. ** Through nack one or more messages, broker indicates that it cannot process the corresponding messages and refuses to be responsible for their processing. In this case, client can choose to re-publish the message.

When is the message confirmed?

The broker confirm s the message in the following case:

  • Broker finds that the current message cannot be routed to the specified queues (if the mandatory attribute is set, broker sends basic.return first)
  • Messages with non-persistent attributes reach all queues they should reach (and mirror queues)
  • Persistent messages reach all queues (and mirror queues) they should reach and are persisted to disk (by fsync)
  • Persistent messages are consume d from all queue s in which they are located (acknowledge d if necessary)

Specific code

The following code, first set the need to use confirmation mode (Ps: Note that rabbitTemplate.setMandatory(true); otherwise connectionFactory. setPublisher Returns (true); will not work, see the following code:

@Bean
public ConnectionFactory createConnectionFactory(){
    CachingConnectionFactory connectionFactory = new CachingConnectionFactory("127.0.0.1", 5672);
    connectionFactory.setUsername("guest");
    connectionFactory.setPassword("guest");
    connectionFactory.setVirtualHost("/");

    connectionFactory.setPublisherReturns(true);
    connectionFactory.setPublisherConfirms(true);

    return connectionFactory;
}

Define callbacks and implement RabbitTemplate. Confirm Callback and RabbitTemplate. Return Callback as follows:

@Bean
    public RabbitTemplate createRabbitTemplate(ConnectionFactory connectionFactory){
        RabbitTemplate rabbitTemplate = new RabbitTemplate();
        rabbitTemplate.setConnectionFactory(connectionFactory);
        rabbitTemplate.setMandatory(true);

        rabbitTemplate.setConfirmCallback(new RabbitTemplate.ConfirmCallback() {
            @Override
            public void confirm(CorrelationData correlationData, boolean ack, String cause) {
                System.out.println("================");
                System.out.println("correlationData = " + correlationData);
                System.out.println("ack = " + ack);
                System.out.println("cause = " + cause);
                System.out.println("================");
            }
        });

        rabbitTemplate.setReturnCallback(new RabbitTemplate.ReturnCallback() {
            @Override
            public void returnedMessage(Message message, int replyCode, String replyText, String exchange, String routingKey) {
                System.out.println("================");
                System.out.println("message = " + message);
                System.out.println("replyCode = " + replyCode);
                System.out.println("replyText = " + replyText);
                System.out.println("exchange = " + exchange);
                System.out.println("routingKey = " + routingKey);
                System.out.println("================");
            }
        });

        return rabbitTemplate;
    }

Define switches, queues, bindings:

@RabbitListener(
        containerFactory = "myConnectionFactory",
        bindings = @QueueBinding(
                value = @Queue(value = "testPublishModeQueue1",durable = "true"),
                exchange = @Exchange(value = "testPublishModeExchange",type = ExchangeTypes.DIRECT),
                key = "key1")
)

Define producers:

@RestController
public class Producer {

    @Autowired
    public RabbitTemplate rabbitTemplate;

    @RequestMapping(value = "/test/{abc}",method = RequestMethod.GET)
    public String test(@PathVariable(value = "abc") String abc){
        rabbitTemplate.convertAndSend("testPublishModeExchange","key1", abc + " from RabbitMQ!");
        return  "abc";
    }
    @RequestMapping(value = "/test1/{abc}",method = RequestMethod.GET)
    public String test1(@PathVariable(value = "abc") String abc){
        rabbitTemplate.convertAndSend("testPublishModeExchange","key11", abc + " from RabbitMQ!");
        return  "abc";
    }
    @RequestMapping(value = "/test2/{abc}",method = RequestMethod.GET)
    public String test2(@PathVariable(value = "abc") String abc){
        rabbitTemplate.convertAndSend("testPublishModeExchange1","key1", abc + " from RabbitMQ!");
        return  "abc";
    }
    @RequestMapping(value = "/test3/{abc}",method = RequestMethod.GET)
    public String test3(@PathVariable(value = "abc") String abc){
        rabbitTemplate.convertAndSend("testPublishModeExchange1","key11", abc + " from RabbitMQ!");
        return  "abc";
    }
}

Test and output results

When curl localhost:8080/test/123 is sent, that is to say, the switch and routing keys sent are correct. The results are as follows:

Received111 <123 from RabbitMQ!>
================
correlationData = null
ack = true
cause = null
================

When curl localhost:8080/test1/123 is sent, that is to say, the sending switch is correct, but the routing key is wrong. The results are as follows:

================
message = (Body:'123 from RabbitMQ!'MessageProperties [headers={}, timestamp=null, messageId=null, userId=null, appId=null, clusterId=null, type=null, correlationId=null, replyTo=null, contentType=text/plain, contentEncoding=UTF-8, contentLength=0, deliveryMode=PERSISTENT, expiration=null, priority=0, redelivered=null, receivedExchange=null, receivedRoutingKey=null, deliveryTag=0, messageCount=null])
replyCode = 312
replyText = NO_ROUTE
exchange = testPublishModeExchange
routingKey = key11
================
================
correlationData = null
ack = true
cause = null
================

When sending curl localhost:8080/test2/123, that is to say, the sending switch is wrong, but the routing key is correct. The results are as follows:

================
correlationData = null
ack = false
cause = channel error; protocol method: #method<channel.close>(reply-code=404, reply-text=NOT_FOUND - no exchange 'testPublishModeExchange1' in vhost '/', class-id=60, method-id=40)
================
2016-04-21 10:36:07.714 ERROR 43884 --- [ 127.0.0.1:5672] o.s.a.r.c.CachingConnectionFactory       : Channel shutdown: channel error; protocol method: #method<channel.close>(reply-code=404, reply-text=NOT_FOUND - no exchange 'testPublishModeExchange1' in vhost '/', class-id=60, method-id=40)

When sending curl localhost:8080/test2/123, that is to say, the sending switch is wrong, but the routing key is wrong. The results are as follows:

================
correlationData = null
ack = false
cause = channel error; protocol method: #method<channel.close>(reply-code=404, reply-text=NOT_FOUND - no exchange 'testPublishModeExchange1' in vhost '/', class-id=60, method-id=40)
================
2016-04-21 10:36:48.269 ERROR 43884 --- [ 127.0.0.1:5672] o.s.a.r.c.CachingConnectionFactory       : Channel shutdown: channel error; protocol method: #method<channel.close>(reply-code=404, reply-text=NOT_FOUND - no exchange 'testPublishModeExchange1' in vhost '/', class-id=60, method-id=40)

Conclusion: From the above observations, we can find that:

  • If the message we send fails to reach the switch, that is to say, the sent switch has written an error, the confirm method will be called back immediately, and ack = false, which will also report to you cause, such as no exchange'test Publish Model Exchange 1'in vhost'/'.
  • If the message we send arrives at the switch, but the routing key is written incorrectly, and the switch fails to forward to the queue, confirm will be called back, and ack = true will be displayed, which means that the switch has received the message correctly, but at the same time, the returned Message method will be called, which will return the message we sent back.

ConfirmCallback vs ReturnCallback

At first, I was doubtful about the two. stackoverflow Explanation:

Returns are when the broker returns a message because it's undeliverable 
(no matching bindings on the exchange to which the message was published, 
and the mandatory bit is set).

Confirms are when the broker sends an ack back to the publisher, 
indicating that a message was successfully routed.

Combined with my own tests, one of my own understandings of the two is that

  • confirm is mainly used to determine whether the message arrives at the switch correctly, if so, ack returns true; if not, false.
  • return means that if your message has arrived at the switch correctly, but the subsequent processing is wrong, it will call back and send back the message to you (provided that Mandatory is set and discarded if not); if the message has not arrived at the switch, it will not call back.

Reference resources

Written in the end

  1. Write it out and say it before you know right or wrong. Only when you know wrong can you correct it and only when you correct it can you grow up.
  2. In terms of technology, I hope you can't tolerate sand in your eyes. Thank you very much if there are any mistake or need to be improved.

Posted by msing on Fri, 14 Dec 2018 19:48:04 -0800