Asynchronous REST API generating warning
Asked Answered
K

5

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

Karachi answered 19/9, 2018 at 3:49 Comment(0)
D
10

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.

Delora answered 3/2, 2019 at 11:23 Comment(2)
This will create 2 instances ConcurrentTaskExecutorWallet
WebMvcConfigurerAdapter is deprecated in SpringBoot 2.7.1 / Spring 5. But it still works.Mouseear
V
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

Vietnamese answered 19/9, 2018 at 5:51 Comment(8)
i configured it. But now the api returns without any resultKarachi
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 helpsVietnamese
Also tried this way: @GetMapping("/fb-roles") @Async("taskExecutor") public List<FbRole> getAllFbRoles() { log.debug("REST request to get all FbRoles"); return fbRoleRepository.findAll(); }Karachi
@ArifRabbani Any error message? Or simply return null result? You are sure the repository implementation is correct and data is present?Vietnamese
Simply returning null. I've checked the repository, it's fine. Data is also present. When i use it as synchronous, data is successfully returnedKarachi
I checked some other threads and there may be a need to return the results in Future or AsyncResult Like the one mentioned here: #29181557 So it becomes: public Future<List<FbRole>> getAllFbRoles()Vietnamese
@ArifRabbani Have you ever succeeded in getting a result?Maes
For me adding the @Async(...) to the Controller method and giving the Task-Executor Bean a corresponding name helped.Renelle
M
2

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/

Mouseear answered 1/12, 2022 at 15:5 Comment(0)
P
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?

Patti answered 19/9, 2018 at 4:44 Comment(1)
I'm using JHipster generatorKarachi
I
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());
  }
}
Impropriety answered 27/12, 2019 at 10:26 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.