To be honest, multithreading is very common in web development, and many web containers natively support multi-threading, so many times we do not need to consider multithreading-related issues when we do web development, but only need to implement relevant business functions. So, as a general rule, web development is not a multi-threading consideration in many cases, because Web applications are based on multithreading.
However, some times in order to improve the performance of the program, in a user's request if there are too many business operations or contain long-time business operations, we need to consider the use of asynchronous way to improve the speed of the program response. This blog briefly describes how to use multithreading to implement a simple asynchronous framework in Java.
The main working process of this event asynchronous processing framework is that, after serializing the event entity class through the Producer class, it is stored in the Redis list queue. While Comsumer is responsible for reading event model objects in the event queue and deserializing the corresponding handler implementation class object for event handling, these handler implements the object of the class, through spring to complete the handler specific class registration operation, exists in a map structure, More specific please read the reader down, welcome to correct the shortcomings of the place!
I. The concept of synchronous and asynchronous
When we learn multithreading, the concept that we touch the most is the concept of synchronization, the meaning of synchronization in multi-threading is like this: the thread is always waiting to access the resource, knowing the end of the resource access. So, with the concept of synchronization, we can probably understand the concept of relative asynchrony: When a thread accesses a resource (or processes a time-consuming data), it does not have to wait for the resource access to complete or the data to be processed, and the thread can do other things during the wait, and when the resource access is complete, The corresponding code will be executed in a callback manner.
For example, in IO Read and write, the way of synchronization is that the blocking process of IO operation is blocked until the IO operation is completed, and asynchronous means that the IO operation blocks the process thread to do other things, and when the IO operation is completed, a callback method is taken to perform the corresponding operation.
Two, the model principle of the asynchronous framework
1. Producer-Consumer model
Readers who have known about design patterns should have heard of this famous design pattern, and its approximate idea is as shown in the example diagram below:
The idea is that the producer is responsible for the production of the data, which puts the data into memory (typically a queue), while the consumer is responsible for processing the in-memory data, and after processing is done, it can be responded by a callback. The above figure is sketchy and the following is a concrete implementation:
The specific implementation of producer consumers is described above:
Eventproducer (or, of course, dataproducer, etc.) is the producer, which encapsulates the data transmitted from the front end or the event that needs to be handled, and then puts the encapsulated data into a queue;
And Eventconsumer is the consumer, it reads the data in the queue and then processes it.
In this process, the program runs asynchronously: The producer does not have to wait for the consumer to finish, its job is to push the data into the memory, and then it can respond, and the consumer only needs to process the data, it does not have to control where the data comes from. Obviously, such a way can improve the speed of response, while making asynchronous implementation easier.
2. The idea of asynchronous framework in Web development
The producers above--consumers provide us with a good idea for implementing the Web's asynchronous framework: in complex business operations or long-time operations, we can use asynchronous methods to improve the responsiveness of our programs. The model of producer consumers is the reference model of our implementation of the asynchronous framework-the service layer of the complex service enables the corresponding producer, it only needs to put the data to be processed in a queue, then the corresponding user, and the corresponding handler class is responsible for the specific data processing.
3. Why use async?
Obviously, in the ideas described above, we can probably know when to use the asynchronous framework: a higher request for the corresponding speed, but a certain delay for the related business operation of the request.
To give a specific example: in a social networking site, a lot of times will be a bit like the operation, A to B like, generally will contain two actions, the first action is to tell a point like success, the second action is to tell B he was a point praise, if not in the way of asynchronous, it is necessary after both operations are completed, To respond to a say like success, but the second operation obviously takes a long time (such as the need to send e-mail notification), so do not take the asynchronous way when a will have such a feeling: How to praise to wait a half a day to respond to, what garbage system! Therefore, in order to improve the corresponding speed of a, we can adopt the asynchronous way: a point like the request issued, the program does not need to wait until B received a point like notification, only to tell a said you praise success, because B received a point like notice relative to a know their praise success, is allowed to delay.
Well, the above explanation may be a bit of a detour, but if you understand the example above, you probably know the asynchronous application scenario.
Iii. Simple Event-handling asynchronous framework
The front is so many, the following is a relatively simple example of the application of the asynchronous framework in Web development and how to implement a simple asynchronous framework.
First of all, in the following code, I extracted some of the business functions from a recent project, so I used the spring framework and the knowledge of Redis (for storing producer-generated data), and in order to improve the extensibility of the program, I used the interface-oriented programming approach, The use of spring's built-in features to realize the automatic registration of consumers, can not understand a little Baidu under the (in fact, just use a little Redis's fur function, after all, I was just contact with Redis rookie only, so don't worry about not understand)
1, the framework of the broad model
Mainly includes three parts: Producer producer class, Consumer Comsumer class, event handling handler interface and corresponding implementation class, specific event Eventmodel class (corresponding data).
Here, the Producer class serializes the Eventmodel object that is transmitted from the front end and joins it to an asynchronous queue, which is implemented using the Redis list data structure.
Consumer Comsumer is responsible for the Redis Squadron data read out, after deserialization, according to the Eventmodel in the EventType to invoke the corresponding handler specific implementation class (Handler implementation class stored in a map structure, Key corresponds to the eventtype,value corresponding to the specific handler implementation Class) for business processing.
The handler implementation class is responsible for the handling of the event, and it needs to implement a handler interface (which is the key to auto-registration via spring, which is discussed later).
Eventmodel is the event model, which mainly stores data related to events, including the type of event, time trigger, event owner, and so on. The details will be explained later.
The following is a detailed explanation of each module and the corresponding code implementation.
2. Eventmodel Event Model
Before explaining the rest, I think we should start with a brief explanation of how we should organize an event model. Go directly to the code, and note that annotations understand how to organize the event model:
/*** Event Model: Used to represent an event*/ Public classEventmodel {/*** Event type, used to identify the event, and in Comsumer based on this value to determine the specific implementation class of handler, generally can be implemented by an enumeration type * For example, the event type of the message like notification corresponding to the event should belong to a different eventtype, should be The handler implementation class should be different*/ PrivateEventType EventType; /*** Event trigger, such as User A to User B likes, A is the time trigger*/ Private intActionId;/*** Event corresponding to the relevant person, for example A to B likes, a corresponds to Actionid,actionownerid*/ Private intActionownerid; /**time processing required additional data, the use of a map to ensure that the program's extensibility * such as the registration of the operation of the message required to send the data and the likes of the notification needs the data is not the same, so with map storage to maximize the flexibility of the program*/ PrivateMap<string,string> exts =NewHashmap<>(); /*** Note that serialization requires an explicit parameter-free constructor*/ PublicEventmodel () {}/*** Getter and setter, this part omitted*/ }
When organizing Eventmodel, we should ensure flexibility, take out the necessary variables, and use a map structure to store the additional data that the specific business may need.
3, producer class
The producer function is more single, just serialize the Eventmodel, and then add it to the appropriate time queue, the code is as follows:
/** event producer @Component public class Eventproducer {@Autowired Jediseventhandleradaptor Jediseventhandleradaptor; @Autowired private Jediskeyutil Jediskeyutil; public void add (Eventmodel model) {String Modeljson = jsonobject.tojsonstring (model); Jediseventhandleradaptor.add (Jediskeyutil.geteventhandlerkey (), Modeljson); }}
Readers who have not contacted Redis can assume that the above jediseventhandleradaptor is actually a class that can operate a queue, and in Java it can also be implemented with blocking queues, and more specific readers can try them on their own.
4, Comsumer class
In this asynchronous event processing framework, Comsumer is primarily responsible for the following responsibilities:
Reads the Eventmodel object in the event queue, deserializes it, and invokes the specific handler implementation class according to EventType;
At the time of initialization, the spring framework is used to automatically register the handler implementation class and store it in a map data structure, and key is Eventtype,valuee is the object handler the concrete implementation class.
Please note the comments in the code in the specific way of implementation:
/*** Event Processing class, which is responsible for calling handler, handling events, need to implement spring's two interfaces, Initializingbean interface is initialized when the automatic registration of handler to use; Applicationcontextaware is the interface that calls Spring's ApplicationContext (the Bean object that stores the handler concrete implementation class in the ApplicationContext), which needs to implement *. by ApplicationContext get handler corresponding beans, then you can automatically register handler to the following Config object (is a map)*/@Component Public classEventcomsumerImplementsInitializingbean, applicationcontextaware{Private Static FinalLogger Logger = Loggerfactory.getlogger (Messagecontroller.class); /*** Threadpoolutil encapsulates thread-pooling-related operations*/@AutowiredPrivateThreadpoolutil ThreadPool; @AutowiredPrivateJediseventhandleradaptor adaptor; /*** This is a tool class related to redis interaction to get a specific Redis key to avoid key conflicts and readers can ignore*/@AutowiredPrivateJediskeyutil Jediskeyutil; /*** Spring Context object, which stores the handler bean object, must be initialized through Setapplicationcontext (implementation of the Applicationcontextaware interface) * To get S The beans of handler concrete implementation class in Pring*/ PrivateApplicationContext ApplicationContext; /**set the consumption function blocking time, tentatively one day, Redis blocks the parameters that must be in the list, and the reader can ignore*/ Private Static intComsume_timeout = 24*3600; /*** Congif: This variable is used to store the mappings between type and EventType, which can be called directly according to your mapping relationship in config during consumption * Note that in order to ensure the flexibility of the program, EventHandler uses an LIS T is stored, because it is possible that a EventType event type may correspond to multiple * Handler event handlers, such as a point-like notification that the event type may need to notify the person being liked and notify the system administrator, so it should correspond to two events handler * More specific can refer to hand Ler interface Design-time annotations*/Map<EventType,List<EventHandler>> config =NewHashmap<>(); /*** When spring initializes the object, it registers all handler specific objects in the Config object*/@Override Public voidAfterpropertiesset ()throwsException {
Get all handler specific object Map<String,EventHandler> beans = Applicationcontext.getbeansoftype (EventHandler.class);
Iterate register Handler Object for(map.entry<string,eventhandler>Entry:beans.entrySet ()) {EventHandler handler=Entry.getvalue ();
Since a handler can also correspond to multiple event types, a handler is registered to all EventType, where it can be understood in conjunction with the comments in the following explanation handler interface code. for(EventType type:handler.getHandlerType ()) {if(Config.get (type) = =NULL) {config.put (type,NewArraylist<eventhandler>()); } config.get (Type). Add (handler); } } //the thread calls the consumption function, attention cannot be called directly, otherwise it will cause the main thread to blockThreadpool.execute (NewRunnable () {@Override Public voidrun () {doconsume (); } }); } /*** Consumption function for execution of handler*/ Public voidDoconsume () { while(true) {List<String> list =Adaptor.pop (string.valueof (comsume_timeout), Jediskeyutil.geteventhandlerkey ()); //deserializationEventmodel model = Json.parseobject (List.get (1), Eventmodel.class); EventType type=Model.geteventtype (); //gets the handler of the eventlist<eventhandler> handlers =config.get (type); //Executive Handler for(EventHandler handler:handlers) {Handler.dohandler (model); } }} @Override Public voidSetapplicationcontext (ApplicationContext ApplicationContext)throwsbeansexception { This. ApplicationContext =ApplicationContext; }}
Here we need to explain the process of handler autoenrollment in Comsumer (Afterpropertitiesset method and Config object):
First of all, our Config object is a map,key is a eventtype, representing an event, and why use list to represent EventHandler? This is because an event may correspond to multiple EventHandler, so in order to ensure flexibility, storing the handler in list form is most appropriate, and the Gethandlertype method in the handler method, The following handler interface design is explained in detail.
In addition, about Initializingbean, the role of the Applicationcontextaware two interface specifically, the reader shares do not know why must realize the two interfaces to use spring to achieve automatic registration, can be Google or Baidu under, I believe it's easy to find the answer.
5. Handler interface Design
Directly on the code, please look at the note:
/*** Event handler: Used to process events in the event queue, called by Eventconsumer * Dohandler:model is a specific event model that needs to be passed in by the caller (typically Comsumer)*/ Public InterfaceEventHandler { Public voidDohandler (Eventmodel model); /** * * @returnindicates what type of Handler,list the interface indicates that handler can support multiple businesses, that is, one handler can correspond to multiple EventType
* For example, Sendemailhandler, e-mail handler, specific business such as registering the activation of the type of event, like the email notification time type will need this handler,
* So a handler is necessary to correspond to a number of eventtype, here please readers must understand, at the beginning I also understand a very long time. Therefore, the specific handler implementation class must have a list variable to store its corresponding event type*/ PublicList<eventtype>Gethandlertype ();}
Here handler why to correspond to a plurality of eventtype please refer to the annotation understanding, I think understanding this is very important, when you understand this, look back on the above automatic registration process (in the Comsumer Class) will not feel confused.
Finally, we just need to implement the EventHandler interface, Comsumer will automatically register the class at spring startup, we just have to declare in the service Eventtype,comsumer will automatically find the appropriate interface to do the operation.
6. Summary
This simple asynchronous leave processing framework example is probably resolved here, in fact, I think the main thing is through this event processing framework design process to understand and understand the producer consumer design model and the working principle of the asynchronous framework; There are many other things to understand about this process: How to design an interface for flexibility, what it means to register an object, how to automate registration, and so on.
Used a morning finally to write this blog, in fact, this asynchronous event processing framework is a bit rough, but in my more complex asynchronous framework working principle is basically the same, but also hope that this blog can give readers a little bit of harvest, insufficient places please the big guy!
Multithreaded implementation of simple event asynchronous processing framework