How to ensure message reliability

Posted by getmukesh on Sat, 22 Jan 2022 19:24:43 +0100

Message loss

The message was sent out and did not reach the message server due to network reasons

  • Try catch, sending messages may lead to network failure. After the failure, there should be a retry mechanism, which can be recorded in the database, and the method of regular scanning and retransmission should be adopted
  • Log whether each message status is received by the server or not
  • Do a good job of regular retransmission. If the message is not sent successfully, regularly go to the database to scan the messages that are not sent successfully for retransmission

When the message arrives at the Broker, the Broker needs to write the message to disk (persistence) before it is successful. At this time, the Broker has not been successfully down for persistence

  • publisher must also add a confirmation callback mechanism to confirm successful messages and modify the database message status

In the state of automatic ACK. Consumers received the news, but they didn't have time to consume and then went down

  • The manual ACK must be enabled, and it can be removed only after the consumption is successful, or it fails or does not have time to process the noAck and rejoin the team

# Enable the confirmation of the sender message arrival service (Broker)
spring.rabbitmq.publisher-confirm-type=correlated
# Enable the confirmation of the sender's message arrival Queue
spring.rabbitmq.publisher-returns=true
# As long as the message arrives at the queue, the priority callback returnConfirm is sent asynchronously
spring.rabbitmq.template.mandatory=true
# Enable manual ack mechanism
spring.rabbitmq.listener.direct.acknowledge-mode=manual
package com.ww.ideaapp.order.config;


import org.springframework.amqp.core.*;
import org.springframework.amqp.rabbit.connection.CorrelationData;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.amqp.support.converter.Jackson2JsonMessageConverter;
import org.springframework.amqp.support.converter.MessageConverter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.lang.Nullable;

import javax.annotation.PostConstruct;

@Configuration
public class RabbitMQConfig {

    @Autowired
    RabbitTemplate rabbitTemplate;

    /**
     * Message transformation using JSON serialization mechanism
     *
     * @return
     */
    @Bean
    public MessageConverter messageConverter() {
        return new Jackson2JsonMessageConverter();
    }

    /**
     * Customize RabbitTemplate
     * 1,The service (Broker) received a message callback
     *    1)spring.rabbitmq.publisher-confirm-type=correlated
     *    2)Set ConfirmCallback
     * 2,The message correctly arrives at the Queue callback
     *    1)spring.rabbitmq.publisher-returns=true
     *       spring.rabbitmq.template.mandatory=true
     *    2)Set ReturnCallback
     * 3,Consumer confirmation (automatic confirmation by default)
     *    Set manual sign in. If there is no ack, the message will always be in the unacketed state. Even if the consumer goes down, the message will not be lost, it will change to Ready again, and it will be sent again next time
     *    spring.rabbitmq.listener.direct.acknowledge-mode=manual
     *    channel.basicAck(deliveryTag, false) Sign for
     *    channel.basicNack(deliveryTag, false, false) deny sb. a visa
     *    long deliveryTag:  channel Internal self increasing
     */
    @PostConstruct // When the constructor creation object (RabbitConfig) is completed, this method is called.
    public void initRabbitTemplate() {
        // Set message arrival service (Broker) callback
        rabbitTemplate.setConfirmCallback(new RabbitTemplate.ConfirmCallback() {
            /**
             * Confirmation callback mechanism
             *
             * @param correlationData Unique associated data of the current message (message unique ID)
             * @param b Whether the message was received successfully
             * @param s Failure reason
             */
            @Override
            public void confirm(@Nullable CorrelationData correlationData, boolean b, @Nullable String s) {
                System.out.println("ConfirmCallback==>correlationData["+correlationData+"]==>b["+b+"]==>s["+s+"]");
            }
        });
        // Set message arrival Queue acknowledgement callback
        rabbitTemplate.setReturnCallback(new RabbitTemplate.ReturnCallback() {
            /**
             * As long as the message is not delivered successfully, the failed callback will be triggered
             *
             * @param message Post failed message details
             * @param i Failure status code replied by the server
             * @param s Text content replied by the server
             * @param s1 Which switch is this message sent to
             * @param s2 The routing key used for this message at that time
             */
            @Override
            public void returnedMessage(Message message, int i, String s, String s1, String s2) {
                System.out.println("fail message==>message["+message+"]==>i["+i+"]==>s1["+s1+"]==>s2["+s2+"]");
            }
        });
    }

}

Duplicate message

The message consumption is successful, the transaction has been committed, and the ACK goes down, resulting in no ack success. The Broker's message changes from unack to ready and is sent to other consumers

  • The consumer business interface should be designed to be idempotent. For example, inventory deduction has the status flag of the work order
  • Each message sent using the anti duplication table (mysql/redis) has a unique business ID, and it will not be processed after processing
  • Each message of rabbitMQ has a redelivered field, which can obtain whether it has been redelivered rather than delivered for the first time

Message consumption failed. Due to the retry mechanism, the message is automatically sent out again

Message backlog

Consumer downtime backlog

Insufficient consumer capacity and backlog

The sender sends too much traffic

  • Online more consumers for normal consumption
  • On line special queue message service, Jiang primary school takes it out in batches, records the database, and processes it slowly offline

Topics: Spring Boot Microservices