Spring ThreadPoolTaskExecutor with fixed delay? - java

I have a Job which is scheduled to run every hour which uses Spring ThreadPoolTaskExecutor bean to fire simultaneous calls (close to 100 calls every hour) to external API.
#Bean
public TaskExecutor getExecutor() {
ThreadPoolTaskExecutor threadPoolTaskExecutor = new ThreadPoolTaskExecutor();
threadPoolTaskExecutor.setCorePoolSize(10);
threadPoolTaskExecutor.setMaxPoolSize(20);
return threadPoolTaskExecutor;
}
Now, the external API has throttled the number of requests and allows one request per 30 secs. I will have to wait 30 secs before making each call.
In this case, I see use of ThreadPoolTaskExecutor is no longer helpful. Will ThreadPoolTaskScheduler with Fixed Delay configuration work?
What is the best way to handle such type of API throttling? Please help
I'm on Java 8, Spring Boot 2.0.1 if that helps

You could do the following.
Configure the TaskExecutor. You have already done this.
Write a method that you want to invoke in a scheduled fashion and annotate with #Scheduled.
#Scheduled(fixedDelay = 3600000)
void doSomething() {
}
The code above will trigger every 1 hour.

Related

Spring Boot Async Programming

I'm new in software. I'm working to understand async programming in Spring Boot. As seen above, I set thread pool size 2. When I requested same url three times one after another. My two requests are working asynchronously. Third one is waiting. This is ok. But when I don't use the asynchronous feature (neither #async annotation nor threadpool), it still performs transactions asynchronously, as before. So I'm confused. Spring Boot rest controller behaves asynchronously by default? Why we use #async in Spring Boot? Or do I misunderstand that?
#Service
public class TenantService {
#Autowired
private TenantRepository tenantRepository;
#Async("threadPoolTaskExecutor")
public Future<List<Tenant>> getAllTenants() {
System.out.println("Execute method asynchronously - "
+ Thread.currentThread().getName());
try {
List<Tenant> allTenants = tenantRepository.findAll();
Thread.sleep(5000);
return new AsyncResult<>(allTenants);
} catch (InterruptedException e) {
//
}
return null;
}
}
#Configuration
#EnableAsync
public class AsyncConfig {
#Bean(name = "threadPoolTaskExecutor")
public Executor threadPoolTaskExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(2);
executor.setMaxPoolSize(2);
executor.setQueueCapacity(100);
executor.setThreadNamePrefix("AsynchThread-");
executor.initialize();
return executor;
}
#Bean(name = "threadPoolTaskExecutor2")
public Executor threadPoolTaskExecutor2() {
return new ThreadPoolTaskExecutor();
}
}
I'm assuming you are using the default embedded Tomcat from Spring Boot. If that's the case, then you are not misunderstanding. Tomcat will indeed work asynchronously by default, meaning it will start a new thread for every request (see this for on that).
The #Async annotation does not aim to replace the functionality that Tomcat provides in this case. Instead, that annotation allows executing any method of a bean in a separate thread. For your particular use case, it might be enough to let Tomcat start a new thread for every request, but sometimes you might want to parallelize work further.
An example on when you would probably want to use both is when a request must trigger some heavy computation, but the response does not depend on it. By using the #Async annotation, you can start the heavy computation on another thread, and let the request finish sooner (effectively allowing the server to handle other requests while the heavy computation runs independently on another thread).

What are impact to my spring boot application if I have task executor

I already have the configuration to config for min and max threads for my spring boot application
server.tomcat.threads.min=20
server.tomcat.threads.max=50
What are impact to my spring boot application if I have task executor in my application?
#Configuration
public class AsyncConfiguration {
#Bean("myExecutor")
public TaskExecutor getAsyncExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(20);
executor.setMaxPoolSize(1000);
executor.setWaitForTasksToCompleteOnShutdown(true);
executor.setThreadNamePrefix("Async-");
return executor;
} }
Those are two different thread pools:
server.tomcat.threads.* defines the request thread pool (knowing that the Servlet API uses one thread per request)
myExecutor is just another pool that you could use for async operations, for instance via #EnableAsync/#Async:
By default, Spring will be searching for an associated thread pool definition: either a unique TaskExecutor bean in the context, or an Executor bean named "taskExecutor" otherwise. If neither of the two is resolvable, a SimpleAsyncTaskExecutor will be used to process async method invocations.
See also https://stackoverflow.com/a/65185737/1225328 for more details about using thread pools with Spring MVC/WebFlux.
So, to answer your actual question: both configurations don't interfere with each other :)
From my understanding
server.tomcat.threads.* : it tomcat thread pool which using for requests.
It mean If I have 2000 request coming, it only create 50 threads as my configuration.
myExcutor: it is using for Asynchronous.
In case I want to handle for logging error in the background by using #Async annotation, If all 2000 request got fail, it create max 1000 threads. So tomcat will manage 50 threads as main threads and Spring Container will manage 1000 threads. But If I not use a custom task executor, it will use default from Spring and create 2000 new threads. In that case, my application will be slower

using #Scheduled and #Async together?

If i want a method to repeat async, can i use #Scheduled and #Async together ?
#Async
#Scheduled(fixedDelay = x)
public void doSomethingEveryXMinuteAsync() {
// action
}
or is there another standard way to achive this ?
There is no need to use #Async. Just use fixedRate attribute of #Scheduled instead of fixedDelay. Spring will make another invocation on the method after the given time regardless of any call is already being processed.
UPDATE:
Apparently fixedRate attribute does not enforce a scheduled method to be called asynchronously and increasing pool size of scheduler task executor only enables asynchronous execution of independent #Scheduled methods. Even putting #Async on the method does not make it work as OP has asked.
ScheduledAnnotationBeanPostProcessor just creates a Runnable from the #Scheduled method and does not create any pointcut as #Async method processor would. ScheduledThreadPoolExecutor waits until Runnable#run() is finished and sets the next execution time using the start time and the fixed rate. So if the method call takes more time than the scheduled time, the next task is triggered right after the previous call is finished.
An easy solution would be extracting the actual method into another class as a #Async method and calling this method from the #Scheduled method.
Implement SchedulingConfigurer and override configureTasks method.
Define poolsize more than one, It will work as you are expecting.
You should configure implementing SchedulingConfigurer:
#Configuration
#EnableScheduling
public class ScheduledConfiguration implements SchedulingConfigurer {
#Override
public void configureTasks(ScheduledTaskRegistrar scheduledTaskRegistrar)
{
ThreadPoolTaskScheduler threadPoolTaskScheduler = new ThreadPoolTaskScheduler();
threadPoolTaskScheduler.setPoolSize(10);
threadPoolTaskScheduler.setThreadNamePrefix("your-scheduler-");
threadPoolTaskScheduler.initialize();
scheduledTaskRegistrar.setTaskScheduler(threadPoolTaskScheduler);
}
}
With this code you achieve parallel execution of your method which is annotated with #Scheduled.
You may also set the property:
spring.task.scheduling.pool.size
#Async and #Scheduled method shouldn't be in a same class.
follow the manual and add AsyncConfigurerhttps://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/scheduling/annotation/EnableAsync.html

spring scheduler not running if a job not finished

I'm trying to make a lib for my spring applications. It has a scheduled job, it is autoconfigured and working as included Maven dependency in most cases.
I have a webapp which basically just flood requests to other webapps (some basic load-testing and fail-detection). The requester implementation is fully async (it has a manually configured async executor too). The web app has a periodic job too, but it doesn't finish reliably its job within the 2 minutes timeframe. It still fine. BUT.
When I started to use my firstly described lib on the secondly described server the lib starts working unreliable. It's not triggered every 2 minutes anymore. I don't have enough spring knowledge to find out why.
My best bet is the servers flood method starts a lot of async tasks, and the scheduler start these tasks, too and these requests are going to the same message queue.
Is there any method to separate my libs scheduled tasks from other servers scheduled tasks and make them running reliable every 2 minutes?
So my research is interesting... It seems to be, only configuring an async executor will force the scheduler to use that too. But if you implements schedulerConfigurer too, you will get another thread pool dedicated to scheduled tasks only.
So my implementation is something like this for separate the 2 threadpool.
#SpringBootApplication
#EnableAsync
#EnableScheduling
#ComponentScan
public class WebService extends AsyncConfigurerSupport implements SchedulingConfigurer {
public static void main(String[] args) {
System.setProperty("http.proxyHost", "localhost");
System.setProperty("http.proxyPort", "8888");
System.setProperty("spring.config.name", "web-server");
SpringApplication.run(WebService.class, args);
}
#Override
public Executor getAsyncExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(2);
executor.setMaxPoolSize(4);
executor.setQueueCapacity(500);
executor.setThreadNamePrefix("tester-");
executor.initialize();
return executor;
}
#Bean(destroyMethod = "shutdown", name = "scheduledExecutor")
public Executor taskExecutor() {
return Executors.newScheduledThreadPool(100);
}
#Autowired
#Qualifier(value="scheduledExecutor")
Executor scheduledExecutor;
#Override
public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
taskRegistrar.setScheduler(scheduledExecutor);
}
Still not clear enough if I could create a separated thread-pool for my lib or not, but its good enough for now.

how can you configure spring to execute overlapping fixedRate tasks?

i am trying to execute a task at a fixed rate using the #Scheduled annotation in java spring. however, it seems that by default spring will not execute a fixedRate task at a fixed rate if the task is slower than the rate. is there some setting i can add to my spring configuration to change this behavior?
example:
#Service
public class MyTask{
#Scheduled(fixedRate = 1000)
public void doIt(){
// this sometimes takes >1000ms, in which case the next execution is late
...
}
}
i have a work-around, but it seems less than ideal. basically, i just replace the default single-thread executor with a thread pool, then i have a scheduled method call an async method since the #Async annotation allows concurrent executions:
#Service
public class MyTask{
#Async
public void doIt(){
// this sometimes takes >1000ms, but the next execution is on time
...
}
}
#Service
public class MyTaskScheduler{
...
#Scheduled(fixedRate = 1000)
public void doIt(){
myTask.doIt();
}
}
#Configuration
#EnableScheduling
#EnableAsync
public class MySpringJavaConfig{
#Bean(destroyMethod = "shutdown")
public Executor taskScheduler() {
return Executors.newScheduledThreadPool(5);
}
}
boring details of my real-world scenario: in my production code i have a task that takes between 10ms and 10 minutes depending on the current workload. ideally, i would like to capture a new thread from the pool every 1000ms so that the number of concurrent threads increases with the workload. obviously i have an upper limit on threads in place (among other controls) to keep things from getting out of hand.
The TaskScheduler API (which backs some of the Spring Scheduling behavior) seems to be defined to prevent the behavior you are requesting
Schedule the given Runnable, invoking it at the specified execution
time and subsequently with the given period.
Parameters
period the interval between successive executions of the task (in milliseconds)
subsequently and successive seem to indicate that the next execution will only occur after the current execution is complete.
What's more, ScheduledExecutorService#scheduleAtFixedRate(..) (which the built-in TaskScheduler implementations use) also says
If any execution of this task takes longer than its period, then
subsequent executions may start late, but will not concurrently
execute.
So there's another layer of the implementation that prevents the behavior you want.
A possible solution, and one I don't recommend since the API doesn't seem to be built around it, is to define and provide your own TaskScheduler that does run the task concurrently. Look into #EnableScheduling and SchedulingConfigurer for how to register a TaskScheduler.
the best solution i have found so far is to simply use a delegate to make the method calls async. this is only preferable because it allows me to declare the schedule in the same class as the method which does the work:
#Service
public class AsyncRunner {
#Async
public void run(Runnable runnable) {
runnable.run();
}
}
#Service
public class MyTask{
...
#Scheduled(fixedRate = 1000)
public void scheduleIt(){
asyncRunner.run(this::doIt);
}
public void doIt(){
// this sometimes takes >1000ms, but the next execution is on time
...
}
}

Categories

Resources