11 release confirmation advanced

Posted by MikeyNoedel on Mon, 29 Nov 2021 19:04:13 +0100

Release confirmation springboot version

configuration file

It needs to be added in the configuration file

spring.rabbitmq.publisher-confirm-type=correlated

  • NONE

Disable publish confirmation mode, which is the default

  • CORRELATED

The callback method will be triggered after the message is successfully published to the exchange

  • SIMPLE

After testing, there are two effects. One effect is the same as the corelated value, which will trigger the callback method,
Second, after publishing the message successfully, use rabbitTemplate to call waitForConfirms or waitForConfirmsOrDie methods
Wait for the broker node to return the sending result, and determine the logic of the next step according to the returned result. Note:
If the waitForConfirmsOrDie method returns false, the channel will be closed, and messages cannot be sent to the broker next

//Configuration class publishing confirmation
@Configuration
public class ConfirmConfig {

    //Switch
    public static final String CONFIRM_EXCHANGE_NAME = "confirm_exchange";
    //queue
    public static final String CONFIRM_QUEUE = "confirm_queue";
    //RoutingKey
    public static final String CONFIRM_ROUTING_KEY = "confirm_routing_key";

    //Claim switch
    @Bean
    public DirectExchange confirmExchange(){
        return new DirectExchange(CONFIRM_EXCHANGE_NAME);
    }

    //Declaration queue
    @Bean
    public Queue confirmQueue(){
        return QueueBuilder.durable(CONFIRM_QUEUE).build();
    }

    //binding
    @Bean
    public Binding queueBindingExchange(@Qualifier("confirmExchange")DirectExchange confirmExchange,
                                        @Qualifier("confirmQueue")Queue confirmQueue){
        return BindingBuilder.bind(confirmQueue).to(confirmExchange).with(CONFIRM_ROUTING_KEY);
    }
}

Message producer

@Slf4j
@RestController
@RequestMapping("/confirm")
public class ProducerController {

    @Autowired
    RabbitTemplate rabbitTemplate;

    //Send a message
    @GetMapping("/sendMessage/{message}")
    public void sendMessage(@PathVariable String message){
        CorrelationData correlationData = new CorrelationData("1");
        rabbitTemplate.convertAndSend(ConfirmConfig.CONFIRM_EXCHANGE_NAME,ConfirmConfig.CONFIRM_ROUTING_KEY,message,correlationData);

        log.info("Message content sent:{}",message);
		
        //Change routingkey to make him wrong
        CorrelationData correlationData2 = new CorrelationData("2");
        rabbitTemplate.convertAndSend(ConfirmConfig.CONFIRM_EXCHANGE_NAME,ConfirmConfig.CONFIRM_ROUTING_KEY+"2",message,correlationData2);

        log.info("Message content sent:{}",message);
    }

}

Callback interface

@Component
@Slf4j
public class MyCallBack implements RabbitTemplate.ConfirmCallback{

    @Autowired
    private RabbitTemplate rabbitTemplate;

    @PostConstruct
    public void init(){
        //injection
        rabbitTemplate.setConfirmCallback(this);
    }

    /**
     *  Switch confirmation callback method
     *  1.The messaging switch received a callback
     *      1.1 CorrelationData  The ID and related information of the callback message are saved
     *      1.2 The switch receives the message ack=true
     *      1.3 cause - null
     * 2. Message sending switch accepts failed callback
     *      2.1 CorrelationData  The ID and related information of the callback message are saved
     *      2.2 The switch receives the message ack = false
     *      2.3  cause  - Reasons for failure
     */

    @Override
    public void confirm(CorrelationData correlationData, boolean ack, String cause) {
        String id = correlationData != null ? correlationData.getId() : "";

        if (ack){
            log.info("I got it id Is:{}News of",id);
        }else {
            log.info("The switch has not received id: {}Message due to reason:{}",id,cause);
        }
    }
}

Message consumer

@Slf4j
@Component
public class Consumer {

    @RabbitListener(queues = ConfirmConfig.CONFIRM_QUEUE)
    public void receiveConfirmMessage(Message message){
       String msg = new String(message.getBody());
        log.info("Received queue confirm.queue Message:{}",msg);
    }

}

Result analysis

Two messages were sent. The RoutingKey of the first message was "key1" and the RoutingKey of the second message was "key2". Both messages were successfully received by the switch and the confirmation callback of the switch was also received. However, the consumer only received one message because the RoutingKey of the second message was inconsistent with the BindingKey of the queue, and no other queue could receive this message, All second messages are discarded directly.

Fallback message

The queue cannot receive messages, routingKey error, and the queue disappears

When only the producer confirmation mechanism is enabled, the switch will directly send a confirmation message to the message producer after receiving the message. If it is found that the message is not routable, the message will be directly discarded. At this time, the producer does not know the event that the message is discarded.

Add spring. Rabbitmq. Publisher returns = true to the configuration class

Message producer code

@Slf4j
@RestController
@RequestMapping("/confirm")
public class ProducerController {

    @Autowired
    RabbitTemplate rabbitTemplate;

    //Send a message
    @GetMapping("/sendMessage/{message}")
    public void sendMessage(@PathVariable String message){
        CorrelationData correlationData = new CorrelationData("1");
        rabbitTemplate.convertAndSend(ConfirmConfig.CONFIRM_EXCHANGE_NAME,ConfirmConfig.CONFIRM_ROUTING_KEY,message,correlationData);

        log.info("Message content sent:{}",message);

        CorrelationData correlationData2 = new CorrelationData("2");
        rabbitTemplate.convertAndSend(ConfirmConfig.CONFIRM_EXCHANGE_NAME+"123",ConfirmConfig.CONFIRM_ROUTING_KEY,message,correlationData2);

        log.info("Message content sent:{}",message);
    }

}

Callback interface

@Component
@Slf4j
public class MyCallBack implements RabbitTemplate.ConfirmCallback,RabbitTemplate.ReturnsCallback {

    @Autowired
    private RabbitTemplate rabbitTemplate;

    @PostConstruct
    public void init(){
        //injection
        rabbitTemplate.setConfirmCallback(this);
        rabbitTemplate.setReturnsCallback(this);
    }

    /**
     *  Switch confirmation callback method
     *  1.The messaging switch received a callback
     *      1.1 CorrelationData  The ID and related information of the callback message are saved
     *      1.2 The switch receives the message ack=true
     *      1.3 cause - null
     * 2. Message sending switch accepts failed callback
     *      2.1 CorrelationData  The ID and related information of the callback message are saved
     *      2.2 The switch receives the message ack = false
     *      2.3  cause  - Reasons for failure
     */

    @Override
    public void confirm(CorrelationData correlationData, boolean ack, String cause) {
        String id = correlationData != null ? correlationData.getId() : "";

        if (ack){
            log.info("I got it id Is:{}News of",id);
        }else {
            log.info("The switch has not received id: {}Message due to reason:{}",id,cause);
        }
    }

    //When the destination cannot be reached during message delivery, the message is returned to the producer
    //Fallback only occurs when the destination is unreachable
    @Override
    public void returnedMessage(Message message, int replyCode, String replyText, String exchange, String routingKey) {
        log.error("news{},Switched{}Return, return reason:{},routingKey: {}",
                new String(message.getBody()),exchange,replyText,routingKey);
    }

    @Override
    public void returnedMessage(ReturnedMessage returned) {

    }
}

Backup switch

Modify configuration class

@Configuration
public class ConfirmConfig {

    //Switch
    public static final String CONFIRM_EXCHANGE_NAME = "confirm_exchange";
    //queue
    public static final String CONFIRM_QUEUE = "confirm_exchange";
    //RoutingKey
    public static final String CONFIRM_ROUTING_KEY = "confirm_routing_key";
    //Backup switch
    public static final String BACKUP_EXCHANGE_NAME = "backup_exchange";
    //Backup queue
    public static final String BACKUP_QUEUE_NAME = "backup_queue";
    //Alarm queue
    public static final String WARNING_QUEUE_NAME = "warning_queue";


    //Claim switch
    @Bean
    public DirectExchange confirmExchange(){
        //The confirmation switch cannot forward the confirmation message to the backup switch
        return ExchangeBuilder.directExchange(CONFIRM_EXCHANGE_NAME).durable(true)
                .withArgument("alternate-exchange",BACKUP_EXCHANGE_NAME).build();
    }

    //Declaration queue
    @Bean
    public Queue confirmQueue(){
        return QueueBuilder.durable(CONFIRM_QUEUE).build();
    }

    //binding
    @Bean
    public Binding queueBindingExchange(@Qualifier("confirmExchange")DirectExchange confirmExchange,
                                        @Qualifier("confirmQueue")Queue confirmQueue){
        return BindingBuilder.bind(confirmQueue).to(confirmExchange).with(CONFIRM_ROUTING_KEY);
    }

    //Backup switch
    @Bean
    public FanoutExchange backupExchange(){
        return new FanoutExchange(BACKUP_EXCHANGE_NAME);
    }

    //Backup queue
    @Bean
    public Queue backupQueue(){
        return QueueBuilder.durable(BACKUP_QUEUE_NAME).build();
    }

    @Bean
    public Queue warningQueue(){
        return QueueBuilder.durable(WARNING_QUEUE_NAME).build();
    }
    //binding
    @Bean
    public Binding backupQueueBindingExchange(@Qualifier("backupExchange") FanoutExchange backupExchange,
                                        @Qualifier("backupQueue")Queue backupQueue){
        return BindingBuilder.bind(backupQueue).to(backupExchange);
    }

    //binding
    @Bean
    public Binding warningQueueBindingExchange(@Qualifier("backupExchange") FanoutExchange backupExchange,
                                              @Qualifier("warningQueue")Queue warningQueue){
        return BindingBuilder.bind(warningQueue).to(backupExchange);
    }
}

Alarm consumer

@Component
@Slf4j
public class WarningConsumer {

    //Accept alarm information
    @RabbitListener(queues = ConfirmConfig.WARNING_QUEUE_NAME)
    public void receiveWarningMsg(Message message) {
        String msg = new String(message.getBody());
        log.error("Alarm discovery non routable message:{}",msg);

    }
}

Result analysis

When the mandatory parameter and the backup switch can be used together, if they are enabled at the same time, where will the message go? Who has the highest priority? The result above shows that the backup switch has the highest priority.

Topics: RabbitMQ