Introduction
Spring provides support for both task scheduling and asynchronous method execution we will check different ways this can be achieved.
- Spring schedular with any Instant time
- Spring Schedular with CronTrigger
- Spring schedular using Annotation
When your app runs on multiple instances then Spring could not handle the synchronization of different instances, this is where we look at the need for external time scheduling on top of Spring Libraries and some of the ways are using –
- Spring Schedular with Shedlock
- Jenkins Schedular
- CloudWatch + Lambda
Spring schedular with any Instant time
First, let’s look at the simple case of triggering a code at a scheduled time.
Step1: Create SpringSchedularComponent
TriggerMessage method would be invoked after two minutes of app start-up. The scheduleTrigger method accepts a date that can be set dynamically, in the below example, I’m just invoking scheduleTrigger from a controller to set the triggerMessage to run after two min.
@Component
public class SpringShedularComponent {
private TaskScheduler scheduler = new ConcurrentTaskScheduler() ;
void scheduleTrigger(String date) {
//LocalDateTime localDateTime = LocalDateTime.parse("2017-08-21 10:19", DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm"));
Runnable runnable = this::triggermessage;
scheduler.schedule(runnable, LocalDateTime.now().plusMinutes(2).toInstant(OffsetDateTime.now().getOffset()));
}
public void triggerMessage() {
System.out.println("====================Triggered=============" + new Date());
}
}
Step2: Run the application
Spring Schedular with CronTrigger
We will look at Custom Task Executor for executing a schedular in Java program.
Step 1: Create CustomTaskExecutor implementing the Runnable.
public class CustomTaskExecutor implements Runnable {
private static final Logger LOGGER = LogManager.getLogger(CustomTaskExecutor.class);
private final int count;
public CustomTaskExecutor(final int count) {
this.count = count;
}
@Override
public void run() {
LOGGER.info(" Inside CustomTaskExecutor run method, count: " + count);
}
}
Step 2: Create TaskExecutorExample.
TaskScheduler : Spring 3.0 introduced a TaskScheduler
with a variety of methods for scheduling tasks to run at some point in the future.
For ex: ScheduledFuture schedule(Runnable task, Trigger trigger);
CronTrigger : The Trigger
interface has two implementations, The most interesting one is the CronTrigger
. It enables the scheduling of tasks based on cron expressions. For example, the following task is scheduled to run 15 minutes past each hour but only during the 9-to-5 “business hours” on weekdays.
import java.util.function.Function;
import org.springframework.scheduling.TaskScheduler;
import org.springframework.scheduling.support.CronTrigger;
public class TaskExecutorExample {
private TaskScheduler taskScheduler;
private Function<int, CustomTaskExecutor> customTaskExecutor = x -> new CustomTaskExecutor(x);
public TaskExecutorExample(TaskScheduler taskScheduler) {
this.taskScheduler = taskScheduler;
}
public void printMessages() {
for(int i = 0; i < 25; i++) {
taskScheduler.schedule(customTaskExecutor.apply(i), new CronTrigger("0 15 9-17 * * MON-FRI"
, TimeZone.getTimeZone("America/Los_Angeles"));
}
}
}
Spring Schedular using Annotation
Spring provides annotation support for both task scheduling and asynchronous method execution.
Step 1: Enable Scheduling Annotation in APP
EnableScheduling : Enable scheduling in your application.EnableAsync
: Enable async operation
@Configuration
@EnableAsync
@EnableScheduling
public class AppConfig {
}
Step 2: Create Method with Scheduled annotation
You can add the @Scheduled
annotation to a method, along with trigger metadata.
@Scheduled(cron="*/5 * * * * MON-FRI")
//@Scheduled(initialDelay = 1000, fixedRate = 5000)
public void doSomething() {
// something that should run on weekdays only
}
Spring Schedular with Shedlock
ShedLock makes sure that your scheduled tasks are executed at most once at the same time even if there are multiple instances are running. If a task is being executed on one node, it acquires a lock which prevents execution of the same task from another instances. ShedLock uses an external store like Mongo for coordination.
Step 1: Add Maven Dependency
<dependency>
<groupId>net.javacrumbs.shedlock</groupId>
<artifactId>shedlock-spring</artifactId>
</dependency>
<dependency>
<groupId>net.javacrumbs.shedlock</groupId>
<artifactId>shedlock-provider-jdbc-template</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-mongodb</artifactId>
</dependency>
<dependency>
<groupId>net.javacrumbs.shedlock</groupId>
<artifactId>shedlock-provider-mongo</artifactId>
</dependency>
Step 2: Enable SchedulerLock Configuration
EnableSchedulerLock : This Annotation enables ShedLock for the app, there are different Properties available like defaultLockAtMostFor, which defines the default value for how long the lock should be kept in case the machine which obtained the lock died before releasing it.
@Configuration
@EnableAsync
@EnableScheduling
@EnableSchedulerLock(defaultLockAtMostFor = "PT30S")
public class AppConfig {
}
Step 3: Create CustomTaskScheduler
SchedulerLock : The name given here should be unique as this unique name is used to identify the instance is locked/ not in DB.
lockAtLeastFor : The lock will be held atleast for given duration.
lockAtMostFor : How long the the lock should be kept in case the machine which obtained the lock died before releasing it.
@Component
public class ScheduledTasks {
@Scheduled(cron = "0 0/15 * * * ?")
@SchedulerLock(name = "TaskScheduler_scheduledTask", lockAtLeastFor = "PT5M", lockAtMostFor = "PT14M")
public void reportCurrentTime() {
.. Do your thing
}
@Bean
public LockProvider lockProvider(MongoClient mongo) {
return new MongoLockProvider(mongo.getDatabase("testdb"));
}
}
Mongo DB lock created by Shedlock
You can view the code here: https://github.com/jonesjalapatgithub/shedlockDemo
CloudWatch + Lambda Scheduling
Please read here for details : https://docs.aws.amazon.com/AmazonCloudWatch/latest/events/RunLambdaSchedule.html
Jenkins Schedular
This is the most easiest way to create a Schedular which can hit an API. Create a Jenkins instance from https://www.jenkins.io/.
After getting jenkins run, we need to configure the Jenkins instance to run at particular time. There are two methods to call a URL from jenkins
- Jenkins remote url invocation
- Execute Shell from build step, run commands for following tasks-
- Check Java present
- delete the workspace
- retrieve the Service Invoker application jar
- Extract jar
- Execute the Java program