Published on

Spring Boot İle Scheduler Yapısı Oluşturma I

Authors

Giriş

Herkese selamlar! Bu yazıda, Spring Boot ile dinamik bir scheduler yapısını nasıl oluşturabileceğimizi anlatmak istiyorum. Bir önceki yazımda spring boot scheduler hakkında genel bir bilgilendirme yapmıştım șimdi ise dinamik olarak scheduler yapısı nasıl kurulabilir bundan bahsedecegim.

Dinamik Scheduler Mekanizması Oluşturma

Önceki yazımızda @Scheduled anotasyonu yardımıyla Scheduler'ın hangi zaman aralıklarında çalıştığını yapılandırmıştık. Ancak bizden beklenti, bu zaman aralıklarının kullanıcıdan alınması olabilir, bu durumda ihtiyacımızı bu yapı karşılamayacaktır. Bu tür durumları çözmek için Spring'te kullanabileceğimiz alternatif Scheduler yapıları bulunmaktadır. Şimdi basit bir örneğini yapalım.

Proje Olusturma

Spring initializer a giriyoruz, Dependency olarak bu bolum icin web eklememiz yeterli olacaktir.

start-spring-io

Yukarıdaki alanları istediğiniz gibi değiştirebilirsiniz. Generate ettikten sonra, favori geliştirme ortamınızda (IDE) projenizi açabilirsiniz.

Geliştirme

İlk olarak, uygulamamızda Spring'in zamanlama (scheduler) mekanizmasını etkinleştirmek için @EnableScheduling anotasyonunu, Main metodumuzun bulunduğu sınıfa ekledik.

SchedulerApplication
@SpringBootApplication
@EnableScheduling
public class SchedulerApplication {

	public static void main(String[] args) {
		SpringApplication.run(SchedulerApplication.class, args);
	}

}

Sonra, TaskDefinition nesnesini oluşturarak geliştirmemize devam edelim.

TaskDefinition
public class TaskDefinition {
    private String cronExpression;
    private String name;

    //all args constructer
    //getter
}

Çok basit iki özelliğimiz bulunmaktadır. name alanının aslında işlevsel bir etkisi yoktur; sadece bilgi tutmak amacıyla eklenmiş bir alandır. CronExpression ise cron job'in ne aralıklarla çalışacağını belirten bir özelliktir.

Controller'ımızı oluşturmayla devam edelim.

SchedulerController.java
@RequestMapping("/api/v1/scheduler")
public class SchedulerController {

    private final SchedulerService schedulerService;

    public SchedulerController(SchedulerService schedulerService) {
        this.schedulerService = schedulerService;
    }

    @PostMapping
    void addTask(@RequestBody TaskDefinition taskDefinition) {
        schedulerService.addTask(taskDefinition);
    }
}

Burada, bir adet endpoint oluşturduk. Bu endpoint, parametre olarak TaskDefinition alır ve ayrıca bağımlılık olarak bir scheduler servisi içerir.

Şimdi, scheduler servisimizi oluşturalım.

SchedulerService.java
@Service
public class SchedulerService {

    private final TaskScheduler taskScheduler;

    public SchedulerService(TaskScheduler taskScheduler) {
        this.taskScheduler = taskScheduler;
    }

    public void addTask(TaskDefinition taskDefinition) {
        MyJob myJob = new MyJob(taskDefinition.getName());
        CronTrigger cronTrigger = new CronTrigger(taskDefinition.getCronExpression());
        taskScheduler.schedule(myJob, cronTrigger);
    }
}

Scheduler servisini oluşturduk, içerisinde sadece "addTask" adında bir metot bulunmaktadır. Bu metot, Spring Framework'un scheduling paketinde yer alan "TaskScheduler" bağımlılığını kullanmaktadır. "TaskScheduler" bir Interface'dir ve onu implement eden diğer Spring Boot class'ları vardır; varsayılan olarak kullanılan sınıf "ThreadPoolTaskScheduler" sınıfıdır.

TaskScheduler Interface'indeki "schedule" metodunu kullanıyoruz, birinci parametre olarak "Runnable" bir nesne beklerken, ikinci parametre olarak bir "Trigger" nesnesi bekler. "Runnable", Thread'lerde bildiğimiz gibi içinde "run" metodunu barındıran bir arayüzdür. Thread olușturmamızı sağlar. Burada parametre olarak verdiğimiz thread, diğer parametre olarak verdiğimiz çalışma zamanı aralığında çalışacaktır. Bu thread'i başlatma ve bitirme görevi aslında "ThreadPoolTaskScheduler" objesinin sorumluluğundadır. Arka planda ScheduledThreadPool oluşturup verdiğimiz konfigürasyonlara göre thread'leri çalıştırır. Bu arayüzü uygulayan bir nesne oluşturarak "run" metodu ile işin çalışma esnasında ne yapılacağını belirleyeceğiz.

Bu bağlamda, MyJob adında bir nesne yaratıyoruz.

MyJob.java
public class MyJob implements Runnable {

    private final String name;

    public MyJob(String name) {
        this.name = name;
    }

    @Override
    public void run() {
        System.out.println(LocalTime.now().format(DateTimeFormatter.ofPattern("hh-mm-ss")) + " ==> " + name + " running");
    }
}

Runnable arayüzünü uyguladık ve run metodunun içini doldurmaya başladık. Basit olsun diye, sadece constructor'dan aldığı parametreyi mevcut zamanla birlikte Output'a yazdıran bir metot haline getirdik.

Scheduler servisine geri döndüğümüzde, ikinci parametre olarak bir Trigger aldığını söylemiştik. Trigger bir arayüzdür ve Scheduler'ı cronExpression ile yapılandırmak istediğimiz için, Trigger'ı implemente eden yine Spring Scheduler içerisinde bulunan CronTrigger nesnesini kullanabiliriz. CronTrigger, parametre olarak bizden cronExpression bekliyor, taskDefinition'dan aldgımız cronExpression bilgisini paremetre olarak geçip oluşturuyoruz.

Hem Runnable hem de Trigger için beklenen nesneleri oluşturduktan sonra, TaskScheduler'in schedule metodunu çağırarak uygulamamızı çalıştırıyoruz.

Test

Test için bize yardımcı olacak olan Postman aracını kullanacağız. Aşağıdaki gibi bir isteği oluşturuyoruz:

postman

CronExpression olarak her saniyede çalışacak bir ifade veriyoruz. Genellikle Cron Expression, dakika, saat, ayın günleri, ay ve haftanın günlerinden oluşan 5 alanla belirtilir.

Spring Boot'ta ise ilk alana saniye eklenmiş durumda, bu nedenle 6 adet alan beklenir. Cron Expression için tanımlanmış özel karakterler vardır. Daha fazlası için CronTab Guru sitesine bakabilirsiniz; denemeler yapabilmek için oldukça faydalı bir kaynaktır.

Postman'dan isteği attıktan sonra response 200 döndü. Her saniyede bir job'un çalışacağını söylemiştik ve baktığımızda aşağıdaki gibi bir çıktı görüyoruz:

02-36-23 ==> test1 running
02-36-24 ==> test1 running
02-36-25 ==> test1 running
02-36-26 ==> test1 running
02-36-27 ==> test1 running
02-36-28 ==> test1 running
02-36-29 ==> test1 running
02-36-30 ==> test1 running

Bu çıktıya göre, her saniyede bir job'un başarılı bir şekilde çalıştığını görebiliriz.

Github reposu icin buraya 👀 bakabilirsiniz.

Sonuç

Yazıyı okuduğunuz için teşekkür ederim, umarım faydalı olmuştur. Bu yazıda Spring Boot'da dinamik scheduler yapısını en basit haliyle anlattım serinin devam yazılarında görüşmek ūzere.

Herkese iyi çalışmalar. ✌🏼