Talk about how to make your project asynchronous.
1. Synchronous and asynchronous, blocking and non-blocking
Synchronous and asynchronous, blocking and non-blocking, this word has been a platize, at that time there are often many students do not know clearly, think that synchronous must be blocking, asynchronous must be non-blocking, other they are not the same thing.
Synchronous and asynchronous are concerned with the communication mechanism of the resulting messages
Synchronous: Synchronous means that the caller does not actively wait for the result to return. Asynchronous: Asynchronous means that the caller does not actively wait for the result to return, but by other means such as status notification, callback function, etc. Blocking and non-blocking are primarily concerned with waiting for the result to return to the caller’s state
Blocked: the current thread is suspended and does nothing until the result is returned. Non-blocked: the thread can do something else without being suspended until the result is returned. You can see synchronous and asynchronous, blocking and non-blocking, the main points of concern are different, and some people ask can synchronous be non-blocking, can asynchronous be blocking? Of course it can. Here are a few simple examples to better illustrate the meanings between their combinations: 1. Synchronous blocking: Synchronous blocking is basically the most common model in programming. For example, you go to the store to buy clothes. When you get there, you find that the clothes are sold out, and you wait in the store, doing nothing (including looking at your phone), until they are available.
2. Synchronous non-blocking: Synchronous non-blocking can be abstracted as a polling mode in programming. When you go to the store and find that the clothes are sold out, you don’t need to wait stupidly.
3. Asynchronous blocking: Asynchronous blocking is used less frequently in this programming. It is similar to writing a thread pool, submitting and future.get() immediately, so that the thread is still suspended. It’s kind of like when you go to the store to buy clothes, and when you find the clothes are gone, you give the boss a phone number and say to call me when the clothes arrive, and then you wait for the phone to ring and do nothing. It does feel silly, so this mode is used less often.
4. Asynchronous non-blocking: Asynchronous non-blocking this is also a core of highly concurrent programming today, and it’s a core of today’s focus. It’s like when you go to the store to buy clothes and they’re gone, all you have to do is tell the boss this is my phone number and call when the clothes arrive. Then you can go and play as you like, and you don’t have to worry about when the clothes will arrive, and when the phone rings, you can go and buy them.
2. Synchronous blocking versus asynchronous non-blocking
We have already seen how inefficient synchronous blocking is. If you use synchronous blocking to buy clothes, you may only buy one piece of clothes in a day and do nothing else. If you use asynchronous non-blocking to buy clothes, it is just a small thing that you do in a day.
Our code, the map to us when our thread a RPC calls or HTTP invocation, or some other time consuming IO call, after launch, if it is a synchronized block, we this thread will be blocked hangs, until the results back, imagine if IO call very frequent that our CPU utilization rate is very low is very low. So-called is everything, now that the CPU utilization rate is low, having a lot of IO call that we can use asynchronous non-blocking, when called in IO I don’t care about the results immediately, I just need to write a callback function to the IO call, this time I thread can continue to process new requests, when IO at the end of the end of the call, will call the callback function. Our thread is always busy, so we can do more meaningful things.
First of all, asynchrony is not a panacea. Asynchrony does not shorten the call time of your entire link, but it can greatly improve your maximum QPS. In general, there are two parts of our business that are time-consuming:
CPU: CPU time refers to our general business processing logic, such as some data operations, object serialization. This asynchronization cannot be solved, but requires some algorithm optimization or some high-performance framework. As mentioned above, iowait occurs during network calls, file transfers, etc., when threads are suspended and blocked. Our asynchrony is usually used to solve this part of the problem. 3. What can be asynchronized?
As mentioned above, asynchrony is used to solve the problem of IO blocking, and we can use asynchrony in general projects as follows:
Servlet asyncization, SpringMVC asyncization RPC calls like dubbo (THRIFT), HTTP calls asyncization database calls, cache calls asyncization and I’m going to talk about asyncization in a couple of ways.
4. The servlet asynchronous
Servlets are no stranger to Java developers, and whether you use Struts2 or SpringMVC in your projects, they are essentially packaged servlets. But our general development, in fact, is the use of synchronous blocking mode as follows:
The advantages of the above mode are that it is easy to code and suitable for projects with less traffic or a lot of CPU operations in the initial stage of the project
The disadvantage is that the business logic thread and the servlet container thread are the same, the general business logic has to occur IO, such as database query, such as RPC call, this time will block, and our servlet container thread must be limited. When servlet container threads are blocked, our service will be denied access. Otherwise, we can add a number of machines to solve this problem, but as the saying goes, it is better to rely on others to share the request than to handle it myself. So after servlet3.0 support for asynchrony, we use asynchrony to look like this:
Here we use a new thread to handle the business logic, so that the BLOCKING of IO calls does not affect our SERlVET, and the code to implement asynchronous SERlVET is relatively simple, as follows:
@WebServlet ( name
“WorkServlet” , urlPatterns
“/work” , asyncSupported
true ) public
class
WorkServlet
extends
HttpServlet {
private
static
final
long serialVersionUID
1L ;
@Override
protected
void doGet ( HttpServletRequest req ,
HttpServletResponse resp )
throws
ServletException ,
IOException
{
this . doPost ( req , resp );
}
@Override
protected
void doPost ( HttpServletRequest req ,
HttpServletResponse resp )
throws
ServletException ,
IOException
{
SetContentType (“text/plain; charset=UTF-8” ); resp . setHeader ( “Cache-Control” , “private” ); resp . setHeader ( “Pragma” , “no-cache” );
final
PrintWriter writer
resp . getWriter (); Writer. println (” Teacher checked homework “); writer . flush ();
List < String
zuoyes
new
ArrayList < String
(a);
for
( int i
0; i <
10; i ++)
{ zuoyes . add ( “zuoye” + i );;
}
// Enable asynchronous requests
final
AsyncContext ac
req . startAsync (); doZuoye ( ac , zuoyes ); Writer. println (” teacher assign homework “); writer . flush ();
}
private
void doZuoye ( final
AsyncContext ac ,
final
List < String
zuoyes )
{ ac . setTimeout ( 1 * 60 * 60 * 1000L ); ac . start ( new
Runnable ()
{
@Override
public
void run ()
{
// Get the character output stream through response
try
{
PrintWriter writer
ac . getResponse (). getWriter ();
for
( String zuoye : zuoyes )
{writer. println (“”” + zuoye + “”” “);
Thread . sleep ( 1 * 1000L ); writer . flush ();
} ac . complete ();
}
catch
( Exception e )
{ e . printStackTrace ();
}
}
});
}} The key to serlvet is that HTTP takes a long connection, meaning that when a request comes in, it does not close, even if it returns, because it may still have data until a close command is returned. AsyncContext ac=req.startAsync(); It is used to get the asynchronous context, and then we call back the data through the asynchronous context, kind of like when we buy clothes, we call the boss, and this context is also a phone, and when the clothes arrive, that is, when the data is ready, we can call and send the data. ac.complete(); Used to close long links.
In fact, very few people now do serlvet programming, are directly using some ready-made frameworks, such as Struts2, SpringMVC. Here’s how to do asynchrony with SpringMVC:
First make sure the servlets in your project are 3.0 or above!! , the second springMVC4.0 +
“3.0”
xmlns
“Java.sun.com/xml/ns/java…”
xmlns:xsi
“Www.w3.org/2001/XMLSch…”
xsi:schemaLocation
“Java.sun.com/xml/ns/java… Java.sun.com/xml/ns/java…”
Using SpringMVC encapsulates AsyncContext for servlets, which is relatively simple to use. Previously, the Controller of our synchronized mode was return ModelAndView, RequestMapping (value? RequestMapping (value
“/asynctask” , method
RequestMethod . GET )
public
DeferredResult < String
asyncTask ()
throws
IOReactorException
{
IOReactorConfig ioReactorConfig
IOReactorConfig . custom (). setIoThreadCount ( 1 ). build ();
ConnectingIOReactor ioReactor
new
DefaultConnectingIOReactor ( ioReactorConfig );
PoolingNHttpClientConnectionManager conManager
new
PoolingNHttpClientConnectionManager ( ioReactor ); conManager . setMaxTotal ( 100 ); conManager . setDefaultMaxPerRoute ( 100 );
CloseableHttpAsyncClient httpclient
HttpAsyncClients . custom (). setConnectionManager ( conManager ). build ();
// Start the client httpclient . start ();
// Set the timeout period to 200ms
final
DeferredResult < String
deferredResult
new
DeferredResult < String
( 200L ); deferredResult . onTimeout ( new
Runnable ()
{
@Override
public
void run ()
{
System.out. println (” Asynchronous call execution timed out! thread id is : “
Thread . currentThread (). getId ()); DeferredResult. SetResult (” timed out “);
}
});
System . out . println ( “/asynctask 调用!thread id is : “
Thread . currentThread (). getId ());
final
HttpGet request2
new
HttpGet ( “www.apache.org/” ); httpclient . execute ( request2 ,
new
FutureCallback < HttpResponse
(a)
{
public
void completed ( final
HttpResponse response2 )
{
System . out . println ( request2 . getRequestLine ()
“- >”
response2 . getStatusLine ()); deferredResult . setResult ( request2 . getRequestLine ()
“- >”
response2 . getStatusLine ());
}
public
void failed ( final
Exception ex )
{
System . out . println ( request2 . getRequestLine ()
“- >”
ex );
}
public
void cancelled ()
{
System . out . println ( request2 . getRequestLine ()
” cancelled” );
}
});
return deferredResult ;
} Note: a problem with serlVET asynchronization is that filter postprocessing cannot be used. For some of our methods, serlVET asynchronization cannot be used directly. This problem is solved in SpringMVC, which takes a more subtle approach to request forwarding, allowing the request to be filtered again. However, a new problem is introduced that the filter will be processed twice. Here we can use SpringMVC source code to determine its own method, we can use the following statement in the filter to determine whether the request is forwarded by SpringMVC, so as not to process the filter preevent. Only post-events are handled:
Object asyncManagerAttr
servletRequest . getAttribute ( WEB_ASYNC_MANAGER_ATTRIBUTE ); return asyncManagerAttr instanceof
WebAsyncManager
; 5. Full link asynchronous
We introduced the above serlvet asynchronous, believes that attentive students see it did not seem to solve the fundamental problem, my IO congestion still exists, just changing the position, when the IO thread pool will also make business calls frequently fill up quickly, although serlvet container thread is not blocked, but the business still can become unavailable.
So how can we solve the above problems? The answer is full link asynchrony. Full link asynchrony pursues no blocking, fills your CPU, and presses the machine’s performance to the extreme. The model is shown as follows:
What exactly does NIO Client do, as shown in the following model:
This is our full link asynchronous diagram (some thread pools can be optimized). The core of full link is that whenever we encounter IO calls, we can use NIO to avoid blocking, which also solves the embarrassing situation that the business thread pool is full.
5.1 Asynchronous Remote Invocation
We usually use RPC or HTTP for remote calls. For RPC, thrift, HTTP,motan, etc. support asynchronous calls. The internal principle is also based on the event-driven NIO model. For HTTP, apachehttpclient and okhttp also provide asynchronous calls. Here’s a quick overview of how Http asynchronous calls work:
public
class
HTTPAsyncClientDemo
{
public
static
void main ( String [] args )
throws
ExecutionException ,
InterruptedException ,
IOReactorException
{
// Specific parameter meanings will be explained later
// Apache provides the ioReactor parameter configuration, here we set the IO thread to 1
IOReactorConfig ioReactorConfig
IOReactorConfig . custom (). setIoThreadCount ( 1 ). build ();
Create an ioReactor based on this configuration
ConnectingIOReactor ioReactor
new
DefaultConnectingIOReactor ( ioReactorConfig );
/ / asyncHttpClient use PoolingNHttpClientConnectionManager manage our client connection
PoolingNHttpClientConnectionManager conManager
new
PoolingNHttpClientConnectionManager ( ioReactor );
// Set the maximum number of connections conmanager.setMaxTotal (100);
// Set the maximum number of connections per route conmanager.setDefaultMaxPerRoute (100);
// Create a Client
CloseableHttpAsyncClient httpclient
HttpAsyncClients . custom (). setConnectionManager ( conManager ). build ();
// Start the client httpclient . start ();
// Execute request
final
HttpGet request1
new
HttpGet ( “www.apache.org/” );
Future < HttpResponse
future
httpclient . execute ( request1 ,
null );
// and wait until a response is received
HttpResponse response1
future . get ();
System . out . println ( request1 . getRequestLine ()
“- >”
response1 . getStatusLine ());
// One most likely would want to use a callback for operation result
final
HttpGet request2
new
HttpGet ( “www.apache.org/” ); httpclient . execute ( request2 ,
new
FutureCallback < HttpResponse
(a)
{
// This method is called back when Complete succeeds
public
void completed ( final
HttpResponse response2 )
{
System . out . println ( request2 . getRequestLine ()
“- >”
response2 . getStatusLine ());
}
public
void failed ( final
Exception ex )
{
System . out . println ( request2 . getRequestLine ()
“- >”
ex );
}
public
void cancelled ()
{
System . out . println ( request2 . getRequestLine ()
” cancelled” );
}
});
}} The entire class diagram of httpAsync is shown below:
So for our HTTPAysncClient we actually ended up using InternalHttpAsyncClient, and inside of InternalHttpAsyncClient we have a ConnectionManager, so this is the manager that we’re managing connections to, And only an implementation that is in in httpAsync PoolingNHttpClientConnectionManager, has two to the connection manager that we are one of the biggest concerns of Reactor, a Cpool.
Reactor: All reactors implement the IOReactor interface. There will be have a Reactor in PoolingNHttpClientConnectionManager, That is DefaultConnectingIOReactor, this DefaultConnectingIOReactor, handle Acceptor. A excutor in DefaultConnectingIOReactor method, generating IOReactor BaseIOReactor in our figure, IO operation. This model is our 1.2.2 model above
A CPool CPool: in PoolingNHttpClientConnectionManager, is mainly responsible for controlling our connection, we said above maxTotal and defaultMaxPerRoute, are controlled by them, If each route is full, it breaks the oldest link, and if the total is full, it stores the header in the Queue, rewiring it as it frees space.
5.2 Database Invocation Asynchronization
For the database call general framework does not provide asynchronous method, here recommend their own packaging or use of open source online, here our company has an open source github.com/ainilife/ze… Asynchrony is well supported
6. The final
Asynchrony is not a silver bullet for high concurrency, but it does improve your machine’s QPS, throughput, and so on. If some of the models mentioned above can be reasonably optimized, and then applied, I believe that can be of great help to your service.