How to run spring-batch jobs threadpooled? - java

I'm starting spring-batch jobs using JobLauncher.run().
Question: how can I threadpool those invocations? So that eg a maximum of 4 job threads may be running concurrently, and any further jobs are just queued?
#Autowired
private JobRegistry registry;
#Autowired
private JobLauncher launcher;
Job job = registry.getJob("jobname");
launcher.run(job, params); //immediately starts the job

You can set ThreadPoolTaskExecutor as the task executor used by the SimpleJobLauncher (the class that actually launches the jobs). This executor has some properties you can set, especially maxPoolSize.
public JobLauncher createJobLauncher() {
ThreadPoolTaskExecutor taskExecutor = new ThreadPoolTaskExecutor();
taskExecutor.setCorePoolSize(4);
taskExecutor.setMaxPoolSize(4);
taskExecutor.afterPropertiesSet();
SimpleJobLauncher launcher = (SimpleJobLauncher) super.createJobLauncher();
launcher.setTaskExecutor(taskExecutor);
return launcher;
}

Please take a look at the docs.
Possibly a duplicate of Spring Batch Multiple Threads and How to set up multi-threading in Spring Batch?.
Update:
I use the following code to start a batch job asynchronously. But I don't know how to limit the maximum number of executions as you mentioned.
TaskExecutor taskExecutor = new SimpleAsyncTaskExecutor();
taskExecutor.execute(new Runnable() {
public void run() {
try {
jobLauncher.run(importJob, jobParametersBuilder.toJobParameters());
} catch (Exception e){
e.printStackTrace();
}
}
});

Related

How to pause a scheduled task started using springboot's #Scheduled annotation?

I have a simple scheduled task that was created using the #Scheduled annotation. Something like this -
public void doSomething() {
// My code
}
I also have a blue-green upgrade scenario where this scheduled task may potentially run in both the blue and green clusters. And given this scheduled task modifies the DB, there is a possibility that one of the nodes might overwrite the data from the other node - or worst case race conditions.
I want to pause all the scheduled tasks on the green cluster until it is ready to accept traffic. I already have the code wired to listen to changes to the application state.
I explored a few options -
Simply adding a boolean at the start of the task.
Adding a decorator to the ThreadPoolTaskScheduler.
The problem with these approaches is they skip the task for that particular schedule and not pause it. So, if the task is skipped, say at T, and the application becomes immediately, the application will have to wait T+30mins to refresh data.
Is there a way in springboot to pause schedulers and resume immediately?
It looks like natively spring does not have the ability to pause scheduled tasks. I tend to use Quartz Scheduler when running schedule tasks with spring.
In order to do that we have to do a couple configurations.
First we have to setup our context aware component:
#Component
public final class ApplicationContextHolder extends SpringBeanJobFactory implements ApplicationContextAware {
private static ApplicationContext context;
private transient AutowireCapableBeanFactory beanFactory;
#Override
public void setApplicationContext(ApplicationContext ctx) throws BeansException {
if (context == null) {
beanFactory = ctx.getAutowireCapableBeanFactory();
context = ctx;
}
}
#Override
protected Object createJobInstance(final TriggerFiredBundle bundle) throws Exception {
final Object job = super.createJobInstance(bundle);
beanFactory.autowireBean(job);
return job;
}
/**
* Get the current application context
* #return the current context
*/
public static ApplicationContext getContext() {
return context;
}
}
Then our configuration:
#Configuration
public class QuartzSchedulerConfig {
#Autowired
private ApplicationContext applicationContext;
/**
* Create the job factory bean
* #return Job factory bean
*/
#Bean
public JobFactory jobFactory() {
ApplicationContextHolder jobFactory = new ApplicationContextHolder();
jobFactory.setApplicationContext(applicationContext);
return jobFactory;
}
/**
* Create the Scheduler Factory bean
* #return scheduler factory object
*/
#Bean
public SchedulerFactoryBean schedulerFactory() {
SchedulerFactoryBean factory = new SchedulerFactoryBean();
factory.setAutoStartup(true);
factory.setSchedulerName("Scheduler");
factory.setOverwriteExistingJobs(true);
factory.setJobFactory(jobFactory());
return factory;
}
}
Then our actual service:
#Service
public class SchedulerService {
#Autowired
private SchedulerFactoryBean schedulerFactory;
private Scheduler scheduler;
private ScheduledExecutorService executor;
/**
* Initialize the scheduler service
*/
#PostConstruct
private void init() {
scheduler = schedulerFactory.getScheduler();
executor = Executors.newScheduledThreadPool(Runtime.getRuntime().availableProcessors());
}
...//Other Scheduling tasks can go here
public void pauseJob(String triggerName, String groupName) {
TriggerKey tk = new TriggerKey(triggerName, groupName);
scheduler.pauseJob(tk);
}
}
Quartz Scheduling gives a lot of flexibility when it comes to scheduling tasks
http://www.quartz-scheduler.org/overview/
Customize the task scheduler to wait until it finishes the job and allow spring boot to do the graceful shutdown
#Bean
TaskSchedulerCustomizer taskSchedulerCustomizer() {
return taskScheduler -> {
taskScheduler.setAwaitTerminationSeconds(60);
taskScheduler.setWaitForTasksToCompleteOnShutdown(true);
};
}
another opinion
Use Shedlock to lock the scheduler for the given amount of time(upgradable time). It also solves the scheduler running on all/multiple instances instead of one instance.

Spring Boot: Can we have seperate thread pool for each request?

I've implemented asynchronous execution for a method in my application using Spring Boot #Async. I'm having a custom thread pool with 20 thread. In a for loop calling the async method for 30 times.
Each individual request is executing asynchronously but when i made two different requests at the same time to my API from browser, first request is executing and then second. Not both requests executing the same method parallely.
I thought when first request reach the app, it's started executing the async method and since it's being executing for 30 times and my pool is having 20 threads, all threads are busy in executing first request. So even second request came for execution also since the pool of threads busy, the other request is waiting till a thread becomes free in the pool.
Can we have separate thread pool for each individual request. or any way that we can make execution of each request separate independent of other request processing.
Here is my code sample.
#SpringBootApplication
#EnableAsync
public class AppBootStrap
{
public static void main(String[] args)
{
SpringApplication.run(AppBootStrap.class, args);
}
#Bean
public AsyncTaskService asyncTaskService() {
return new AsyncTaskService();
}
#Bean(name="customExecutor")
public Executor taskExecutor() {
ThreadPoolTaskExecutor poolExecutor = new ThreadPoolTaskExecutor();
poolExecutor.setCorePoolSize(10);
poolExecutor.setMaxPoolSize(20);
poolExecutor.setThreadNamePrefix("customPoolExecutor");
poolExecutor.initialize();
return poolExecutor;
}
}
**Controller Class:**
#RestController
public class TaskController
{
#Autowired
private TaskService taskService;
#RequestMapping("/performAction")
public void performAction() {
taskService.performAction();
}
}
**Service class**
#Service
public class TaskService
{
#Autowired
private AsyncTaskService asyncTaskService;
public void performAction()
{
for(int i = 0; i < 30; i++) {
asyncTaskService.greetings(i);
}
}
}
**Async Method Class**
public class AsyncTaskService
{
#Async
public void greetings(Integer i)
{
try
{
Thread.sleep(500 * (i + 10));
}
catch (InterruptedException e)
{
e.printStackTrace();
}
System.out.println("Welcome to Async Service: " + i);
}
}
#Bean(name = "apiTaskExecutor")
public ThreadPoolTaskExecutor apiTaskExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(20);
executor.setMaxPoolSize(100);
executor.setQueueCapacity(50);
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
executor.initialize();
return executor;
}
#Bean(name = "lruTaskExecutor")
public ThreadPoolTaskExecutor lruTaskExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(100);
executor.setMaxPoolSize(200);
executor.setQueueCapacity(500);
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
executor.initialize();
return executor;
}
The Way to do this is to create two Thread different Thread Pools using Different Qualifier names. (As shown above)
#Autowired
#Qualifier("apiTaskExecutor")
private ThreadPoolTaskExecutor apiTaskExecutor;
Then autowire the required pool using the qualifier you have given . You can also use the #Async annotation instead of autowiring . I prefer this way.
If I am not wrong this way of having different thread pools for different Tasks is known as bulkhead pattern.
I think the problem is that the customExecutor bean is a singleton. This means that when the second request tries to use this bean it finds all the threads of the thred pool busy. You can try to make the customExecutor bean non-singleton by using the #Scope("prototype") annotation which will cause a new one to be instantiated whenever a bean of this type is requested.

Autowiring multiple SimpleJobLaunchers

I have a class which I want to run jobs from asynchronously. To do this I have the following code:
#Resource
private SimpleJobLauncher jobLauncher;
#PostConstruct
public void initLauncher(){
jobLauncher.setTaskExecutor(taskExecutor());
}
However, there is a situation where I need to do this synchronously. So, what I have done is added the following variable:
#Resource
private SimpleJobLauncher synchronousJobLauncher;
Which I hoped wouldn't have the taskExecutor on it making it synchronous. I then pass the synchronousJobLauncher to the places where I want to do things synchronously. However, using the synchronousJobLauncher gives me the same error as when I use the asynchronous one which leads me to believe that you cannot autowire the same variable twice like I am trying to do. If I do not do the #PostConstruct part of the code, the synchronous part works like I expect but not the asynchronous part, even though they use, what I think are, different job launchers.
Any ideas? I have tried using the #Resource annotation instead of #Autowired.
I haven't used SimpleJobLaunchers, but normally, in Spring I always used the #Async annotation, which make asynchronous execution quite simple. All you have to do is add this annotation, #EnableAsync in any configuration file, just like the one below.
#Configuration
#EnableAsync
public class MvcConfig extends WebMvcConfigurerAdapter { ... }
Now, is all about adding the #Async to any method that will run asynchronously.
#Component
class AsyncTask {
#Async
public Future<String> run() throws InterruptedException {
return new AsyncResult<String>("return value");
}
}
And if you want to wait for the result, you can do the following.
public void callAsyncTask() {
try {
Future<String> future = asyncTask.run();
// Do some other things while the async task is running.
// future.get() will block the function and wait for the result.
String asyncResult = future.get();
} catch(InterruptedException e) {
e.printStackTrace();
// Do something else.
}
}
Hope this helps. I know is not directly what you were asking for, but maybe this solution can facilitate your problem.
You can create two job launchers (one synchronous and one asynchronous) then inject the one you want using a qualifier. By default, the SimpleJobLauncher uses a synchronous task executor, so you need to configure a task executor only for the asynchronous one. Here is an example:
#Bean
public SimpleJobLauncher synchronousJobLauncher() {
return new SimpleJobLauncher();
}
#Bean
public ThreadPoolTaskExecutor threadPoolTaskExecutor() {
return new ThreadPoolTaskExecutor();
}
#Bean
public SimpleJobLauncher asynchronousJobLauncher() {
SimpleJobLauncher jobLauncher = new SimpleJobLauncher();
jobLauncher.setTaskExecutor(threadPoolTaskExecutor());
return jobLauncher;
}
Then, inject the job launcher you want with a #Qualifier("synchronousJobLauncher") or #Qualifier("asynchronousJobLauncher") according to your needs.
Hope this helps.

Converting Spring task XML configuration to code configuration

I'm trying to convert XML configuration for using Spring's tasks framework into purely code configuration. I'm able to reproduce the functionality but whenever I shut down the war on the Tomcat server the task scheduler lives on, it hangs (it doesn't hang with XML configuration). I've debugged to inspect the instances of scheduler and executor but I'm not seeing a difference so I'm not sure what could be causing it to hang.
Here is the XML configuration that works:
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:task="http://www.springframework.org/schema/task"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
http://www.springframework.org/schema/task http://www.springframework.org/schema/task/spring-task-3.1.xsd">
<task:executor id="com.work.gwx.pix.executor"
pool-size="${pix.job.executor.pool.size:1-40}"
queue-capacity="${pix.job.executor.queue.capacity:0}"
rejection-policy="CALLER_RUNS"/>
<task:scheduler id="com.work.gwx.pix.scheduler" pool-size="${pix.job.scheduler.pool.size:4}" />
<task:annotation-driven executor="com.work.gwx.pix.executor" scheduler="com.work.gwx.pix.scheduler" />
<bean id='queueProcessor' class="com.work.gwx.queueing.QueueProcessor" />
</beans>
Here is the code configuration:
#EnableAsync
#EnableScheduling
#Configuration
public class TaskConfiguration implements AsyncConfigurer, SchedulingConfigurer {
#Value("${pix.job.executor.max.pool.size:1}")
private int executorMaxPoolSize;
#Value("${pix.job.executor.queue.capacity:0}")
private int executorQueueCapacity;
#Value("${pix.job.scheduler.pool.size:4}")
private int schedulerPoolSize;
#Bean(destroyMethod = "shutdown")
public Executor pixTaskScheduler() {
final ScheduledThreadPoolExecutor ex = new ScheduledThreadPoolExecutor(schedulerPoolSize, new ThreadPoolTaskExecutor());
// ex.setExecuteExistingDelayedTasksAfterShutdownPolicy(false);
return ex;
}
#Bean
public Executor pixExecutor() {
final ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(executorMaxPoolSize);
executor.setQueueCapacity(executorQueueCapacity);
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
executor.setThreadFactory(new ThreadPoolTaskExecutor());
executor.initialize();
return executor;
}
#Override
public void configureTasks(final ScheduledTaskRegistrar taskRegistrar) {
taskRegistrar.setScheduler(pixTaskScheduler());
}
#Override
public Executor getAsyncExecutor() {
return pixExecutor();
}
#Override
public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
return new SimpleAsyncUncaughtExceptionHandler();
}
}
When I use setExecuteExistingDelayedTasksAfterShutdownPolicy(false) in code configuration it does shut down but I'm worried that might have adverse effects as that is set to true when done via XML configuration. Also, I should note, the QueueProcessor class is doing the work I want and I don't mind if delayed executions get cancelled -- I just don't want currently executing threads to be abruptly cancelled.
This is the message I get when it hangs:
SEVERE: The web application [/pix-queue-processor] appears to have
started a thread named [ThreadPoolTaskExecutor-1] but has failed to
stop it. This is very likely to create a memory leak.
Any ideas on what might be causing the hanging? Or, would using that commented out method let me do what I want (won't kill a running task but will cancel delayed tasks)?
Your Java based configuration isn't really a representation of the the XML configuration. There are at least 2 things that are different.
Your TaskExecutor has another TaskExecutor wired as a ThreadFactory this isn't the case in XML and also starts another TaskExecutor.
Your TaskScheduler uses a new TaskExecutor whereas the xml configuration use the one configured.
The have your task finished on shutdown you can set the waitForTasksToCompleteOnShutdown property on the TaskExecutor to true then any pending tasks will be completed and no new tasks will be accepted.
Also the call to initialize shouldn't be needed as the afterPropertiesSet method will be called by Spring which in turn calls initialize.
The following 2 bean definitions are more in line with the XML configuration (and you now have a single TaskExecutor instead of 3 and it will first finish tasks before shutdown.
#Bean(destroyMethod = "shutdown")
public Executor pixTaskScheduler() {
final ScheduledThreadPoolExecutor ex = new ScheduledThreadPoolExecutor(schedulerPoolSize, pixExecutor());
// ex.setExecuteExistingDelayedTasksAfterShutdownPolicy(false);
return ex;
}
#Bean
public Executor pixExecutor() {
final ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(executorMaxPoolSize);
executor.setQueueCapacity(executorQueueCapacity);
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
executor.setWaitForTasksToCompleteOnShutdown(true);
return executor;
}

How to shutdown ThreadPoolExecutor in spring bean when tomcat is shutdown?

I create a threadpoolexecutor in a spring bean, so I need to shutdown this executor when tomcat is shutdown.
public class PersistBO implements InitializingBean {
private ThreadPoolExecutor executer;
public void shutdownExecutor() {
executer.shutdown();
}
#Override
public void afterPropertiesSet() throws Exception {
taskQueue = new LinkedBlockingQueue<Runnable>(queueLength);
executer = new ThreadPoolExecutor(minThread, maxThread, threadKeepAliveTime,
TimeUnit.SECONDS, taskQueue, new ThreadPoolExecutor.DiscardOldestPolicy());
}
I have searched solution on google and get a result. That is to add a shutdownhook to java.lang.runtime. However, the java docs says java.lang.Runtime#shutdownHook is called when the last non-daemon thread exits. So it is a dead lock. Is there any solution to shutdown executor in spring bean?
I guess lifecycle of the executor should depend on lifecycle of your application, not Tomcat as a whole. You can stop your application while Tomcat is still running, therefore Runtime.shutdownHook() is not applicable.
Since you already use Spring and its InitializingBean for initialization, you can use DispasableBean to perform cleanup when application context is being closed:
public class PersistBO implements InitializingBean, DisposableBean {
public void destroy() {
shutdownExecutor();
}
...
}
Use the Runtime to add a shutdown hook. Here's a very good tutorial by Heinz Kabutz: http://www.roseindia.net/javatutorials/hooking%20_into_the_shutdown_call.shtml
You can implement your own javax.servlet.ServletContextListener to be notified when your application is being shutdown and shutdown the pool from the listener.
Use the #Predestroy annotation on your shutdown method on the bean. this will result in spring calling this method when context is shutting down
Check if there is some executor service has a thread running in background. you can shutdown an executor by calling executor.shutdownNow().
also see http://taranmeet.com/jvm-not-shutting-down-on-spring-context-close/
Here is how to start and stop a thread in Spring bean.
#PostConstruct
public void init() {
BasicThreadFactory factory = new BasicThreadFactory.Builder()
.namingPattern("myspringbean-thread-%d").build();
executorService = Executors.newSingleThreadExecutor(factory);
executorService.execute(new Runnable() {
#Override
public void run() {
try {
// do something
System.out.println("thread started");
} catch (Exception e) {
logger.error("error: ", e);
}
}
});
executorService.shutdown();
}
#PreDestroy
public void beandestroy() {
if(executorService != null){
executorService.shutdownNow();
}
}

Categories