Don't understand asynchronous requests and calls in spring boot? Just read this one

1, The use of asynchronous requests in Spring Boot

1. Asynchronous request and synchronous request

characteristic:

The thread and related resources allocated by the container to the request can be released first to reduce the burden of the system, and the request of the thread allocated by the container can be released. The response will be delayed, and the client can respond when the time-consuming processing is completed (such as a long-time operation).

Bottom line: it increases the throughput of requests from the server to the client (we use less in actual production. If the concurrent requests are large, we will load the requests to each node of the cluster service through nginx to share the request pressure, and of course, we can buffer the requests through the message queue).

2. Implementation of asynchronous request

Mode 1: implement 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 events such as start, finish, exception, timeout, etc
    asyncContext.addListener(new AsyncListener() {
            @Override
            public void onTimeout(AsyncEvent event) throws IOException {
                System.out.println("Time out...");
                //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("An error occurred:"+event.getThrowable());
            }
            @Override
            public void onComplete(AsyncEvent event) throws IOException {
                System.out.println("Execution complete");
                //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
                //The entire request is now complete
                asyncContext.complete();
            }
        });
        //At this point, the thread connection of request has been released
        System.out.println("Main thread:" + Thread.currentThread().getName());
    }

Method 2: it is easy to use. The parameters returned directly wrap a layer of callable. 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) {
        //Processing callable timeout
        configurer.setDefaultTimeout(60*1000);
        configurer.setTaskExecutor(myThreadPoolTaskExecutor);
        configurer.registerCallableInterceptors(timeoutCallableProcessingInterceptor());
    }
​
    @Bean
    public TimeoutCallableProcessingInterceptor timeoutCallableProcessingInterceptor() {
        return new TimeoutCallableProcessingInterceptor();
    }
​
}

Mode 3: similar to mode 2, set a timeout callback for the WebAsyncTask on the Callable outsourcing layer 1 to implement 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("Sub thread return");
            System.out.println("Internal thread returned:" + 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 "overtime";
            }
        });
        return wat;
    }

Mode 4: DeferredResult can handle some relatively complex business logic. The most important thing is to process and return business in another thread, which 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);
        //Handling timeout events with delegation mechanism
        result.onTimeout(new Runnable() {
​
            @Override
            public void run() {
                System.out.println("DeferredResult overtime");
                result.setResult("Time out!");
            }
        });
        result.onCompletion(new Runnable() {
​
            @Override
            public void run() {
                //After completion
                System.out.println("Call complete");
            }
        });
        myThreadPoolTaskExecutor.execute(new Runnable() {
​
            @Override
            public void run() {
                //Process business logic
                System.out.println("Internal thread:" + Thread.currentThread().getName());
                //Return results
                result.setResult("DeferredResult!!");
            }
        });
       return result;
    }

2, The use of asynchronous call in Spring Boot

1. Introduction

Processing of asynchronous requests. In addition to asynchronous requests, we usually use asynchronous calls. Usually in the development process, there is a method that has nothing to do with the actual business and has no compactness. Such as 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

Add this annotation to the method requiring asynchronous execution to @ async ("ThreadPool"), which is a custom thread pool.

Code omitted. Just two labels. Just try one on your own.

3. Precautions

By default, when TaskExecutor is not set, SimpleAsyncTaskExecutor is used as the thread pool by default, but this thread is not a real thread pool, because the thread is not reused, and a new thread will be created every time it is called. You can see from the console log output that the output thread name is incremented every time. So it's best to come from defining a thread pool.

The asynchronous method to be called cannot be the method of the same class (including the internal class of the same class). Simply speaking, because Spring will create a proxy class for it when it starts scanning, and when it calls the same kind, it still calls its own proxy class, so it is the same as the normal call.

Other annotations, such as @ Cache, are the same. To put it bluntly, they are caused by Spring's proxy mechanism. Therefore, in development, it is better to separate asynchronous services out of a class to manage. The following will focus on..

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

Calling the same class bets @ Async asynchronous method:

In spring, annotations such as @ Async and @ Transactional and cache use dynamic agents in essence. In fact, when spring container initializes, it will "replace" the class object with AOP annotation as the proxy object (in a simple way), so the reason for annotation failure is obvious, because it is the object itself rather than the proxy object that calls the method, because there is no proxy object After the spring container, the solution will follow this idea.

static methods are called

Call (private) privatization method

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

Extract the method to be executed asynchronously into a class. The principle is that when you extract the method to be executed asynchronously into a class, the class must be managed by Spring, and other Spring components must be injected when they need to be called. At this time, the agent class is actually injected.

In fact, our injection objects all assign member variables to the current Spring components from the Spring container. Because some classes use AOP annotations, what actually exists in the Spring container is its proxy object. Then we can get our own proxy object to call asynchronous methods through context.

@Controller
@RequestMapping("/app")
public class EmailController {
​
    //There are many ways to get ApplicationContext objects. This is the simplest way. Let's learn about other ways
    @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{
            //In this way, it does not work to call asynchronous methods under the same category
            //this.testAsyncTask();
            //Get own proxy object through context and call asynchronous method
            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!");
    }
​
}

Turn on cglib proxy and get the Spring proxy class manually, so as to call asynchronous methods under the same category. First, annotate the startup class with @ EnableAspectJAutoProxy(exposeProxy = true).

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 CGLIB proxy object;
        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

The use scenarios of the two are different. Asynchronous requests are used to solve the pressure caused by concurrent requests on the server, so as to improve the throughput of requests. Asynchronous calls are used to do some non mainline processes without real-time calculation and response tasks, such as synchronous logs to kafka for log analysis.

Asynchronous requests are always waiting for response and need to return results to the client; while asynchronous calls are often returned to the client immediately to respond and complete the whole request. As for the task of asynchronous calls, the background can run slowly, and the client will not mind.

Asynchronous request and asynchronous call are almost used here. If you have any problems, please point out more.

  • END -

Original link: https://www.cnblogs.com/baixianlong/p/10661591.html

Source network, only for learning, if there is infringement, contact delete.

I have compiled the interview questions and answers into PDF documents, as well as a set of learning materials, including Java virtual machine, spring framework, java thread, data structure, design pattern, etc., but not limited to this.

Focus on the official account [java circle] for information, as well as daily delivery of quality articles.

Tags: Programming Spring Java less Nginx

Posted on Fri, 08 May 2020 00:06:30 -0400 by kankohi