Spring schedule graceful shutdown not working when using a cron scheduled - java

I have a small standalone application that configures the scheduler to terminate gracefully. With the following configuration:
#Bean
public TaskScheduler taskScheduler() {
ThreadPoolTaskScheduler scheduler = new ThreadPoolTaskScheduler();
scheduler.setWaitForTasksToCompleteOnShutdown(true);
scheduler.setAwaitTerminationSeconds(60);
return scheduler;
}
I can get it to gracefully terminate the scheduler, but only if I don't have any #Scheduled(cron = ) task. Once I have one of those, no matter what the scheduler will get stuck until timeout. I already tried configuring it also with an executor and do the shutdown/await manually and the effect is exactly the same.
These cron jobs are not even running. They are set to run at a fixed time during the night for example.
Spring version: 4.2.8.RELEASE
This will happen when the timeout reaches the end:
2017.07.28 01:44:56 [Thread-3] WARN Timed out while waiting for executor 'taskScheduler' to terminate
Any thoughts?

Because by default the ScheduledThreadPoolExecutor will wait for all delayed scheduled tasks to finish executing, even if scheduled tasks aren't running at that time.
Try this below:
#Bean
public TaskScheduler taskScheduler() {
ThreadPoolTaskScheduler scheduler = new ThreadPoolTaskScheduler() {
private static final long serialVersionUID = -1L;
#Override
public void destroy() {
this.getScheduledThreadPoolExecutor().setExecuteExistingDelayedTasksAfterShutdownPolicy(false);
super.destroy();
}
};
scheduler.setWaitForTasksToCompleteOnShutdown(true);
scheduler.setAwaitTerminationSeconds(60);
return scheduler;
}
Then the ScheduledThreadPoolExecutor will only wait for scheduled tasks which are currently running to finish executing.

Possible bug in that version of Spring? Refer to jira.spring.io/browse/SPR-15067

Related

Asynchronous task within scheduler in Java Spring

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

How to avoid relaunching task while it's status is still running on Spring Cloud Data Flow

I have deployed a Spring Batch project as a task on Spring Cloud Data Flow.
I have launched the task for the first time (It was going to run about 5 minutes which made the status of task is "running"). However, I have relaunched this task for the second time when it's status is running and it worked again.
1) How can I configure that the task could not be relaunched until it's completed status for the last time.
2) Besides, I found that when stopping the job, it will execute until it finishes the current step(My job has 2 steps and the second step was not executed ). However, are there any ways to shut down it (step 1) immediately when I stop the job?
How to avoid relaunching task while it's status is still running on Spring Cloud Data Flow
Spring Cloud Task 2.0.0 added a feature that allows you to prevent tasks from running concurrently. See https://spring.io/blog/2018/05/07/spring-cloud-task-2-0-0-release-is-now-available#restricting-concurrent-task-execution
You need to activate the spring.cloud.task.singleInstanceEnabled=true property.
You can find more details in the Restricting Spring Cloud Task Instances section.
You can use JobExplorer class for checking running jobs, see jobExplorer.findRunningJobExecutions(jobName) method.
In your JobLauncher class you can something like that:
#Component
public class MyJobLauncher {
private static final Logger LOG = LoggerFactory.getLogger(MyJobLauncher.class);
private final Job job;
private final JobLauncher jobLauncher;
private final JobExplorer jobExplorer;
#Autowired
public MyJobLauncher(#Qualifier("myJob") Job job, JobLauncher jobLauncher, JobExplorer jobExplorer) {
this.job = job;
this.jobLauncher = jobLauncher;
this.jobExplorer = jobExplorer;
}
public void launchJob() throws JobParametersInvalidException, JobExecutionAlreadyRunningException,
JobRestartException, JobInstanceAlreadyCompleteException, IOException {
if (jobExplorer.findRunningJobExecutions("myJob").isEmpty()) {
LOG.info("Starting myJob");
jobLauncher.run(job, new JobParameters());
LOG.info("Stopping myJob");
}
}
}
For shutting down the job, check JobExecution.stop() method which can be fetched from jobExplorer.getJobExecution() method.

Spring ThreadPoolTaskExecutor with fixed delay?

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.

Quartz - Shutdown Scheduler based on Job Variable

I am using Quartz to schedule a job in Java.
The scheduler repeats indefinitely.
If a certain exception occurs in the Job.class, I need to shutdown the scheduler.
How can I get the Job to notify the scheduling class to shutdown()?
Thanks,
RayK
Quartz's jobs are run by calling their execute(JobExecutionContext) method. This JobExecutionContext object has a getScheduler() method to access the Scheduler that is running your job.
So you could just do:
class YourJob implements Job {
//...
#Override
public void execute(final JobExecutionContext context) throws JobExecutionException {
//...
if (someCondition) {
try {
context.getScheduler().shutdown();
} catch (SchedulerException e) {
// log or rethrow!
}
}
//...
However, I'm not really sure if shutting the Scheduler down in the middle of running a job is a good idea. Why do you want to do that? If all you're trying to do is to stop some job from running again, it may be better to just unschedule it:
Unscheduling a Particular Trigger of Job:
// Unschedule a particular trigger from the job (a job may have more than one trigger)
scheduler.unscheduleJob(triggerKey("trigger1", "group1"));
Deleting a Job and Unscheduling All of Its Triggers
scheduler.deleteJob(jobKey("job1", "group1"));

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.

Categories

Resources