#springboot integrated rabbitmq commodity spike business practice (traffic peak shaving) how does the message queue realize traffic peak shaving?
To cut the peak of traffic, the easiest solution is to use the message queue to buffer the instantaneous traffic, convert the synchronous direct call into asynchronous indirect push, accept the instantaneous traffic peak at one end through a queue, and push the message smoothly at the other end.
Insert picture description here
I won't talk about how to integrate springboot and rabbitmq here. Please refer to the article https://www.cnblogs.com/fantongxue/p/12493497.html
1, Preparation:
The database has a commodity table with an inventory of 100. Now 1000 consumers are ready to rob the 100 stocks.
t_ The product table maintains the item number and the remaining quantity of the item inventory. The inventory of this commodity numbered No123321 has 100.
Insert picture description here
t_product_record maintains the user ID of the grabbed goods. Theoretically t_ The number of records in the product table should be 100 (a total of 100 people grabbed the goods).
Insert picture description here
We use the stress testing tool jweter to test its concurrency.
2, Spring boot starts integrating rabbitmq
1. Add amqp dependency
<dependency>
<groupId>org.springframework.amqp</groupId>
<artifactId>spring-rabbit</artifactId>
</dependency>
2. Configure the application.yml configuration file
spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=utf-8&useSSL=true&serverTimezone=UTC
username: root
password: 1234
rabbitmq:
host: 101.201.101.206
username: guest
password: guest
publisher-confirms: true # Enable the Rabbitmq send message confirmation mechanism, send messages to the queue and trigger the callback method
publisher-returns: true
listener:
simple:
concurrency: 10 #Number of consumers
max-concurrency: 10 #Maximum number of consumers
prefetch: 1 #Current limit (number of messages consumers get from the queue each time)
auto-startup: true #Automatically start container on startup
acknowledge-mode: manual #Enable ACK manual confirmation mode
3. RabbitConfig configuration class
1. Define the message conversion instance and convert it into JSON transmission
2. Configure and enable rabbitmq transaction
package com.aaa.springredis.controller;
import org.springframework.amqp.rabbit.connection.CachingConnectionFactory;
import org.springframework.amqp.rabbit.transaction.RabbitTransactionManager;
import org.springframework.amqp.support.converter.Jackson2JsonMessageConverter;
import org.springframework.amqp.support.converter.MessageConverter;
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Component;
@Component
public class RabbitConfig {
/**
* Define the message conversion instance and convert it into JSON transmission * * @return Jackson2JsonMessageConverter */ @Bean public MessageConverter integrationEventMessageConverter() { return new Jackson2JsonMessageConverter(); }
/**
* Configure enable rabbitmq affair * * @param connectionFactory connectionFactory * @return RabbitTransactionManager */ @Bean public RabbitTransactionManager rabbitTransactionManager(CachingConnectionFactory connectionFactory) { return new RabbitTransactionManager(connectionFactory); } }
4. Initialize the callback function of rabbitmq
<font
Color = "red" > note: the method modified by @ PostConstruct will run when the server loads the Servlet, and will only be executed once by the server. If you want to complete some initialization operations when generating objects, but these initialization operations depend on dependency injection, you can't implement them in the constructor for a long time. For this purpose, you can use @ PostConstruct annotation to complete initialization. The @ PostConstruct annotation method will be called automatically after dependency injection is completed</ font>
The precondition for using the callback function is that the rabitmq message confirmation mechanism is enabled in the configuration file
Insert picture description here
Constructor >> @Autowired >> @PostConstruct
@Autowired
RabbitTemplate rabbitTemplate;
private static final Logger LOGGER = LoggerFactory.getLogger(RabbitController.class);
@PostConstruct
private void init(){
/**
* Message sent to switch Exchange Trigger callback after. * To use this function, you need to turn it on for confirmation, spring-boot The configuration in is as follows: * spring.rabbitmq.publisher-confirms = true */ rabbitTemplate.setConfirmCallback(new RabbitTemplate.ConfirmCallback() { @Override public void confirm(CorrelationData correlationData, boolean b, String s) { if (b) { System.out.println("Message confirmed cause:{"+s+"} - {"+correlationData+"}"); } else { System.out.println("Message unacknowledged cause:{"+s+"} - {"+correlationData+"}"); } } }); /** * By implementing the ReturnCallback interface, * Triggered if the message fails to be sent from the switch to the corresponding queue * For example, it will be triggered when the queue cannot be found according to the routingKey specified when sending the message * To use this function, you need to start confirmation. The spring boot configuration is as follows: * spring.rabbitmq.publisher-returns = true */ rabbitTemplate.setReturnCallback(new RabbitTemplate.ReturnCallback() { @Override public void returnedMessage(Message message, int replyCode, String replyText, String exchange, String routingKey) { LOGGER.error("Message returned:{}", message); LOGGER.error("Switch used by message:{}", exchange); LOGGER.error("The routing key used by the message:{}", routingKey); LOGGER.error("describe:{}", replyText); } }); }
3, Start test
1. Write order Grab Test
To write the order grabbing test class, we use jweter stress test tool to start 1000 threads for testing (start multi-threaded concurrent testing). Therefore, in order to distinguish each simulated user, we use userId accumulation to distinguish.
private int userId=0;
//Start grabbing orders
@RequestMapping("/begin")
@ResponseBody
public void begin(){
userId++;
this.send(userId);
}
The above send method is to send the received user request to rabbitmq message middleware.
@RequestMapping("/send")
@ResponseBody
public String send(Integer messge){
//The first parameter: switch name, the second parameter: the value of Routing Key, and the third parameter: the message object to be delivered
rabbitTemplate.convertAndSend("test.direct","test",messge);
return "Message sent successfully";
}
2. Configure rabbitmq listening method
rabbitmq listening, as mentioned in the previous article, is used to listen to messages received from the switch in a specified queue. One message is received until it is received!
Confirm whether the Message is received correctly through ACK. Each Message must be confirmed and can be sent manually
ACK or automatic ACK, if the information consumption fails, it will reject the current message and return the message to the original queue.
Receive the user's userId from the queue, and then simulate the purchase of goods (reduce an inventory and add a purchase record)
@Autowired
RabbitController controller;
/**
* @RabbitListener It can be marked on the class and needs to be matched @RabbitHandler Use with annotations * @RabbitListener The label on the class indicates that when a message is received, it will be handed over to the @RabbitHandler Which method to use, * according to MessageConverter Converted parameter type * * use @Payload and @Headers Annotations can be used in messages body And headers information * * adopt ACK Confirm whether it is received correctly, each Message All have to be confirmed( acknowledged),You can go there manually ACK Or automatic ACK */ @RabbitListener(queues = "test") //Specifies the queue name to listen on public void receiver(@Payload Integer userId, @Headers Channel channel, Message message) throws IOException { LOGGER.info("user{}Start grabbing orders", userId); try { //Processing messages controller.robbingProduct(userId); // Confirm that the message has been consumed successfully channel.basicAck(message.getMessageProperties().getDeliveryTag(), false); } catch (Exception e) { LOGGER.error("Consumption processing exception:{} - {}", userId, e); // Reject the current message and return the message to the original queue channel.basicNack(message.getMessageProperties().getDeliveryTag(), false, true); } }
Method of purchasing goods
public void robbingProduct(Integer userId){
Product product = testDao.selectProductByNo("No123321");
if (product != null && product.getTotal() > 0) {
//When updating the inventory table, the inventory quantity decreases by 1. Returning 1 indicates that the update is successful. Returning 0 indicates that the inventory has been 0
int i = testDao.updateProduct("No123321");
if(i>0){
//insert record
testDao.insertProductRecord(new ProductRecord("No123321", userId));
//Send SMS
LOGGER.info("user{}Order grabbing succeeded", userId);
}else {
LOGGER.error("user{}Order grabbing failed", userId);
}
} else {
LOGGER.error("user{}Order grabbing failed", userId);
}
}
3. jweter tool test concurrency
jweter stress test tool how to use Baidu, ignore here!
Console printing
Insert picture description here
Insert picture description here
The inventory in the database becomes 0
Insert picture description here
The purchase record stores the user id (100 records) of successful order grabbing
Insert picture description here
Of course, the remaining 900 users failed to grab orders!
<font
Color = "blur" > rabbitmq queue is in the order of first in first out, first in first out. You have to queue up for 1000 requests. After the first 100 requests succeed, it is doomed that the last 900 requests fail! < / font >
<font
color="red" >The main change in using RabbitMQ is that in the past, the order grabbing request was directly executed by our order grabbing application, and now the request is transferred to the RabbitMQ server. The RabbitMQ server queues the received order grabbing request, and finally the RabbitMQ server forwards the order grabbing request to our order grabbing application. This benefit is to avoid our order grabbing application The sequence directly processes a large number of requests in a short time. The RabbitMQ server is mainly used to slow down the concurrency pressure of the order grabbing application, which is equivalent to adding a request buffer before our order grabbing application. < / font >
The actual combat is over!
seckill](https://links.jianshu.com/go?to=https%3A%2F%2Fgithub.com%2Ffantongxue666%2Frabbitmq-
seckill)