Search code examples
spring-bootspring-mvcmicrometerspring-micrometer

What is the difference between getAsyncExecutor and threadPoolTaskScheduler in Spring MVC ASyncConfigurer?


Could someone please tell me the difference between getAsyncExecutor and threadPoolTaskScheduler, as explained in the spring async-instrumentation document? I created the config below, as mentioned in the above doc.

@Configuration
public class ServiceConfig {
  @Configuration(proxyBeanMethods = false)
  @EnableAsync
  static class AsyncConfig implements AsyncConfigurer {
    @Override
    public Executor getAsyncExecutor() {
      return ContextExecutorService.wrap(
          Executors.newCachedThreadPool(new ThreadFactoryBuilder().setNameFormat("my-sad-thread-%d").build()), ContextSnapshot::captureAll);
    }
  }


  @Bean(name = "taskExecutor", destroyMethod = "shutdown")
  ThreadPoolTaskScheduler threadPoolTaskScheduler() {
    final ThreadPoolTaskScheduler threadPoolTaskScheduler =
        new ThreadPoolTaskScheduler() {
          @Override
          protected ExecutorService initializeExecutor(
              final ThreadFactory threadFactory,
              final RejectedExecutionHandler rejectedExecutionHandler) {
            final ExecutorService executorService =
                super.initializeExecutor(threadFactory, rejectedExecutionHandler);
            return ContextExecutorService.wrap(executorService, ContextSnapshot::captureAll);
          }

          @Override
          public ScheduledExecutorService getScheduledExecutor() throws IllegalStateException {
            return ContextScheduledExecutorService.wrap(super.getScheduledExecutor());
          }
        };
    threadPoolTaskScheduler.setThreadNamePrefix("SpTaskExecutor1");
    threadPoolTaskScheduler.initialize();
    return threadPoolTaskScheduler;
  }

  @Bean
  public WebMvcConfigurer webMvcConfigurerConfigurer(final AsyncTaskExecutor taskExecutor) {
    return new WebMvcConfigurer() {
      @Override
      public void configureAsyncSupport(final AsyncSupportConfigurer configurer) {
        configurer.setTaskExecutor(taskExecutor);
        // Added timeout of 60 seconds for async api due to downloads api having 100K rows.
        configurer.setDefaultTimeout(60 * 1000L /* 60 sec*/);
      }
    };
  }
}

I have a simple Hello World controller.

public class HelloWorldController {
    @GetMapping("/hello")
    public ResponseEntity<StreamingResponseBody> sendGreetings() {
        logMsg("Hello Outside Body Thread");
        return ResponseEntity.ok()
            .body(
                outputStream ->
                {
                    logMsg("Hello Inside Body Thread");
                    outputStream.write("Hello World".getBytes());
                }
            );
    }

    @GetMapping("/hello1")
    public ResponseEntity<String> sendGreetings1() {
        logMsg("Hello1");
        return ResponseEntity.ok().body("Hello World");
    }

    private void logMsg(final String msg) {
        log.info("Thread: {} {}", Thread.currentThread().getName(), msg);
    }
}

I am observing logs like the one below.

2024-09-02T16:04:56.072+05:30  INFO 68014 --- [nio-8080-exec-5] c.e.h.controller.HelloWorldController    : Thread: http-nio-8080-exec-5 Hello Outside Body Thread
2024-09-02T16:04:56.072+05:30  INFO 68014 --- [pTaskExecutor11] c.e.h.controller.HelloWorldController    : Thread: SpTaskExecutor11 Hello Inside Body Thread

2024-09-02T12:35:17.273+05:30  INFO 21749 --- [nio-8080-exec-4] c.e.h.controller.HelloWorldController    : Thread: http-nio-8080-exec-4 Hello1

threadPoolTaskScheduler is being used. Where is getAsyncExecutor expected to be used?

The sample code is placed here


Solution

  • getAsyncExecutor() - is used when you invoke methods annotated with @Async.

    ThreadPoolTaskScheduler - is used for scheduling tasks (like @Scheduled annotation) or for asynchronous request handling like with StreamingResponseBody