[architect interview - message queue - 5] - MQ message reliability actual combat source code solution

Posted by kapishi on Wed, 24 Nov 2021 04:33:55 +0100

1: Introduction

If the reliability of the message is guaranteed? The following problems need to be solved

Question 1: the producer can send 100% messages to the message queue!

Two unexpected situations:

First, the consumer fails to send a message to MQ, and the message is lost;

Second, the switch fails to route to the queue, and the routing key is written incorrectly;

Question 2: consumers can receive 100% requests, and there can be no errors in the process of business execution!

2: Producer confirmation

When using RabbitMQ, the message sender wants to eliminate any message loss or delivery failure scenarios. RabbitMQ provides us with two ways to control the delivery reliability mode of messages.

confirm confirmation mode

Return return mode

rabbitmq the delivery path of the whole message is:

The message sends a message from the producer to the exchange. Whether it is successful or not, a confirmation callback method confirmCallback will be executed.

If the delivery of a message from the exchange to the message queue fails, a return callback method returnCallback will be executed.

We will use these two callback s to control the reliable delivery of messages

1: confirm confirmation mode

target

Demonstrate the effect of message confirmation mode

The producer releases the message confirmation mode. The callback method is executed whether the message enters the switch or not

Implementation steps

1. In the configuration file, turn on the producer publishing message confirmation mode

2. Write producer confirmation callback method

3. In the RabbitTemplate, set the message publishing confirmation callback method

4. Request test:

Test successful callback:

Test failure callback:

Implementation process

1. In the configuration file, turn on the producer publishing message confirmation mode

# Turn on the producer confirmation mode: (confirm), deliver to the switch, and call back whether it fails or succeeds
spring.rabbitmq.publisher-confirms=true

2. Write producer confirmation callback method

//Send a message callback confirmation class, implement the callback interface ConfirmCallback, and override the confirm() method
@Component
public class MessageConfirmCallback implements
RabbitTemplate.ConfirmCallback {
/**
* After delivery to the switch, the method will be called back no matter whether the delivery is successful or failed
* @param correlationData Delivery of relevant data
* @param ack Post to switch
* @param cause Reason for delivery failure
*/
@Override
public void confirm(CorrelationData correlationData, boolean ack,
String cause) {
if (ack){
System.out.println("The message entered the switch successfully{}");
} else {
System.out.println("Message failed to enter switch{} , Failure reason:" + cause);
}
}
}

3. In the RabbitTemplate, set the message publishing confirmation callback method

@Component
public class MessageConfirmCallback implements
RabbitTemplate.ConfirmCallback{
@Autowired
private RabbitTemplate rabbitTemplate;
/**
* After creating the RabbitTemplate object, execute the current method and set the callback confirmation method for the template object
* Set message confirmation callback method
* Set message fallback callback method
*/
@PostConstruct
public void initRabbitTemplate(){
//Set message confirmation callback method
rabbitTemplate.setConfirmCallback(this::confirm);
}
/**
* After delivery to the switch, the method will be called back no matter whether the delivery is successful or failed
* @param correlationData Delivery of relevant data
* @param ack Post to switch
* @param cause Reason for delivery failure
*/
@Override
public void confirm(CorrelationData correlationData, boolean ack,
String cause) {
if (ack){
System.out.println("The message entered the switch successfully{}");
} else {
System.out.println("Message failed to enter switch{} , Failure reason:" + cause);
}
}
}

4. Request test:

Test successful callback: http://localhost:8080/direct/sendMsg? exchange=order_ Exchange & routingkey = order. A & MSG = buy Apple phone

Test failure callback: http://localhost:8080/direct/sendMsg?

exchange=order_ XXXXXXXX & routingkey = order. A & MSG = buy Apple phone

2: Return return mode

target

Demonstrate the effect of message fallback mode

Characteristics of message fallback mode: when a message enters the switch and is routed to the queue, a callback method is executed if an exception occurs

Implementation steps

1. In the configuration file, turn on the producer publishing message fallback mode

2. In the MessageConfirmCallback class, implement the interface RabbitTemplate.ReturnCallback

3. And override the returnedMessage() method in the RabbitTemplate.ReturnCallback interface

4. In the RabbitTemplate, set the message publishing fallback callback method

5. Request test:

Test successful callback:

Test failure callback:

Implementation process

1. In the configuration file, turn on the producer publishing message fallback mode

# Turn on the producer fallback mode: (returns), the switch routes messages to the queue, and calls back in case of exceptions
spring.rabbitmq.publisher-returns=true

2. In the MessageConfirmCallback class, implement the interface RabbitTemplate.ReturnCallback

@Component
public class RabbitConfirm implements RabbitTemplate.ConfirmCallback
,RabbitTemplate.ReturnCallback {
//.. Omitted
}

3. And override the returnedMessage() method in the RabbitTemplate.ReturnCallback interface

/**
* When a message is delivered to the switch, the switch routes to the message queue. If an exception occurs, execute the returnedMessaged party
 method
* @param message Post message content
* @param replyCode Return error status code
* @param replyText Return error content
* @param exchange Switch name
* @param routingKey Routing key
*/
@Override
public void returnedMessage(Message message, int replyCode, String
replyText, String exchange, String routingKey) {
System.out.println("Error routing switch to message queue:>>>>>>>");
System.out.println("Switch:"+exchange);
System.out.println("Routing key:"+routingKey);
System.out.println("Error status code:"+replyCode);
System.out.println("Error reason:"+replyText);
System.out.println("Send message content:"+message.toString());
System.out.println("<<<<<<<<");
}

4. In the RabbitTemplate, set the message publishing fallback callback method

@PostConstruct

public void initRabbitTemplate(){

//Set message confirmation callback method

rabbitTemplate.setConfirmCallback(this::confirm);

//Set message fallback callback method

rabbitTemplate.setReturnCallback(this::returnedMessage);

}

5. Request test failed. Execute returnedMessage method: http://localhost:8080/direct/sendMsg?

exchange=order_ Exchange & routingkey = xxxxx & MSG = buy Apple phone

3: Summary

Confirmation mode

Set publisher confirms = "true" to enable confirmation mode.

Implement the RabbitTemplate.ConfirmCallback interface and override the confirm method

Features: no matter whether the message is successfully delivered to the switch, the confirm method is called back. Only when the sending fails, the business code needs to be written for processing.

Return mode

Set publisher returns = "true" to enable the return mode.

Implement the RabbitTemplate.ReturnCallback interface and override the returnedMessage method

Features: after a message enters the switch, the returnedMessage method is called back only when the route from exchange to queue fails;

3: Consumer confirmation (ACK)

ack refers to an Acknowledge, which has the meaning of confirmation. It is a confirmation mechanism for the consumer to receive a message;

1: Three types of message acknowledgement

Automatic confirmation: acknowledge="none"

Manual confirmation: acknowledge="manual"

Confirm according to the abnormal conditions: acknowledge="auto", (this method is troublesome and will not be explained)

Automatic confirmation means that once a message is received by the Consumer, it will automatically confirm receipt and send the corresponding message from the

RabbitMQ is removed from the message cache. However, in the actual business processing, it is likely that the message will be lost if an exception occurs in the business processing after the message is received.

If a manual confirmation is set, you need to call channel.basicAck() manually after the transaction is successful. If there is an exception, call the channel.basicNack() method to send the message automatically.

 

Implement the consumer sign in code yourself: the custom listener involves three objects: the three objects must be injected into the Spring container

1. Custom listener object

2. Adapter object of custom listener

3. Listener's container object

You can use the sign in method provided in RabbitTemplate:

2: Code implementation

target

Demonstrate consumer manual validation

Customize the consumer to receive the message listener, listen to the content of the received message, and sign in manually; When the business system throws an exception, it refuses to sign in and returns to the queue

Implementation steps

1. Build a new case project, consumer received ack, to demonstrate the sign in of ack consumers

2. In consumer engineering, create a custom listener class CustomAckConsumerListener to implement

ChannelAwareMessageListener interface

3. Write listener configuration class ListenerConfiguration and configure custom listener binding message queue order.A

Inject the message queue listener adapter object into the ioc container

Inject the message queue listener container object into the ioc container:

Configure connection factory

Configure custom listener adapter object

Configure message queuing

Enable manual sign in

4. Start the consumer service and observe whether the console and consumer listener establish a Connection with RabbitMQ

5. Test sending message and sign in manually

6. Simulation business logic is abnormal

7. Test the exception, demonstrate that the message is rejected and returned to the queue

Implementation process

1. Build a new case project consumer received ACK, and the construction process is similar to the manufacturer's confirmation

2. In consumer engineering, create a custom listener class CustomAckConsumerListener to implement

ChannelAwareMessageListener interface

/**
* Customize the listener. After listening to the message, execute the onMessage method immediately
*/
@Component
public class CustomAckConsumerListener implements
ChannelAwareMessageListener {
/**
* Method executed after listening to the message
* @param message Message content
* @param channel Message channel
*/
@Override
public void onMessage(Message message, Channel channel) throws
Exception {
//Get message content
byte[] messageBody = message.getBody();
String msg = new String(messageBody, "utf-8");
System.out.println("After receiving the message, execute the specific business logic{} Message content:"+msg);
//Get delivery label
MessageProperties messageProperties =
message.getMessageProperties();
long deliveryTag = messageProperties.getDeliveryTag();
/**
* The precondition for signing in messages is to enable the manual signing in mode in the listener configuration
* Parameter 1: message delivery label
* Parameter 2: batch sign in: true sign in all at once, false, sign in only the current message
*/
channel.basicAck(deliveryTag,false);
System.out.println("Manual sign in completed:{}");
}
}

3. Write listener configuration class ListenerConfiguration and configure custom listener binding message queue order.A

Inject the message queue listener adapter object into the ioc container

Inject the message queue listener container object into the ioc container:

Configure connection factory

Configure custom listeners

Configure message queuing

Enable manual sign in

/**
* Consumer listener configuration, binding listener to message queue
*/
@Configuration
public class ListenerConfiguration {
/**
* Injection message listener adapter
* @param customAckConsumerListener Custom listener object
*/
@Bean
public MessageListenerAdapter
messageListenerAdapter(CustomAckConsumerListener
customAckConsumerListener){
//Create a custom listener adapter object
return new
MessageListenerAdapter(customAckConsumerListener);
}
/**
* Inject message listener container
* @param connectionFactory Connection factory
* @param messageListenerAdapter Custom message listener adapter
*/
@Bean
public SimpleMessageListenerContainer
simpleMessageListenerContainer(
ConnectionFactory connectionFactory,
MessageListenerAdapter messageListenerAdapter){
//Simple message listener container object
SimpleMessageListenerContainer container = new
SimpleMessageListenerContainer();
//Bind message queue
container.setQueueNames("order.A");
//Set connection factory object
container.setConnectionFactory(connectionFactory);
//Setting up the message listener adapter
container.setMessageListener(messageListenerAdapter);
//Set manual confirmation messages: none, manual, auto
 Automatic confirmation message)
container.setAcknowledgeMode(AcknowledgeMode.MANUAL);
return container;
}
}

4. Start consumer control and observe whether the console and consumer listener establish a Connection with RabbitMQ

5. Test sending message, sign in manually and request address http://localhost:8080/direct/sendMsg?exchange=order_exch Ange & routingkey = order. A & MSG = buy Apple phone

6. Modify the user-defined listener when an exception occurs in the simulation business logic

@Override
public void onMessage(Message message, Channel channel) throws Exception
{
//Get message content
byte[] messageBody = message.getBody();
String msg = new String(messageBody, "utf-8");
System.out.println("After receiving the message, execute the specific business logic{} Message content:"+msg);
//Get delivery label
MessageProperties messageProperties =
message.getMessageProperties();
long deliveryTag = messageProperties.getDeliveryTag();
try {
if (msg.contains("Apple")){
throw new RuntimeException("Apple phones are not allowed!!!");
}
/**
* Manually sign in messages
* Parameter 1: message delivery label
* Parameter 2: batch sign in: true sign in all at once, false, sign in only the current message
*/
channel.basicAck(deliveryTag,false);
System.out.println("Manual sign in completed:{}");
} catch (Exception ex){
/**
* Manually reject sign in
* Parameter 1: delivery label of current message
* Parameter 2: batch sign in: true sign in all at once, false, sign in only the current message
* Parameter 3: whether to return to the queue. true means to return to the queue, and false means not to return
*/
channel.basicNack(deliveryTag,false,true);
System.out.println("Reject sign in and return to the queue:{}"+ex);
}
}

7. Test the exception, demonstrate that the message is rejected and returned to the queue

The request address contains an apple. An exception is thrown: http://localhost:8080/direct/sendMsg?exchange=order_ex

Change & routingkey = order. A & MSG = buy an Apple phone

Console print results

2: Summary

If you want to sign in messages manually, you need to customize the implementation of the message receiving listener

ChannelAwareMessageListener interface

Set the AcknowledgeMode mode

none: automatic

auto: exception mode

manual: manual

Call the channel.basicAck method to sign the message

Call the channel.basicNAck method to reject the message

Topics: Java Interview architecture