Spring Async get current Executor - spring-boot

I am using Spring Async by implementing AsyncConfigurer and overriding the getAsyncExecutor to define my Executor.
Now I would like to expose an endpoint, to return the current queue size, number of threads ...etc, of the Executor that is used by Async.
But I could not find a way to find or autowire the current executor that is used by Async.
I was thinking I can define a bean, that will be both used by the getAsyncExecutor method, and my reporting service.
But I was wondering if there is an easier/more appropriate way I can interact with async to get the current Executor.
My current config:
#Configuration
#EnableAsync
public class AsyncConfiguration implements AsyncConfigurer {
#Override
public Executor getAsyncExecutor() {
final ThreadPoolTaskExecutor threadPoolTaskExecutor = new ThreadPoolTaskExecutor();
threadPoolTaskExecutor.setThreadNamePrefix("async-thread-");
threadPoolTaskExecutor.setCorePoolSize(2);
threadPoolTaskExecutor.setMaxPoolSize(2);
threadPoolTaskExecutor.setQueueCapacity(100);
threadPoolTaskExecutor.initialize();
return threadPoolTaskExecutor;
}
}

You haven't registered a bean for the ThreadPoolTaskExector.
#Configuration
#EnableAsync
public class AsyncConfiguration implements AsyncConfigurer {
#Override
public Executor getAsyncExecutor() {
return taskExecutor();
}
#Bean
public ThreadPoolTaskExecutor taskExecutor() {
ThreadPoolTaskExecutor threadPoolTaskExecutor = new ThreadPoolTaskExecutor();
threadPoolTaskExecutor.setThreadNamePrefix("async-thread-");
threadPoolTaskExecutor.setCorePoolSize(2);
threadPoolTaskExecutor.setMaxPoolSize(2);
threadPoolTaskExecutor.setQueueCapacity(100);
return threadPoolTaskExecutor;
}
}
However Spring Boot 2.1 already pre-configures a TaskExecutor for you which you can configure through properties. You can then remove all config and only use #EnableAsync in your configuration.
spring.task.execution.pool.core-size=2
spring.task.execution.pool.max-size=2
spring.task.execution.pool.queue-capacity=100
spring.task.exection.thread-name-prefix=async-thread-
This configuration, together with a single #EnableAsync will achieve the same, without additional configuration.
With either configuration, you can now use #Autowired to get the instance in your service.

Related

How to fix - Executor is required to handle java.util.concurrent.Callable return values

I have a controller in Spring Boot/Spring Data Rest where my handler downloads a file like this
#RequestMapping(method = GET, value = "/orderAttachments/{id}/download")
#ResponseStatus(HttpStatus.OK)
public ResponseEntity<StreamingResponseBody> downloadAttachment(#PathVariable("id") Long attachmentID, HttpServletRequest request)
throws IOException {
InputStream inputStream = fileManager.getInputStream(orderAttachment);
StreamingResponseBody responseBody = outputStream -> {
int numberOfBytesToWrite;
byte[] data = new byte[1024];
while ((numberOfBytesToWrite = inputStream.read(data, 0, data.length)) != -1) {
outputStream.write(data, 0, numberOfBytesToWrite);
}
inputStream.close();
};
return ResponseEntity
.ok()
.contentLength(orderAttachment.getFileSize())
.header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"" + orderAttachment.getFileName()+ "\"")
.contentType(MediaType.APPLICATION_OCTET_STREAM)
.body(responseBody);
}
I got this error in the console
!!!
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.
-------------------------------
Request URI: '/api/v1/orderAttachments/163/download'
!!!
But everything works, I can download the file from calling the API
For starters it is a warning and not an error. If it was an error your application wouldn't start or work. It is a warning telling you that the default used SimpleAsyncTaskExecutor shouldn't be used in production.
The SimpleAsyncTaskExecutor creates a new Thread when something needs to be processed asynchronously. Each thread takes up 1MB of memory by default (and some processing power). Now imagine someone issuing 100000 calls to this API. This would also mean 100000 threads (times the memory and each a little cpu power). It would cripple your application and maybe even kill it.
NOTE: If you are on Spring Boot 2.1.x or higher a default TaskExecutor will be configured and used for you. However if you have something in your configuration that disables the auto-configuration (like an #EnableWebMvc on an #Configuration class for instance). To configure that executor (with threads etc) check the Spring Boot Reference Guide.
You can also manually create a TaskExecutor and associate that with Spring MVC.
#Configuration
public class WebMvcConfig implements WebMvcConfigurer {
#Bean
public ThreadPoolTaskExecutor mvcTaskExecutor() {
ThreadPoolTaskExecutor taskExecutor = new ThreadPoolTaskExecutor();
taskExecutor.setCorePoolSize(10);
taskExecutor.setMaxPoolSize(10);
return taskExecutor;
}
public void configureAsyncSupport(AsyncSupportConfigurer configurer) {
configurer.setTaskExecutor(mvcTaskExecutor());
}
}
Now this dedicated TaskExecutor will be used for the async handling in Spring MVC. However as mentioned this should already be pre-configured by Spring Boot if you haven't disabled auto-configuration (maybe even by accident!).
It is just a warning (that is why you still are able to download requested file) and Spring's suggestion to define your own TaskExecutor rather than using the SimpleAsyncTaskExecutor which, as the message states, is not suitable under load.
SimpleAsyncTaskExecutor fires up a new Thread for each task and does not reuse them. If you haven't configured limit on concurrent threads, it is by default unlimited.
#Configuration
class AsyncConfiguration implements AsyncConfigurer {
#Bean
protected WebMvcConfigurer webMvcConfigurer() {
return new WebMvcConfigurerAdapter() {
#Override
public void configureAsyncSupport(AsyncSupportConfigurer configurer) {
configurer.setTaskExecutor([your task executor]);
}
};
}
}
Use the above configuration to define your own TaskExecutor that suits your needs and get rid of the warning.
Spring boot in 2.1.0 provides auto configuration for task executors and uses for #EnableAsync and Spring MVC Async support.
There is no task executor bean / webMvcConfigurer configuration is needed from application. If you have one please remove it and you should be good.
If you like to control the values you can adjust using applicaion properties/yml file with spring.task.execution.*. Full listing can be found here
More details here and here
For me, the following did the trick:
#Configuration
public class WebMvcConfiguration implements WebMvcConfigurer {
#Override
public void configureAsyncSupport(AsyncSupportConfigurer configurer) {
final ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(1);
executor.setMaxPoolSize(10);
executor.setThreadNamePrefix("mvc-task-executor-");
executor.initialize();
configurer.setTaskExecutor(executor);
}
}
By this means a ThreadPoolTaskExecutor is specified for the MVC context.
Make sure your application picks up the #Configuration class! If your main class is annotated with #SpringBootApplication, this should happen automatically.
Also, make sure to configure your ThreadPoolTaskExecutor appropriately to your needs.

Asynchronous REST API generating warning

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
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.
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
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?
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());
}
}
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/

Spring ThreadPoolTaskExecutor Autowiring Different Instance

I'm using Spring Boot and have configured a ThreadPoolTaskExecutor as follows:
#Data
#Configuration
public class WorkflowThreadConfig {
#Value("${threadConfig.corePoolSize}")
private Integer corePoolSize;
#Value("${threadConfig.maxPoolSize}")
private Integer maxPoolSize;
#Bean
#Qualifier("threadPoolTaskExecutor")
public TaskExecutor threadPoolTaskExecutor() {
ThreadPoolTaskExecutor threadPoolTaskExecutor = new ThreadPoolTaskExecutor();
threadPoolTaskExecutor.setCorePoolSize(corePoolSize);
threadPoolTaskExecutor.setMaxPoolSize(maxPoolSize);
log.debug("threadPoolTaskExecutor maxPoolSize is : " + threadPoolTaskExecutor.getMaxPoolSize());
threadPoolTaskExecutor.setThreadNamePrefix("workflow_thread_");
threadPoolTaskExecutor.initialize();
return threadPoolTaskExecutor;
}
}
When I #Autowire the #Bean into another class using a #Qualifier, I see the default number of Threads in the max pool size instead of the number I provided (10) from my configuration even after commenting out most of my code and just using a #PostConstruct:
#Component
public class WorkflowTaskScheduler {
//#Autowired
//private WorkflowThreadManager workflowThreadManager;
#Autowired
#Qualifier("threadPoolTaskExecutor")
private TaskExecutor taskExecutor;
#PostConstruct
public void workflowTaskScheduler(){
ThreadPoolTaskExecutor threadPool = (ThreadPoolTaskExecutor) taskExecutor;
log.debug(" Max Thread Pool count is : " + threadPool.getMaxPoolSize());
}
}
Logs:
SpanId="">threadPoolTaskExecutor maxPoolSize is : 10</L_MSG>
SpanId=""> Max Thread Pool count is : 2147483647</L_MSG>
Another interesting point to make is when i remove the #Qualifier annotation from both the threadPoolTaskExecutor #Bean and the #Autowired TaskExecutor I get the following error:
Field taskExecutor in com.package.WorkflowTaskScheduler required a single bean, but 2 were found:
- threadPoolTaskExecutor: defined by method 'threadPoolTaskExecutor' in class path resource [com/package/WorkflowThreadConfig.class]
- taskScheduler: defined in null
Two options:
1. Use a different qualifier then the camelcase. myThreadPoolTaskExecutor
Use #primary on your ThreadPoolTaskExecutor. SO it will be the one getting bundled by default

Spring Boot Asynchronous Request Processing Task Executor Configuration

From this doc I have learned that, now we can return Callable<T> from any action method. And spring will execute this action in a separate thread with the help of a TaskExecutor. This blog only says that this TaskExecutor is configurable. But I did not find a way to configure this TaskExecutor in spring boot application. Can anyone help me?
My another question is should I worry about the configuration of this TaskExecutor like threadpool size, queue size etc?
As pkoli asked, here is my Main class
#SpringBootApplication
public class MyWebApiApplication extends SpringBootServletInitializer {
public static void main(String[] args) {
SpringApplication.run(MyWebApiApplication.class, args);
}
#Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
return application.sources(MyWebApiApplication.class);
}
#Bean
public Executor asyncExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(10);
executor.setMaxPoolSize(10);
executor.setQueueCapacity(100);
executor.setThreadNamePrefix("MyThread-");
executor.initialize();
return executor;
}
}
Finally found answer here
To use other implementation of TaskExecutor we can extend our configuration class from WebMvcConfigurerAdapter or we can use it as bean. For example in a boot application:
#SpringBootApplication
public class AsyncConfigExample{
#Bean
WebMvcConfigurer configurer(){
return new WebMvcConfigurerAdapter(){
#Override
public void configureAsyncSupport (AsyncSupportConfigurer configurer) {
ThreadPoolTaskExecutor t = new ThreadPoolTaskExecutor();
t.setCorePoolSize(10);
t.setMaxPoolSize(100);
t.setQueueCapacity(50);
t.setAllowCoreThreadTimeOut(true);
t.setKeepAliveSeconds(120);
t.initialize();
configurer.setTaskExecutor(t);
}
};
}
public static void main (String[] args) {
SpringApplication.run(AsyncConfigExample.class, args);
}
}
To create a task executor simply create a bean as follows with the configuration that suits your requirement.
#Bean
public Executor asyncExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(1);
executor.setMaxPoolSize(1);
executor.setQueueCapacity(100);
executor.setThreadNamePrefix("MyThread-");
executor.initialize();
return executor;
}
Regarding the second part of your question, Yes the configuration needs to be provided keeping in mind your application.
Here's an explanation from the javadoc.
When a new task is submitted in method execute(java.lang.Runnable), and fewer than corePoolSize threads are running, a new thread is created to handle the request, even if other worker threads are idle. If there are more than corePoolSize but less than maximumPoolSize threads running, a new thread will be created only if the queue is full.

#Resource becomes null when Java Config implements AsyncConfigurer

I have a very strange problem. I'm using Spring 3.2.4 and have a Spring Java Config class which is part of a larger mix of Java and XML configuration files.
The file uses 6 resources defined in other config files to construct an #Async bean. When I add "implements AsyncConfigurer" to the file, some of the #Resource fields appear as null, but when I leave the implementation off, they are populated. This is confusing, since the AsyncConfigurer is just supposed be used for configuring your asynchronous executor, it shouldn't do anything funky like cause your configuration to be loaded asynchronously.
When I set a debugger on the construction method, I can see that the beans are in fact null. It seems to be a race condition of some kind, because out of the 6 beans, the 4th one was once null, and then populated the next time.
My file looks like:
public class UserBackgroundProcessorConfiguration implements AsyncConfigurer {
#Resource(name="bean1")
MyBean bean1;
// in reality there are 6 #Resources defined...
#Resource(name="bean6")
MyBean bean6;
#Bean(name="backgroundProcessor")
public BackgroundProcessor getBackgroundProcessor() {
BackgroundProcessor backgroundProcess = new BackgroundProcessor();
backgroundProcess.setBean1(bean1);
// beans 1-3 are always populated
// bean 4 seems to sometimes be populated, sometimes null
// beans 5&6 are always null
backgroundProcess.setBean6(bean6);
return backgroundProcess;
}
#Override
#Bean(name="executor")
public Executor getAsyncExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(10);
executor.setMaxPoolSize(40);
executor.setQueueCapacity(25);
executor.setThreadNamePrefix("BackgroundProcessor-");
return executor;
}
}
Again, when I remove "implements AsyncConfigurer" and comment out getAsyncExecutor, the problem goes away.
According to the documentation
Interface to be implemented by #Configuration classes annotated with #EnableAsync that wish to customize the Executor instance used when processing async method invocations.
So I don't see how that cause the behavior I am seeing.
It appears that your backgroundProcessor bean is depending on your 6 resource beans.
Try annotating the method signature with:
#Bean(name="backgroundProcessor")
#DependsOn("bean1","bean2","bean3","bean4","bean5","bean6")
public BackgroundProcessor getBackgroundProcessor() {..}
I solved this problem by removing the #Resource annotations from the Java configuration file:
public class UserBackgroundProcessorConfiguration implements AsyncConfigurer {
#Bean(name="backgroundProcessor")
public BackgroundProcessor getBackgroundProcessor() {
BackgroundProcessor backgroundProcess = new BackgroundProcessor();
return backgroundProcess;
}
#Override
#Bean(name="executor")
public Executor getAsyncExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(10);
executor.setMaxPoolSize(40);
executor.setQueueCapacity(25);
executor.setThreadNamePrefix("BackgroundProcessor-");
return executor;
}
}
And then adding #Resource annotations to the BackgroundProcessor class:
public class BackgroundProcessor {
#Resource private MyBean bean1;
// 4 more
#Resource private MyBean bean6;
}
For some reason this works fine. I don't like this as much, because I would prefer my classes not have a dependency on the IOC annotations, but I'll go with this solution for now.

Resources