Various Scheduling technique for Java Spring Application

stopwatch, time management, time-2215261.jpg

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

Output

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

  1. Jenkins remote url invocation
  2. 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

Leave a Comment

Your email address will not be published.

error: Content is protected !!
Scroll to Top