Spring Boot Asynchronous Request Processing Task Executor Configuration - spring-boot

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.

Related

Spring Async get current Executor

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.

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.

Limit thread with partitioner Spring Batch

My Spring batch application consumes too many resources (+4 go Ram).
When I look at the jvm, the application creates 10 threads.
I use the partitioner to process file by file without scheduler
jobExecutionListener is used to stop the batch at the end of execution
#Bean
public Job mainJob() throws IOException {
SimpleJobBuilder mainJob = this.jobBuilderFactory.get("mainJob")
.start(previousStep())
.next(partitionStep())
.next(finalStep())
.listener(jobExecutionListener(taskExecutor()));;
return mainJob.build();
}
#Bean
public Step partitionStep() throws IOException {
Step mainStep = stepBuilderFactory.get("mainStep")
.<InOut, InOut>chunk(1)
.reader(ResourceReader())
.processor(processor())
.writer(itemWriter())
.build();
return this.stepBuilderFactory.get("partitionStep")
.partitioner(mainStep)
.partitioner("mainStep", partitioner())
.build();
}
#Bean(name = "taskExecutor")
public ThreadPoolTaskExecutor taskExecutor() {
ThreadPoolTaskExecutor taskExecutor = new ThreadPoolTaskExecutor();
taskExecutor.setCorePoolSize(1);
taskExecutor.setMaxPoolSize(1);
taskExecutor.setQueueCapacity(1);
taskExecutor.setThreadNamePrefix("MyBatch-");
taskExecutor.initialize();
return taskExecutor;
}
//This jobExecutionListener stop the batch
#Bean
public JobExecutionListener jobExecutionListener(#Qualifier("taskExecutor")
ThreadPoolTaskExecutor executor) {
return new JobExecutionListener() {
private ThreadPoolTaskExecutor taskExecutor = executor;
#Override
public void beforeJob(JobExecution jobExecution) {
}
#Override
public void afterJob(JobExecution jobExecution) {
taskExecutor.shutdown();
System.exit(0);
}
};
}
#Bean
public Partitioner partitioner() {
MultiResourcePartitioner partitioner = new MultiResourcePartitioner();
ResourcePatternResolver patternResolver = new
PathMatchingResourcePatternResolver();
try {
partitioner.setResources(patternResolver.getResources(FILE +
configProperties.getIn()+ "/*.xml"));
} catch (IOException e) {
throw new RuntimeException("I/O problems when resolving the input file pattern.",e);
}
partitioner.setKeyName("file");
return partitioner;
}
How can I apply my application in monothread ? The taskexecutor doesn't work.
Your app creates 10 threads but those are not necessarily Spring Batch threads. According to your config, only one thread with prefix MyBatch- should be created.
Moreover, you declared a task executor as a bean but you did not set it on the partitioned step. Your partitionStep should be something like:
#Bean
public Step partitionStep() throws IOException {
Step mainStep = stepBuilderFactory.get("mainStep")
.<InOut, InOut>chunk(1)
.reader(ResourceReader())
.processor(processor())
.writer(itemWriter())
.build();
return this.stepBuilderFactory.get("partitionStep")
.step(mainStep) // instead of .partitioner(mainStep)
.partitioner("mainStep", partitioner())
.taskExecutor(taskExecutor())
.build();
}
How can I apply my application in monothread ? The taskexecutor doesn't work.
After setting the task executor on the partitioned step, you should see this step being executed by the sole thread as defined in your ThreadPoolTaskExecutor. However, I don't see the benefit of using a single thread for a partitioned step, because the usual goal for such a setup is to process partitions in parallel (either locally with multiple threads or remotely with multiple worker JVMs).
As a side note, it's good that you shutdown the task executor with a Job listener in afterJob, but don't System.exit. You need to let the JVM shutdown gracefully.
Hope this helps.

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/

Issues with Spring Batch

Hi I have been working in Spring batch recently and need some help.
1) I want to run my Job using multiple threads, hence I have used TaskExecutor as below,
#Bean
public TaskExecutor taskExecutor() {
SimpleAsyncTaskExecutor taskExecutor = new SimpleAsyncTaskExecutor();
taskExecutor.setConcurrencyLimit(4);
return taskExecutor;
}
#Bean
public Step myStep() {
return stepBuilderFactory.get("myStep")
.<MyEntity,AnotherEntity> chunk(1)
.reader(reader())
.processor(processor())
.writer(writer())
.taskExecutor(taskExecutor())
.throttleLimit(4)
.build();
}
but, while executing in can see below line in console.
o.s.b.c.l.support.SimpleJobLauncher : No TaskExecutor has been set, defaulting to synchronous executor.
What does this mean? However, while debugging I can see four SimpleAsyncExecutor threads running. Can someone shed some light on this?
2) I don't want to run my Batch application with the metadata tables that spring batch creates. I have tried adding spring.batch.initialize-schema=never. But it didn't work. I also saw some way to do this by using ResourcelessTransactionManager, MapJobRepositoryFactoryBean. But I have to make some database transactions for my job. So will it be alright if I use this?
Also I was able to do this by extending DefaultBatchConfigurer and overriding:
#Override
public void setDataSource(DataSource dataSource) {
// override to do not set datasource even if a datasource exist.
// initialize will use a Map based JobRepository (instead of database)
}
Please guide me further. Thanks.
Update:
My full configuration class here.
#EnableBatchProcessing
#EnableScheduling
#Configuration
public class MyBatchConfiguration{
#Autowired
public JobBuilderFactory jobBuilderFactory;
#Autowired
public StepBuilderFactory stepBuilderFactory;
#Autowired
public DataSource dataSource;
/* #Override
public void setDataSource(DataSource dataSource) {
// override to do not set datasource even if a datasource exist.
// initialize will use a Map based JobRepository (instead of database)
}*/
#Bean
public Step myStep() {
return stepBuilderFactory.get("myStep")
.<MyEntity,AnotherEntity> chunk(1)
.reader(reader())
.processor(processor())
.writer(writer())
.taskExecutor(executor())
.throttleLimit(4)
.build();
}
#Bean
public Job myJob() {
return jobBuilderFactory.get("myJob")
.incrementer(new RunIdIncrementer())
.listener(listener())
.flow(myStep())
.end()
.build();
}
#Bean
public MyJobListener myJobListener()
{
return new MyJobListener();
}
#Bean
public ItemReader<MyEntity> reader()
{
return new MyReader();
}
#Bean
public ItemWriter<? super AnotherEntity> writer()
{
return new MyWriter();
}
#Bean
public ItemProcessor<MyEntity,AnotherEntity> processor()
{
return new MyProcessor();
}
#Bean
public TaskExecutor taskExecutor() {
SimpleAsyncTaskExecutor taskExecutor = new SimpleAsyncTaskExecutor();
taskExecutor.setConcurrencyLimit(4);
return taskExecutor;
}}
In the future, please break this up into two independent questions. That being said, let me shed some light on both questions.
SimpleJobLauncher : No TaskExecutor has been set, defaulting to synchronous executor.
Your configuration is configuring myStep to use your TaskExecutor. What that does is it causes Spring Batch to execute each chunk in it's own thread (based on the parameters of the TaskExecutor). The log message you are seeing has nothing to do with that behavior. It has to do with launching your job. By default, the SimpleJobLauncher will launch the job on the same thread it is running on, thereby blocking that thread. You can inject a TaskExecutor into the SimpleJobLauncher which will cause the job to be executed on a different thread from the JobLauncher itself. These are two separate uses of multiple threads by the framework.
I don't want to run my Batch application with the metadata tables that spring batch creates
The short answer here is to just use an in memory database like HSQLDB or H2 for your metadata tables. This provides a production grade data store (so that concurrency is handled correctly) without actually persisting the data. If you use the ResourcelessTransactionManager, you are effectively turning transactions off (a bad idea if you're using a database in any capacity) because that TransactionManager doesn't actually do anything (it's a no-op implementation).

Resources