Daily record - SpringBoot integrates RabbitMQ Section 3 (producer ConfirmCallback and ReturnCallback)

Refer to the following blogger's article

Getting Started with RabbitMQ Install SpringAMQP Simple Queue, Work Queue, Publish and Subscribe (Fan-Out Mode, Broadcast Mode), Direct Mode (Roting Mode), Topic Mode

RabbitMQ (2), MQ issues: message reliability, delayed messages (delayed queue (plug-in)), message accumulation (lazy queue), high availability of MQ. ConfirmCallback mechanism, ReturnCallback mechanism, dead letter switch

I will only record here how to integrate SpringBoot. For details of installation and deployment, please refer to the article written by the blogger above.

1. What problem does MQ solve?

Solve the problem: realize asynchronous, peak clipping, decoupling

common problem:

Message reliability problem: how to ensure that the message is successfully delivered to the consumer and consumed by the consumer successfully

Delayed message problem: If a message needs to be delayed for 15 minutes before consumption, such as 12306 canceling the order overtime, how to realize the delayed delivery of the message (
if the delay time set by your message exceeds the limit of MQ, the message will be consumed immediately. The message The expiration time must be a non-negative 32 as an integer, that is: 0<=n<=2^32-1, unit (milliseconds). 2^32-1=4294967295 is about 49 days)

Message accumulation problem: If messages cannot be consumed in time and accumulate, how to solve the problem of million-level message accumulation

MQ's high availability problem: how to avoid the problem that MQ is not available because of a single point of failure

2. MQ problems and solutions

Link 1: Producer -> Switch
Link 2: Switch -> Queue
Link 3: Queue -> Consumer
If there is a problem in one of the above links, the message cannot reach the consumer

insert image description here

Problems that may arise when producers send messages:

  1. The message sent by the producer did not reach the exchange
  2. The message sent by the producer arrived at the exchange, but not the queue

The problem that MQ lost after receiving the message:

  1. MQ is down, resulting in the loss of unpersisted saved messages
  2. After the consumer receives the message, it crashes before consuming the MQ

The solutions corresponding to MQ are as follows:

  1. The message sent by the producer is lost: use the producer confirmation mechanism
  2. MQ received message loss: MQ message persistence
  3. Consumers receive message loss: consumer confirmation mechanism and failure retry mechanism

3. Producer Confirmation

Producer -> Switch -> Queue Only
through ConfirmCallback and ReturnCallback can it be confirmed whether the message has reached the queue
insert image description here

1. ConfirmCallback (confirm whether the message reaches the switch)

Application.yml adds configuration publisher-confirm-type configuration, whether the confirmation mechanism is asynchronous or synchronous, and decides according to the business to
insert image description here
simulate whether the producer confirms that the message reaches the switch

import org.junit.jupiter.api.Test;
import org.springframework.amqp.rabbit.connection.CorrelationData;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

@SpringBootTest
public class ConfirmCallbackConfig {
    
    


    @Autowired
    private RabbitTemplate rabbitTemplate;

    @Test
    public void test(){
    
    
        //创建一个CorrelationData Correlation:关联
        CorrelationData correlationData = new CorrelationData();
        //设置消息的id
        correlationData.setId("msg-888888");
        //准备好 回调函数:Callback,用于接收 将来:Future 的确认结果
        correlationData.getFuture().addCallback(
                /**
                 * 当消息发送没有出现异常时,这个方法会被调用
                 */
                result -> {
    
    
                    if (result.isAck()){
    
    
                        System.out.println("发送消息成功,消息已到达交换机,消息id:"+correlationData.getId());
                    }else {
    
    
                        System.out.println("发送消息成功,消息没有到达交换机,消息id:"+correlationData.getId()+" 原因:"+result.getReason());

                    }                },
                /**
                 * 当消息发送出现异常时,这个方法会被调用
                 */
                ex -> {
    
    
                    System.out.println("发送消息异常,消息id:"+correlationData.getId()+" 异常:"+ex);
                }
        );
        //自己随便定一个没有的交换机
        rabbitTemplate.convertAndSend("xxxx.exchange", "demo", "到达交换机了吗",correlationData);
    }



}

I have no message for the exchange named xxxx.exchange.
insert image description here
The message has not been delivered to the specified exchange.
insert image description here
The message has been delivered to the specified exchange.
insert image description hereinsert image description here

2. ReturnCallback (confirm whether the message has arrived in the queue)

Application.yml adds the configuration of ReturnCallback, written on the producer side, my producer and consumer are the same service

insert image description here
Create a ReturnCallback configuration class for unified management

import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.annotation.Configuration;

@Slf4j
@Configuration
public class RabbitReturnCallBackConfig implements ApplicationContextAware {
    
    

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
    
    
        RabbitTemplate rabbitTemplate = applicationContext.getBean(RabbitTemplate.class);
        rabbitTemplate.setReturnCallback((message, replyCode, replyText, exchange, routingKey) -> {
    
    
            //当交换机把消息路由到队列出现问题时,这个方法会自动执行
            log.warn("把消息路由到队列失败,replyCode{},replyText{},RoutingKey={},msg={}", replyCode,replyText,exchange,routingKey, message);
        });
    }


}

Define switches, queues

import org.springframework.amqp.core.*;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
 * 需要:
 *      1. 声明Direct类型的交换机:direct.exchange
 *      2. 声明队列1:direct.queue1
 *         声明队列2:direct.queue2
 *      3. 把交换机与队列1绑定:把 RoutingKey为orange的消息 -> 投递到队列1
 *         把交换机与队列2绑定:把 RoutingKey为black的消息  -> 投递到队列2
 *         把交换机与队列2绑定:把 RoutingKey为green的消息  -> 投递到队列2
 *
 *                             ↗ RoutingKey = orange   ->  direct.queue1 -> C(消费者1)
 * P(生产者) -> direct.exchange
 *                             ↘ RoutingKey = black
 *                                                    ->  direct.queue2   -> C(消费者2)
 *                             ↘ RoutingKey = green
 *
 */
@Configuration
public class DirectQueueConfig {
    
    


    //定义direct类型交换机
    @Bean
    public DirectExchange directExchange() {
    
    
        return ExchangeBuilder.directExchange("direct.exchange").build();
    }


    //定义持久化队列
    @Bean
    public Queue directQueue1() {
    
    
        return new Queue("direct.queue1",true,false,false);
    }

    //定义持久化队列
    @Bean
    public Queue directQueue2() {
    
    
        return QueueBuilder.durable("direct.queue2").build();
    }

    //把交换机与队列direct.queue1绑定:把 RoutingKey为orange的消息,投递到队列direct.queue1
    @Bean
    public Binding directQueue1Binding(Queue directQueue1, DirectExchange directExchange) {
    
    
        return BindingBuilder.bind(directQueue1).to(directExchange).with("orange");
    }

    //把交换机与队列direct.queue2绑定:把 RoutingKey为black的消息,投递到队列direct.queue2
    @Bean
    public Binding directQueue2BindingInfo(Queue directQueue2, DirectExchange directExchange) {
    
    
        return BindingBuilder.bind(directQueue2).to(directExchange).with("black");
    }
    //把交换机与队列direct.queue2绑定:把 RoutingKey为green的消息,投递到队列direct.queue2
    @Bean
    public Binding direcQueue2BindingError(Queue directQueue2, DirectExchange directExchange) {
    
    
        return BindingBuilder.bind(directQueue2).to(directExchange).with("green");
    }



}

mock producer

import org.junit.jupiter.api.Test;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

@SpringBootTest
public class ReturnCallBackTest {
    
    

    @Autowired
    private RabbitTemplate rabbitTemplate;

    //模拟生产者
    @Test
    public void test(){
    
    
        
        //rabbitTemplate.convertAndSend("direct.exchange","orange","这是orange");
        //rabbitTemplate.convertAndSend("direct.exchange","black","这是black");
        //rabbitTemplate.convertAndSend("direct.exchange","green","这是green");

        //没有绑定routingkey=xxxx的队列
        rabbitTemplate.convertAndSend("direct.exchange","xxxx","这是green");
    }
}

Failed to capture message sending
insert image description here

Guess you like

Origin blog.csdn.net/qq407995680/article/details/132086350
Recommended