Spring Boot uses WebAsyncTask to return results asynchronously

  • 2021-01-22 05:11:48
  • OfStack

In Spring Boot (Spring MVC), requests are synchronous by default, and one thread is responsible for each request from the beginning to the end. In many cases, in order to improve the throughput, some operations need to be asynchronous. In addition to some time-consuming business logic, our query interface can also be asynchronous.

A request to the service is received by a thread using the web container, such as thread http-nio-8084-exec-1

We can use WebAsyncTask to distribute this request to a new thread to execute, http-nio-8084-exec-1 to receive processing of other requests. Once WebAsyncTask returns the data, it is called again and processed, asynchronously, to return the value to the requester.

The sample code is as follows:


@RequestMapping(value="/login", method = RequestMethod.GET)
public WebAsyncTask<ModelAndView> longTimeTask(){
  System.out.println("/login Is called  thread id is : " + Thread.currentThread().getName());
  Callable<ModelAndView> callable = new Callable<ModelAndView>() {
      public ModelAndView call() throws Exception {
        Thread.sleep(1000); / Simulate long - duration tasks 
        ModelAndView mav = new ModelAndView("login/index");
        System.out.println(" Execute successfully  thread id is : " + Thread.currentThread().getName());
        return mav;
      }
  };
  return new WebAsyncTask<ModelAndView>(callable);
}

You can see the output as follows:

/login is called thread id is: http-nio-8084-exec-1

thread id: MvcAsync1 successfully executed

The thread that precedes the execution of the business logic is not the same as the thread that specifically processes the business logic, which serves our purpose.

Then I did a concurrency test and found that I was constantly creating the ES43en1 thread, and I thought, isn't there a thread pool?

By reading the source code found that is true, WebAsyncManager is Spring MVC management async processing central class.

The default is to use ES53en, which creates 1 new thread per request


private AsyncTaskExecutor taskExecutor = new SimpleAsyncTaskExecutor(this.getClass().getSimpleName());

If the task specifies executor, the task specifies SimpleAsyncTaskExecutor. If not, the default SimpleAsyncTaskExecutor


AsyncTaskExecutor executor = webAsyncTask.getExecutor();
if (executor != null) {
  this.taskExecutor = executor;
}

We can configure the thread pool for async without having to specify it separately for each task

Through configurer. setTaskExecutor (threadPoolTaskExecutor ()); To specify the


import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.web.context.request.async.TimeoutCallableProcessingInterceptor;
import org.springframework.web.servlet.config.annotation.AsyncSupportConfigurer;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport;
@Configuration
public class WebMvcConfig extends WebMvcConfigurationSupport {
  @Override
  public void configureAsyncSupport(final AsyncSupportConfigurer configurer) {
    configurer.setDefaultTimeout(60 * 1000L);
    configurer.registerCallableInterceptors(timeoutInterceptor());
    configurer.setTaskExecutor(threadPoolTaskExecutor());
  }
  @Bean
  public TimeoutCallableProcessingInterceptor timeoutInterceptor() {
    return new TimeoutCallableProcessingInterceptor();
  }
  @Bean
  public ThreadPoolTaskExecutor threadPoolTaskExecutor() {
    ThreadPoolTaskExecutor t = new ThreadPoolTaskExecutor();
    t.setCorePoolSize(10);
    t.setMaxPoolSize(50);
    t.setThreadNamePrefix("YJH");
    return t;
  }
}

After the configuration, you can see that the output thread name starts with YJH, and it does not create a new thread

You can see the output as follows:


/login Is called  thread id is : http-nio-8084-exec-1
 Execute successfully  thread id is : YJH1

conclusion


Related articles: