1.8. Scheduling#
Zamanlanmış görevler tanımlanmasını sağlayan bileşendir. Uygulama cluster özelliklerini sağlar (distributable). Quartz library'si kulanılmaktadır.
Konfigurasyon#
Gradle Bağımlılıkları
Cluster Konfigürasyonları
Veritabanında cluster olan bir uygulamadır. Quartz tabloları manuel ya da liquibase ile initialize edilebilir.
spring:
quartz:
scheduler-name: ${spring.application.name}
auto-startup: true
startup-delay: 2s
overwrite-existing-jobs: true
job-store-type: jdbc
liquibase:
parameters:
quartz-jdbc-job-store-schema: ${hvl.scheduling.quartz.jdbc-job-store.schema-name}
quartz-jdbc-job-store-table-prefix: ${hvl.scheduling.quartz.jdbc-job-store.table-prefix}
Liquibase
Liquibase konfigürasyonu için; changelog-root.yaml dosyasına; aşağıdaki gibi quartz changelog dosya tanımı eklenmelidir.
Çevre Değişkenleri
Cloud profili olarak hvl-scheduling
verilerek, ya da bootstrap.yml dosyasına yapılan aşağıdaki tanımlamalarla çevre değişkenleri yönetilebilir.
QUARTZ_JDBC_JOB_STORE_SCHEMA
(schema-name): Scheduler'ın kullandığı şema adıdır (default değerini hibernate'den alınır).table-prefix
: Quartz tablolarına koyulacak ön ektir (default değeriqrz_
'dır).
ÖNEMLİ NOT: Varsayılan olarak her uygulamanın default şemasında quartz tabloları oluşmaktadır. Bazı durumlarda quartz tablolarının tek şemada olması ya da ortak scheduler kullanımları gibi durumlar için bu property'ler yararlıdır. Örneğin; "oauth", "report" ve "bpmn" şemalarımız olsun. Bu şemalarda quartz tabloları olması istenmeyebilir. Bu durumda "schedule" şema ismi tanımlanır. "oauth", "report" ve "bpmn" uygulamalarının quartz scheduler'ların olduğunu varsayalım. Bu uygulamalar için ortak şema kullanmak istediğimizde yukarıdaki "schema-name" property'si değeri kullanılır.
ÖNEMLİ NOT: Varsayılan olarak her uygulamanın içerisine quartz dahil edildiğinde otokonfigurasyon tarafından uygulamanın adı ile tanımlanmış bir scheduler oluşmaktadır. Yani uygulama adı; "scheduler name"'dir. Sebebi; ortak bir arayüzden uygulamadaki planlanmış görevler yönetilmek istendiğinde, uygulama bazlı ayrı yapılmasının sağlanmasıdır.
Kullanım#
@HvlDistributableScheduled
anotasyonu ile deklaratif olarak,HvlDistributableJobSchedulerManager
bileşeni ile programatik olarak,Spring Quartz Bean
tanımı ile
olmak üzere 3 farklı şekilde kullanılabilir.
@HvlDistributableScheduled
Anotasyonu ile Deklaratif Kullanım#
@HvlDistributableScheduled
anotasyonu metodun üzerine yazılır ve bu anotasyonun property'leri örnekte görüldüğü gibi doldururulur.
Alan Adı | Açıklama |
---|---|
jobName | Görev adıdır. |
jobGroupName | Görev grubu adıdır. |
triggerType | Görevi tetikleyecek olan tetikleyicinin tipidir. |
triggerExp | Tetikleyici tipine uygun olarak girilmesi gereken ifade alanıdır. |
import org.quartz.JobExecutionContext;
import org.springframework.stereotype.Service;
import tr.com.havelsan.javarch.scheduling.annotation.HvlDistributableScheduled;
import tr.com.havelsan.javarch.scheduling.annotation.HvlJobTrigger;
import tr.com.havelsan.javarch.scheduling.annotation.HvlJobTriggerType;
import java.time.OffsetDateTime;
@Service
public class AnnotatedJobService {
AnnotatedJobService() {
super();
}
@HvlDistributableScheduled(
jobGroupName = "sample-job-group",
jobTriggers = @HvlJobTrigger(
triggerType = HvlJobTriggerType.CRON,
triggerExp = "0/59 * * * * ?"
)
)
public void scheduledWithCron(JobExecutionContext jobExecutionContext) {
System.err.println("scheduledWithCron triggered." + "(" + OffsetDateTime.now() + ")");
}
@HvlDistributableScheduled(
jobName = "scheduled-with-second",
jobGroupName = "sample-job-group",
jobTriggers = @HvlJobTrigger(
triggerType = HvlJobTriggerType.FIXED_RATE,
triggerExp = "PT15S"
)
)
public void scheduledWithSecond(JobExecutionContext jobExecutionContext) {
System.err.println("scheduledWithSecond triggered. (" + OffsetDateTime.now() + ")");
}
}
HvlDistributableJobSchedulerManager
Bileşeni ile Programatik Kullanım#
Bu yöntem ile çalışma zamanında (runtime
) görev yönetimi (ekleme/silme/durdurma..) yapılır.
Örnek kullanım:
public class JobDeclarationExample {
private final HvlDistributableJobSchedulerManager distributableJobSchedulerManager;
/*
veya
@Autowired
private HvlDistributableJobSchedulerManager distributableJobSchedulerManager;
*/
public JobDeclarationExample(HvlDistributableJobSchedulerManager distributableJobSchedulerManager) {
this.distributableJobSchedulerManager = distributableJobSchedulerManager;
}
public void doExample() {
// Görev silme
distributableJobSchedulerManager.removeJob("jobName", "jobGroupName");
distributableJobSchedulerManager.removeOrPauseJob("jobName", "jobGroupName");
distributableJobSchedulerManager.removeJobs();
// Görev ekleme
distributableJobSchedulerManager.declareInvocableJob(jobDefinitionBuilder -> jobDefinitionBuilder
.withJobName("jobName")
.withJobGroupName("jobGroupName")
.addJobTrigger("jobTriggerName", HvlJobTriggerType.CRON, "0 0 12 * * ?")
.withOverwriteJobDeclIfExists()
.withConcurrentJobExecution(false)
);
}
}
Spring Quartz Bean
Tanımı ile Kullanım#
Bu yöntemde; görevlerin kontrollü çalıştırılması için HvlQuartzExecutableJob
sınıfını kalıtan bir sınıf oluşturulup görev ile ilgili servis çağrısı veya işlemler bu sınıf üzerinden yapılması önerilir.
import org.quartz.JobExecutionContext;
import tr.com.havelsan.javarch.scheduling.quartz.job.HvlQuartzExecutableJob;
import java.time.OffsetDateTime;
import java.util.Set;
public class SampleJob extends HvlQuartzExecutableJob {
@Override
protected Integer jobExecutionMaxRetryCount() {
return 3;
}
@Override
protected Set<Class<? extends Throwable>> whenJobExecutionRetry() {
return Set.of();
}
@Override
protected void executeJobInstance(JobExecutionContext jobExecutionContext) {
System.err.println("job executed. (" + OffsetDateTime.now() + ")");
}
}
Ardından bu class, aşağıdaki örnekte görüldüğü gibi JobBuilder'a tanımlanır. TriggerBuilder bu tanımı job parametresi olarak alır ve bu tanımlar quartz scheduler'ına verilir.
import org.quartz.*;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import tr.com.havelsan.javarch.configuration.HvlBaseConfiguration;
import tr.com.havelsan.javarch.samples.scheduling.configuration.constant.HvlSchedulingConfigurationConstant;
import tr.com.havelsan.javarch.samples.scheduling.job.SampleJob;
/**
* @author javarch
*/
@Configuration
@ComponentScan(basePackages = {HvlSchedulingConfigurationConstant.BASE_PACKAGE})
public class HvlSchedulingConfiguration extends HvlBaseConfiguration {
@Bean
public JobDetail sampleJobDetail() {
return JobBuilder.newJob(SampleJob.class)
.storeDurably()
.build();
}
@Bean
public Trigger sampleTrigger() {
return TriggerBuilder.newTrigger()
.forJob(sampleJobDetail())
.withIdentity("triggerName", "group1")
.withSchedule(SimpleScheduleBuilder.simpleSchedule()
.withIntervalInSeconds(30)
.repeatForever())
.build();
}
}