I currently have a scheduled task within my Spring application.
However a two parts of this logic is severely time-consuming and I am wondering if there would be a way to make these two parts asynchronous so that it does not interfere with the time of the logic being executed.
The logic that I need to execute as follows.
#Scheduled(fixedDelay = 10000)
public void startAuction() throws Exception {
List<SchGoodsAuctionStartListRes> list = schedulerService.schGoodsAuctionStartList();
for (SchGoodsAuctionStartListRes item : list) {
schedulerService.schGoodsAuctionStart(item);
// 1st time consuming block that needs async
PushInfo pushInfo = pushMapper.pushGoodsSeller(item.getGoodsIdx());
pushInfo.setTitle("Start");
pushInfo.setBody("[" + pushInfo.getBrand() + "] started.");
pushInfo.setPushGrp("001");
pushInfo.setPushCode("003");
fcmPushUtil.sendPush(pushInfo);
// 2nd time consuming block that needs async
List<PushInfo> pushInfos = pushMapper.pushGoodsAuctionAll(item.getIdx());
for (PushInfo pushInfoItem : pushInfos) {
pushInfoItem.setTitle("\uD83D\uDD14 open");
pushInfoItem.setBody("[" + pushInfo.getBrand() + "] started. \uD83D\uDC5C");
pushInfoItem.setPushGrp("002");
pushInfoItem.setPushCode("008");
fcmPushUtil.sendPush(pushInfoItem);
}
}
}
From my understanding, a scheduler already is executing logic asynchronously, and I wonder if there would be any way of making those two blocks asynchronous so that it does not cause delays when executing this logic.
Any sort of advice or feedback would be deeply appreciated!
Thank you in advance!
There are several approaches that you could take here.
Configuring Thread Pool executor for Spring's scheduled tasks
By default Spring uses single thread executor to execute scheduled tasks, which means that even if you have multiple #Scheduled tasks or another execution for a task triggers before the previous one is completed, they will all have to wait in the queue.
You can configure your own executor to be used by Spring Scheduling. Take a look at the documentation of #EnableScheduling, it is pretty exhaustive on the subject.
To configure ExecutorService to be used for scheduled tasks it is enough to define a bean:
#Bean
public TaskScheduler taskScheduler() {
ThreadPoolTaskScheduler threadPoolTaskScheduler = new ThreadPoolTaskScheduler();
threadPoolTaskScheduler.setPoolSize(8);
threadPoolTaskScheduler.setThreadNamePrefix("task-scheduler");
return threadPoolTaskScheduler;
}
Additionally, if you use Spring Boot, you can use properties file:
spring.task.scheduling.pool.size=8
Executing scheduled tasks asynchronously
To execute scheduled tasks asynchronously you can use Spring's #Async annotation (and make sure to #EnableAsync somewhere in your configuration. That will make your tasks to be executed on a background thread, freeing the scheduling thread.
#EnableAsync
public class ScheduledAsyncTask {
#Async
#Scheduled(fixedRate = 10000)
public void scheduleFixedRateTaskAsync() throws InterruptedException {
// your task logic ...
}
}
Offload expensive parts of your tasks to a different executor
Finally, you could use a separate ExecutorService and run expensive parts of your tasks using that executor instead of the one used for task scheduling. This will keep the time needed to complete the execution on the thread used by Spring to schedule tasks to a minimum, allowing it to start next executions.
public class ScheduledAsyncTask implements DisposableBean {
private final ExecutorService executorService = Executors.newFixedThreadPool(4);
#Scheduled(fixedRate = 10000)
public void scheduleFixedRateTaskAsync() throws InterruptedException {
executorService.submit(() -> {
// Expensive calculations ...
});
}
#Override
public void destroy() {
executorService.shutdown();
}
}
Related
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).
In my application I have a single java Executor, can it create issues if I create multiple Scheduler from the same instance of Executor, having something like:
public class MyServiceController {
#Autowired
private Executor mainExecutor;
public Object something() {
return Flux.from(somethingElse())
.publishOn(Schedulers.fromExecutor(mainExecutor))
.toFuture()
}
}
(Or having multiple classes implementing this pattern, all of them having the same instance of mainExecutor)
it should be fine in both cases, the same Executor will back all Scheduler.Worker spawned by each Scheduler you create from it, and the same is true if it is an ExecutorService (albeit with a different Scheduler implementation for the wrapper).
For clarity, I'd still consider making the Scheduler a singleton next to the Executor.
I am trying to schedule a task inside a Spring #Bean which will update the property of the instance returning from Bean.
I am able to run this code, and the executor works fine a couple of times, but after that, it suddenly stops loading.
What exactly is the problem here? Is there a better way to work this out??
#Bean(name="service")
public Service getService(){
Service service = new Service();
ScheduledExecutorService serviceLoader = Executors.newScheduledThreadPool(1);
serviceLoader.scheduleAtFixedRate(new Runnable() {
#Override
public void run() {
service.loadAllLiveEvents();
}
}, 0, 1, TimeUnit.HOURS);
return service;
}
The lifecycle of serviceLoader looks weird - it gets initialized right during the method or service, then schedules some work and then service gets returned. What happens to the reference to this pool? when the shutdown can be called?
In addition, the first iteration runs immediately, and this happens when the application context is not ready yet, this can lead to unpredictable results depending on the actual code that runs during the iteration.
I can't say for sure what happens based on this code snippet, but here are some possible solutions:
Use #Scheduled annotation, running scheduled tasks is a bulit-in spring feature. There are a lot of tutorials, here is one of them
If you absolutely have to use the thread pool, I suggest the following configuration:
#Configuration
public class MyConfiguration {
#Bean
public Service service() {
return new Service();
}
#Bean(destroyMethod="shutdownNow") // or shutdown - now spring will close the pool when the app context gets closed
#Qualifier("serviceLoaderPool")
public ScheduledExecutorService serviceLoader() {
return Executors.newScheduledThreadPool(1);
}
#EventListener
public void onAppContextStarted(ApplicationReadyEvent evt) {
ScheduledExecutorService loader =
(ScheduledExecutorService)evt.getApplicationContext().getBean("serviceLoaderPool");
Service service = evt.getApplicationContext.getBean(Service.class);
loader.scheduleAtFixedRate(new Runnable() {
#Override
public void run() {
service.loadAllLiveEvents();
}
}, 0, 1, TimeUnit.HOURS);
}
}
With this approach you can be sure that the service will start refreshing when the application context is ready.
The lifecycle of the executor service is well-defined as well, spring manages it as a regular singleton bean, so it won't be GC'ed as long as the application context is up-and-running.
The presence of destroy method guarantees graceful shutdown (again, spring will call it for you).
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.
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
...
}
}