5

I am working with a Spring boot application. I have a rest controller that returns Callable.

@GetMapping("/fb-roles")
@Timed
public Callable<List<FbRole>> getAllFbRoles() {
    log.debug("REST request to get all FbRoles");
    return (() -> { return fbRoleRepository.findAll(); });
}

A ThreadPoolTaskExecutor is configures as follow:

@Configuration
@EnableAsync
@EnableScheduling
public class AsyncConfiguration implements AsyncConfigurer {

private final Logger log = LoggerFactory.getLogger(AsyncConfiguration.class);

private final JHipsterProperties jHipsterProperties;

public AsyncConfiguration(JHipsterProperties jHipsterProperties) {
    this.jHipsterProperties = jHipsterProperties;
}

@Override
@Bean(name = "taskExecutor")
public Executor getAsyncExecutor() {
    log.debug("Creating Async Task Executor");
    ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
    executor.setCorePoolSize(jHipsterProperties.getAsync().getCorePoolSize());
    executor.setMaxPoolSize(jHipsterProperties.getAsync().getMaxPoolSize());
    executor.setQueueCapacity(jHipsterProperties.getAsync().getQueueCapacity());
    executor.setThreadNamePrefix("fb-quiz-Executor-");
    return new ExceptionHandlingAsyncTaskExecutor(executor);
}

@Override
public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
    return new SimpleAsyncUncaughtExceptionHandler();
}

}

2018-09-19 00:43:58.434 WARN 10104 --- [ XNIO-2 task-28] o.s.w.c.request.async.WebAsyncManager : !!! An Executor is required to handle java.util.concurrent.Callable return values. Please, configure a TaskExecutor in the MVC config under "async support". The SimpleAsyncTaskExecutor currently in use is not suitable under load.

But while accessing the api server is producing the following warning

Arif Rabbani
  • 131
  • 1
  • 2
  • 12

5 Answers5

9

Spring configuration is a bit confusing in this respect, since it requires separate configuration for MVC Async support, i.e. using a Controller handler method that returns a Callable, and for any Spring bean method annotated with @Async. To configure both of it correctly you can apply something like the configuration below keeping in mind that the AsyncTaskExecutor config might need amending:

@Configuration
@EnableAsync
public class AsyncConfig  implements AsyncConfigurer {

    @Bean
    protected WebMvcConfigurer webMvcConfigurer() {
        return new WebMvcConfigurerAdapter() {
            @Override
            public void configureAsyncSupport(AsyncSupportConfigurer configurer) {
                configurer.setTaskExecutor(getTaskExecutor());
            }
        };
    }

    @Bean
    protected ConcurrentTaskExecutor getTaskExecutor() {
        return new ConcurrentTaskExecutor(Executors.newFixedThreadPool(5));
    }
}

On a side note, you might feel tempted to simply annotate your Controller handler method with @Async. This will only have the desired effect - freeing up web server threads - on fire and forget operations (this observation is based on Spring Boot 2.1.2, possibly they will address this in the future). If you want to leverage the power of Servlet 3.0 Asynchronous Processing, you really have to use Callables and configure them with a WebMvcConfigurer.

Fritz Duchardt
  • 11,026
  • 4
  • 41
  • 60
2

Given the warning and your Callable method.

Seems like Spring is not able to identify the Executor bean that you have just set up in your configuration class.

You might need to annotate your method and specify the executor bean name, so

@GetMapping("/fb-roles")
@Timed
@Async("taskExecutor")
public Callable<List<FbRole>> getAllFbRoles() {
    log.debug("REST request to get all FbRoles");
    return (() -> { return fbRoleRepository.findAll(); });
}

Hope this helps

Guide can be found here: https://www.baeldung.com/spring-async

JWiryo
  • 371
  • 4
  • 11
  • i configured it. But now the api returns without any result – Arif Rabbani Sep 19 '18 at 14:02
  • Can you try removing the `@Timed` annotation, i think combining both of them forces the API to stop before the asynchronous results come Update me if it makes any difference If it still doesn't work, try removing Callable and the lambda expression first and return List directly. That way we'll know if the Async is actually working Hope this helps – JWiryo Sep 20 '18 at 08:13
  • Also tried this way: `@GetMapping("/fb-roles") @Async("taskExecutor") public List getAllFbRoles() { log.debug("REST request to get all FbRoles"); return fbRoleRepository.findAll(); }` – Arif Rabbani Sep 20 '18 at 12:18
  • @ArifRabbani Any error message? Or simply return null result? You are sure the repository implementation is correct and data is present? – JWiryo Sep 21 '18 at 03:00
  • Simply returning null. I've checked the repository, it's fine. Data is also present. When i use it as synchronous, data is successfully returned – Arif Rabbani Sep 22 '18 at 15:42
  • I checked some other threads and there may be a need to return the results in `Future` or `AsyncResult` Like the one mentioned here: https://stackoverflow.com/questions/29181057/how-to-check-that-async-call-completed-in-spring So it becomes: `public Future> getAllFbRoles()` – JWiryo Sep 24 '18 at 06:33
  • @ArifRabbani Have you ever succeeded in getting a result? – jokster Oct 02 '18 at 18:17
  • For me adding the `@Async(...)` to the Controller method and giving the Task-Executor Bean a corresponding name helped. – Tarator Nov 15 '18 at 09:49
1

From your warning "Please, configure a TaskExecutor in the MVC config under "async support". The SimpleAsyncTaskExecutor currently in use is not suitable under load."

I wonder if you use the spring mvc or not?

With MVC, a few below links might help:

Configuring mvc async task executor in springboot application

Spring Boot - Any shortcuts for setting TaskExecutor?

Huy Nguyen
  • 1,931
  • 1
  • 11
  • 11
1

I had combined mvc configuration (xml + annotations) and for me the following config helped to fix that warning:

mvc-servlet.xml:

<mvc:annotation-driven> 
  <mvc:async-support default-timeout="30000" task-executor="taskExecutor"
</mvc:annotation-driven>

AsyncConfig.java

@Configuration
@EnableAsync
public class AsyncConfig implements AsyncConfigurer {
  @Bean
  public AsyncTaskExecutor taskExecutor() {
    return new ConcurrentTaskExecutor(Executors.newCachedThreadPool());
  }
}
Alex Rewa
  • 319
  • 4
  • 11
1

You need to configure an task executor like described by Fritz already. Sadly its solution uses now deprecated WebMvcConfigurerAdapter.

import org.springframework.context.annotation.Configuration;
import org.springframework.core.task.AsyncTaskExecutor;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.web.servlet.config.annotation.AsyncSupportConfigurer;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

@Configuration
public class AsyncWebMvcConfiguration implements WebMvcConfigurer{
    @Override
    public void configureAsyncSupport(AsyncSupportConfigurer configurer) {
        configurer.setTaskExecutor(asyncExecutor());
    }

    private AsyncTaskExecutor asyncExecutor() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        executor.initialize();
        return executor;
    }
}

Enhancement for solution from Fritz Duchardt and derived from: https://docs.sentry.io/platforms/java/guides/spring-boot/async/

Maik
  • 310
  • 1
  • 11