summary
Installation and introduction
Introduction to typical application scenarios
Asynchronous communication and service decoupling
Taking user registration as the actual scenario, the traditional enterprise application processes user registration. First, the user enters user name, email or mobile phone number on the interface. After confirmation, click registration, and the front end will submit these information to the relevant back-end interface for processing; After receiving these information, the back-end will first perform the most basic verification on these information. After the verification is successful, the information will be written into the relevant data table of the database. For the security of user registration information, the back-end will call the interface provided by the mail server to send an email to verify the legitimacy of the user account, Or call the sending SMS verification code interface of SMS service to verify the user; After the verification is passed, the response information is returned to the front-end user and a prompt of successful registration is given.
However, if the business logic of sending e-mail or SMS is abnormal in the process, the whole process will terminate - obviously, this processing method is almost unacceptable to current Internet users.
After careful analysis, it can be found that the core logic is to "judge the legitimacy of user registration information and write it into the database". To some extent, sending e-mail or short message service does not belong to the core process of user registration, so the corresponding services can be decoupled, and the message middleware RabbitMQ is used for asynchronous communication.
Interface current limiting and message distribution
Take the shopping mall users' rush purchase of goods as an example. At the beginning of the rush purchase of goods, due to the limited number of goods, all users click the rush purchase button to start the rush purchase of goods at almost the same time.
There is no doubt that at the beginning of the rush purchase, there will be huge user rush purchase requests, which arrive at the back-end system interface almost at the same time.
Under normal circumstances, the backend interface will execute the following process after receiving the request from the front end:
Firstly, the legitimacy of the user and commodity information will be verified. After the verification is passed, it will judge whether the inventory of the current commodity is sufficient. If sufficient, it means that the current user will be able to successfully rush purchase the commodity. Finally, the relevant data records of the user's successful rush purchase will be stored in the database, and the user will be notified of the success of the rush purchase asynchronously, and payment will be made as soon as possible.
However, careful observation shows that the overall business process of the back-end system in dealing with user rush purchase is too long. In the process of business logic processing, the inventory is taken out first, then judged, and finally updated by subtracting one. In the case of high concurrent requests, these business operations will bring many problems to the system, such as oversold goods, inconsistent data, long user waiting time, hanging system interface and so on. Therefore, this single processing process is only applicable to the case where there are few front-end requests at the same time. For similar shopping malls, commodity spike, When high concurrent requests are generated at a certain time, it seems that it is not enough.
However, the introduction of message oriented middleware can greatly improve the overall business process and performance of the system.
The main system optimization processing has two aspects
(1) Interface flow restriction: high concurrent requests generated at the front end will not immediately reach the back-end system interface like headless flies, but will be stored in the RabbitMQ queue in the order of first come, first served, just like the subway flow restriction at work tomorrow, that is, interface flow restriction will be realized to some extent.
(2) Asynchronous message distribution: when the commodity inventory is sufficient, the current rush purchase user can grab the commodity, and then asynchronously notify the user of the success of the rush purchase by sending text messages and e-mails, and inform the user to pay as soon as possible, that is, the message is distributed in different steps to some extent
Delay processing
Take 12306 ticket grabbing as an example, but when we grab the train ticket, 12306 will remind the user to pay within 30 minutes. Under normal circumstances, the user will click payment immediately, and then enter the corresponding payment password to pay the cost of the train ticket. After the deduction is successful, 12306 will send email or SMS to inform the user of ticket grabbing and payment success.
However, in reality, there are some special circumstances. For example, after the user grabs the train ticket, he fails to pay for various reasons, and still fails to pay the ticket fee after 30 minutes, resulting in the system automatically canceling the order. Similar foreign matters that need to be processed after a certain delay are not uncommon in the actual production of huayouxin. When traditional enterprise applications process this business, they use a timer to obtain orders that have not been paid, and judge whether the user's order time is more than 30 minutes from the current time. If so, it means that the user still has not paid within 30 minutes, The system will automatically invalidate the order and withdraw the ticket.
In the big data and high concurrency scenario of Spring Festival transportation ticket grabbing, users will grab tickets one after another under normal circumstances, but there is a certain time interval from the success of ticket payment. During this period, if the timer frequently obtains orders in unpaid status from the database, the amount of data will be unimaginable, Moreover, if a large number of users do not pay within 30 minutes, the amount of data obtained from the database will continue to grow. To a certain extent, it will bring great pressure to the database server and application server, and even crush the server, resulting in the overall collapse of ticket grabbing and other businesses, with unimaginable consequences.
The introduction of message oriented middleware, whether from the business level or application level, has greatly improved the overall business process and performance of the system.
From the optimization process, it can be seen that the introduction of RabbitMQ mainly cancels the timer processing logic of the traditional processing process, and instead uses the delay queue of RabbitMQ for processing. As the name suggests, delay queue refers to processing the corresponding business logic for a certain time.
Event driven model based on Spring
Before starting RabbitMQ terminology and various message models, let's first understand how to build a message model in the traditional Spring application system to realize the asynchronous distribution of messages and the decoupling of business modules. Take a look at the built-in ApplicationEvent and ApplicationListener event driven models.
Spring's event driven model is mainly composed of three parts, including the producer who sends the message, the message, and the consumer who listens and receives the message. The three are bound together. It can be said that it is inseparable, which is very similar to the producer + message + switch + routing corresponding queue + consumer of RabbitMQ message model.
package com.zwx.middleware.event; import lombok.Data; import lombok.ToString; import org.springframework.context.ApplicationEvent; import java.io.Serializable; /** * Event entity after successful user login **/ @ToString @Data public class LoginEvent extends ApplicationEvent implements Serializable{ private String userName; //user name private String loginTime; //login time private String ip; //Host ip public LoginEvent(Object source) { super(source); } //Overloaded constructor is required when inheriting ApplicationEvent class public LoginEvent(Object source, String userName, String loginTime, String ip) { super(source); this.userName = userName; this.loginTime = loginTime; this.ip = ip; } }
package com.zwx.middleware.event; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.context.ApplicationListener; import org.springframework.scheduling.annotation.Async; import org.springframework.scheduling.annotation.EnableAsync; import org.springframework.stereotype.Component; @Component @EnableAsync//Allow asynchronous execution public class Consumer implements ApplicationListener<LoginEvent>{ private static final Logger log= LoggerFactory.getLogger(Consumer.class); /** * Listening for consumption messages - event information * @param loginEvent */ @Override @Async public void onApplicationEvent(LoginEvent loginEvent) { log.info("Spring Event driven model-Receive message:{}",loginEvent); //TODO: subsequently, it is necessary to realize its own business requirements - such as writing to the database, etc } }
package com.zwx.middleware.event; import lombok.extern.slf4j.Slf4j; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.ApplicationEventPublisher; import org.springframework.stereotype.Component; import java.text.SimpleDateFormat; import java.util.Date; /** * spring Event driven model producer **/ @Component @Slf4j public class Publisher { @Autowired private ApplicationEventPublisher publisher; public void sendMsg() throws Exception{ LoginEvent event=new LoginEvent(this,"debug",new SimpleDateFormat("yyyyy-MM-dd HH:mm:ss").format(new Date()),"127.0.0.1"); publisher.publishEvent(event); log.info("Spring Event driven model-Send message:{}",event); } }
package com.zwx.middleware; import com.zwx.middleware.event.Publisher; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; @RunWith(SpringJUnit4ClassRunner.class) @SpringBootTest public class EventTest { @Autowired private Publisher publisher; @Test public void test1() throws Exception{ publisher.sendMsg(); } }
Because we accept messages asynchronously, it can be seen from the console output that the method of receiving and processing messages is executed by sub threads different from the main thread. This method can achieve the purpose of asynchronous communication and business module decoupling.