Spring boot asynchronous requests and asynchronous calls

Keywords: Spring Spring Boot

Transferred from: Micro reading (www.weidianyuedu.com) Micro reading - complete collection of model articles - a website for free learning knowledge

1, Use of asynchronous requests in Spring Boot**

1. Asynchronous request and synchronous request

Synchronization request

Asynchronous request

Features: you can first release the threads and related resources allocated by the container to the request, reduce the system burden, and release the request of the thread allocated by the container. Its response will be delayed. You can respond to the client when the time-consuming processing is completed (such as long-time operation). Bottom line: it increases the throughput of the server to client requests (we use less in actual production. If there are a large number of concurrent requests, we will load the requests to each node of the cluster service through nginx to share the request pressure. Of course, we can also buffer the requests through the message queue).

2. Implementation of asynchronous request

Method 1: realize asynchronous request in Servlet mode

@RequestMapping(value = "/email/servletReq", method = GET)  public void servletReq (HttpServletRequest request, HttpServletResponse response) {      AsyncContext asyncContext = request.startAsync();      //Set listener: you can set the callback processing of its start, completion, exception, timeout and other events        asyncContext.addListener(new   AsyncListener()   {           @ Override            public   void   onTimeout(AsyncEvent   event)   throws   IOException   {                System.out.println("timeout...);               // Do some related operations after timeout           }          @ Override            public   void   onStartAsync(AsyncEvent   event)   throws   IOException   {                System.out.println("thread start");           }          @ Override            public   void   onError(AsyncEvent   event)   throws   IOException   {                System.out.println("error occurred:" + event.getThrowable());           }          @ Override            public   void   onComplete(AsyncEvent   event)   throws   IOException   {                System.out.println("execution completed");               // Here you can do some operations to clean up resources           }      });      // Set timeout        asyncContext.setTimeout(20000);        asyncContext.start(new   Runnable()   {           @ Override            public   void   run()   {                try   {                    Thread.sleep(10000);                    System.out.println("internal thread:"  +  Thread.currentThread().getName());                    asyncContext.getResponse().setCharacterEncoding("utf-8");                    asyncContext.getResponse().setContentType("text/html;charset=UTF-8");                    asyncContext.getResponse().getWriter().println("this is an asynchronous request return");               }  catch   (Exception   e)   {                    System.out.println("exception:" + e);               }              // Asynchronous request completion notification               // At this time, the whole request is completed                asyncContext.complete();           }      });      // At this time and so on   The thread connection of request has been released        System.out.println("main thread:"  +  Thread.currentThread().getName());   }

Method 2: it is very simple to use. The directly returned parameters can be wrapped with a layer of label. You can inherit the WebMvcConfigurerAdapter class to set the default thread pool and timeout processing

@RequestMapping(value = "/email/callableReq", method = GET)  @ResponseBody  public Callable<String> callableReq () {      System.out.println("External thread:" + Thread.currentThread().getName());      return new Callable<String>() {          @Override          public String call() throws Exception {              Thread.sleep(10000);              System.out.println("Internal thread:" + Thread.currentThread().getName());              return "callable!";          }      };  }  @Configuration  public class RequestAsyncPoolConfig extends WebMvcConfigurerAdapter {  @Resource  private ThreadPoolTaskExecutor myThreadPoolTaskExecutor;  @Override  public void configureAsyncSupport(final AsyncSupportConfigurer configurer) {      //handle   callable timeout        configurer.setDefaultTimeout(60*1000);        configurer.setTaskExecutor(myThreadPoolTaskExecutor);        configurer.registerCallableInterceptors(timeoutCallableProcessingInterceptor());   }  @ Bean    public   TimeoutCallableProcessingInterceptor   timeoutCallableProcessingInterceptor()   {        return   new   TimeoutCallableProcessingInterceptor();   }}

Method 3: similar to method 2, set a timeout callback for WebAsyncTask in the Callable outsourcing layer to realize timeout processing

@RequestMapping(value = "/email/webAsyncReq", method = GET)    @ResponseBody    public WebAsyncTask<String> webAsyncReq () {        System.out.println("External thread:" + Thread.currentThread().getName());        Callable<String> result = () -> {            System.out.println("Internal thread start:" + Thread.currentThread().getName());            try {                TimeUnit.SECONDS.sleep(4);            } catch (Exception e) {                //  TODO:   handle   exception             }             logger.info("returned by the secondary thread");              System.out.println("internal thread return:"  +  Thread.currentThread().getName());              return   "success";         };         WebAsyncTask<String>   wat  =  new   WebAsyncTask<String>(3000L,   result);          wat.onTimeout(new   Callable<String>()   {             @ Override              public   String   call()   throws   Exception   {                 //  TODO   Auto-generated   method   stub                  return   "Timeout";             }        });         return   wat;     }

Mode 4: DeferredResult can handle some relatively complex business logic. The most important thing is that it can process and return business in another thread, that is, it can communicate between two completely unrelated threads.

@RequestMapping(value = "/email/deferredResultReq", method = GET)    @ResponseBody    public DeferredResult<String> deferredResultReq () {        System.out.println("External thread:" + Thread.currentThread().getName());        //Set timeout          DeferredResult<String>   result  =  new   DeferredResult<String>(60*1000L);         // Processing timeout events   Adopt entrustment mechanism          result.onTimeout(new   Runnable()   {             @ Override              public   void   run()   {                  System.out.println("DeferredResult timeout");                  Result.setresult ("timeout!");             }        });         result.onCompletion(new   Runnable()   {             @ Override              public   void   run()   {                 // After completion                  System.out.println("call completed");             }        });         myThreadPoolTaskExecutor.execute(new   Runnable()   {             @ Override              public   void   run()   {                 // Processing business logic                  System.out.println("internal thread:"  +  Thread.currentThread().getName());                 // Return results                  result.setResult("DeferredResult!!");             }        });        return   result;     }

2, Use of asynchronous calls in Spring Boot

1. Introduction

Asynchronous request processing. In addition to asynchronous requests, we usually use asynchronous calls. Usually, in the development process, we will encounter a method that has nothing to do with the actual business and is not tight. For example, logging information and other businesses. At this time, it is normal to start a new thread to do some business processing and let the main thread execute other businesses asynchronously.

2. Usage (based on spring)

You need to add @ EnableAsync to the startup class to make the asynchronous call @ Async annotation take effect. You can add this annotation to the methods that need asynchronous execution @ Async ("ThreadPool"), which is a custom thread pool. The code is omitted... Just two tags. Just try it yourself

3. Precautions

By default, when TaskExecutor is not set, the thread pool SimpleAsyncTaskExecutor is used by default, but this thread is not a real thread pool because threads are not reused and a new thread will be created every time they are called. It can be seen from the console log output that the thread name is incremented every time. Therefore, it is best to define a thread by ourselves Program pool. The asynchronous methods called cannot be methods of the same class (including internal classes of the same class) To put it simply, Spring will create a proxy class for it when it starts scanning, and when similar calls are made, it still calls its own proxy class, so it is the same as normal calls. Other annotations such as @ Cache are the same. To put it bluntly, they are made by Spring's agent. Therefore, it is best to extract a separate class for asynchronous services in development Management. The following will focus on..

4. Under what circumstances will @ Async asynchronous methods fail?

Call the same class and bet @ Async asynchronous method:

In spring, annotations such as @ Async, @ Transactional and cache essentially use dynamic proxies. In fact, when the spring container initializes, the spring container will "replace" the class object with AOP annotation as a proxy object (simply understood as this) , the reason why the annotation fails is obvious, because the method is called by the object itself rather than the proxy object, and because it has not passed through the spring container, the solution will follow this idea.

The static method is called

Call the private method

5. How to solve problem 1 in 4 (just pay attention to the other two problems in 2 and 3)

The principle of extracting the methods to be executed asynchronously into a class is that when you extract the methods to be executed asynchronously into a class, the class must be managed by Spring, and other Spring components will be injected when they need to be called. At this time, what is actually injected is the proxy class. In fact, our injection objects are all injected from the Spring container At present, the Spring component assigns member variables. Because some classes use AOP annotations, its proxy object actually exists in the Spring container. Then we can get our own proxy object through the context and call asynchronous methods.

@Controller@RequestMapping("/app")public class EmailController {    //There are many ways to obtain ApplicationContext objects. This is the simplest. Let's learn about others by ourselves     @ Autowired      private   ApplicationContext   applicationContext;     @ RequestMapping(value  =  "/email/asyncCall",   method  =  GET)     @ ResponseBody      public   Map<String,   Object>   asyncCall   ()   {          Map<String,   Object>   resMap  =  new   HashMap<String,   Object>();          try{             // It doesn't work to call asynchronous methods of the same kind             // this.testAsyncTask();             // Get your own proxy object through the context and call asynchronous methods              EmailController   emailController  =  (EmailController)applicationContext.getBean(EmailController.class);              emailController.testAsyncTask();              resMap.put("code",200);         } catch   (Exception   e)   {              resMap.put("code",400);              logger.error("error!",e);         }         return   resMap;     }    // Note that it must be public and non static     @ Async      public   void   testAsyncTask()   throws   InterruptedException   {          Thread.sleep(10000);          System.out.println("asynchronous task execution completed!");     }}

Open the cglib proxy and manually obtain the Spring proxy class to call asynchronous methods under the same class. First, add  @ EnableAspectJAutoProxy (exposeProxy = true) annotation. The code implementation is as follows:

@Service@Transactional(value = "transactionManager", readOnly = false, propagation = Propagation.REQUIRED, rollbackFor = Throwable.class)public class EmailService {    @Autowired    private ApplicationContext applicationContext;    @Async    public void testSyncTask() throws InterruptedException {        Thread.sleep(10000);        System.out.println("Asynchronous task execution completed!");    }    public void asyncCallTwo() throws InterruptedException {        //this.testSyncTask();//   EmailService   emailService  =  (EmailService)applicationContext.getBean(EmailService.class);//   emailService.testSyncTask();          boolean   isAop  =  AopUtils.isAopProxy(EmailController.class);// Whether it is a proxy object;          boolean   isCglib  =  AopUtils.isCglibProxy(EmailController.class);  // Whether it is a proxy object in CGLIB mode;          boolean   isJdk  =  AopUtils.isJdkDynamicProxy(EmailController.class);  // Whether it is a proxy object of JDK dynamic proxy mode;         // The following is the key!!!          EmailService   emailService  =  (EmailService)applicationContext.getBean(EmailService.class);          EmailService   proxy  =  (EmailService)   AopContext.currentProxy();          System.out.println(emailService  ==  proxy  ?  true  :  false);          proxy.testSyncTask();          System.out.println("end!!!");     }}

3, The difference between asynchronous request and asynchronous call is different. Asynchronous request is used to solve the pressure on the server caused by concurrent requests, so as to improve the throughput of requests; Asynchronous call is used to do some tasks that are not mainline processes and do not require real-time calculation and response, such as synchronizing logs to kafka for log analysis. The asynchronous request will always wait for the corresponding response, and the result needs to be returned to the client; For asynchronous calls, we often return the response to the client immediately to complete the whole request. As for the task of asynchronous calls, the background can run slowly, and the client won't care.

4, To summarize the use of asynchronous requests and asynchronous calls, it's almost here. I hope you can point out more problems.

Posted by alexcrosson on Wed, 01 Dec 2021 06:39:49 -0800