SpringBoot integrates RabbitMQ
Refer to the following blogger's article
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
Problems that may arise when producers send messages:
- The message sent by the producer did not reach the exchange
- The message sent by the producer arrived at the exchange, but not the queue
The problem that MQ lost after receiving the message:
- MQ is down, resulting in the loss of unpersisted saved messages
- After the consumer receives the message, it crashes before consuming the MQ
The solutions corresponding to MQ are as follows:
- The message sent by the producer is lost: use the producer confirmation mechanism
- MQ received message loss: MQ message persistence
- 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
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
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.
The message has not been delivered to the specified exchange.
The message has been delivered to the specified exchange.
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
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