• 30.10.2026 19:01:20
  • İbrahim Gökyar

Spring Scheduler temelleri, @Scheduled, TaskExecutor, Cron ifadeleri ve dağıtık zamanlama çözümleri. Performans, hata yönetimi ve modern alternatiflere geçiş adımları.

Spring ile Task Scheduling: Pratik Rehber, İpuçları ve Modern Geçişler

Arka planda çalışan zamanlanmış işler (scheduled tasks) birçok uygulama için kritik öneme sahiptir: rapor üretimi, temizleme işleri, entegrasyon tetiklemeleri ve daha fazlası. Spring Framework, hem basit hem de karmaşık zamanlama senaryolarını destekleyen yerleşik araçlar sunar. Bu makalede @Scheduled, TaskExecutor, Cron ifadeleri, hata yönetimi, ölçeklenebilirlik ve dağıtık mimarilere geçiş yollarını ele alacağız.

Spring Scheduling: Temel Kavramlar ve @Scheduled

Spring içinde zamanlanmış işler oluşturmanın en hızlı yolu @Scheduled anotasyonudur. Basit periyodik işler için yeterince pratiktir ve birkaç satır konfigürasyonla çalışır. Ancak üretimde dikkat etmeniz gereken davranışlar ve sınırlamalar vardır; örneğin aynı JVM içinde bile eşzamanlı çalışan işlerin yönetimi, thread havuzu ayarları ve hata izolasyonu gibi konular göz önünde bulundurulmalıdır.

Örnek temel kullanım aşağıdadır. Bu örnek uygulama seviyesinde bir bean içinde periyodik log yazma işini gösterir:

<!-- Java sınıfı içinde -->
@Component
public class CleanupTask {

    @Scheduled(fixedDelay = 60000)
    public void cleanup() {
        // Temizlik kodu
        System.out.println("Cleanup çalıştı: " + Instant.now());
    }
}

Bu yaklaşım basit senaryolar için uygundur fakat @EnableScheduling ile scheduling altyapısını etkinleştirmeyi unutmayın:

@Configuration
@EnableScheduling
public class SchedulingConfig {
}

TaskExecutor ve Thread Yönetimi

Varsayılan olarak @Scheduled işleri tek bir thread üzerinde çalışır. Çok sayıda veya uzun süren işiniz varsa, bir TaskExecutor (ExecutorService) ile eşzamanlılığı kontrol etmelisiniz. Aksi halde işler kuyruğa girer ve gecikmeler oluşur.

Aşağıda ThreadPoolTaskScheduler ile yapılandırma örneği var. Bu sayede paralel iş yürütme, thread isimlendirme ve hata loglama gibi davranışlar kontrol edilebilir:

@Bean
public ThreadPoolTaskScheduler taskScheduler() {
    ThreadPoolTaskScheduler scheduler = new ThreadPoolTaskScheduler();
    scheduler.setPoolSize(10);
    scheduler.setThreadNamePrefix("sched-");
    scheduler.setAwaitTerminationSeconds(30);
    scheduler.setWaitForTasksToCompleteOnShutdown(true);
    return scheduler;
}

Bu yapılandırma ile aynı JVM içindeki işler daha öngörülebilir şekilde paralel çalışır. Ancak thread havuzunu çok büyük ayarlamak bellek ve CPU üzerinde olumsuz etki yapabilir; bu nedenle uygulama iş yükünü analiz edin ve sınırları test edin.

Cron İfadeleri, Zaman Dilimi ve Hassas Zamanlama

Cron ifadeleri karmaşık zamanlama kurallarını ifade etmek için elverişlidir. Spring'in Cron desteği esnektir ancak dikkat edilmesi gerekenler vardır: zaman dilimi (timezone) yönetimi ve yaz saati uygulaması (DST) gibi durumlar işleri beklenmedik şekilde kaydırabilir.

Örnek cron kullanımı:

@Scheduled(cron = "0 0/5 9-17 * * MON-FRI", zone = "Europe/Istanbul")
public void everyFiveMinutesDuringWorkHours() {
    // İş kodu
}

Burada zone parametresi ile zaman dilimini belirlemek önemlidir; aksi halde JVM'in varsayılan zaman dilimi kullanılır. Zamanlama tutarlılığı için UTC tercih edilip uygulama içi gösterimler UTC'den dönüştürülebilir.

Hata Yönetimi, Retry ve Gözlemlenebilirlik

Zamanlanmış işler hata verebilir; bu hataların loglanması ve gerektiğinde tekrar denenmesi (retry) önemlidir. Basit hatalarda @Scheduled metodu içindeki try/catch yeterli olabilir, daha gelişmiş gereksinimler için Spring Retry veya iş kuyruğu entegrasyonları kullanın.

Örnek basit retry yaklaşımı:

public void taskWithRetry() {
    int attempts = 0;
    while (attempts < 3) {
        try {
            // iş yapılır
            break;
        } catch (TransientException ex) {
            attempts++;
            Thread.sleep(1000 * attempts);
        }
    }
}

Ayrıca gözlemlenebilirlik için metrik ve izleme ekleyin: iş başarı/başarısızlık sayıları, çalışma süreleri ve gecikmeler Prometheus/Jaeger gibi araçlarla toplanmalı. Bu veriler, scheduler performansını değerlendirmek ve darboğazları tespit etmek için kritiktir.

Dağıtık Zamanlama: Sorunlar ve Modern Alternatifler

Monolitik uygulamalarda @Scheduled yeterliyken mikroservis mimarisine geçtiğinizde zamanlanmış işleri tek bir instance'a sabitlemek gerekebilir. Aksi halde aynı işi birden fazla instance tekrarlar ve veri tutarsızlığı oluşur. Bu sorunu çözmek için lider seçim (leader election), veritabanı kilidi (DB lock) veya merkezi zamanlayıcı kullanabilirsiniz.

Güncel ve önerilen yaklaşımlar:

  • Dağıtık kilit mekanizmaları: Redis RedLock, Zookeeper, Consul gibi araçlarla cron işlerinin tek bir instance tarafından yürütülmesini sağlayın.
  • Mesaj-kuyruğu tabanlı tetikleme: Kafka, RabbitMQ veya AWS SQS ile zamana bağlı tetiklemeyi ayrı bir koordinatör servis üzerinden yönetmek, işlerin yeniden denemenmesini ve paralel işleme kontrolünü kolaylaştırır.
  • Harici zamanlama servisleri: Kubernetes CronJob, AWS EventBridge veya Google Cloud Scheduler ile altyapının zamanlama sorumluluğunu devredin. Bu yaklaşımla JVM dışı scheduling sağlanır ve uygulama sadece gelen tetiklemeyi işler.

Örneğin Kubernetes CronJob, container seviyesinde bağımsız işler çalıştırır ve uygulama içindeki scheduler karmaşıklığını azaltır. Ancak iş idempotent olmalı ve ortak kaynaklara erişimde eşzamanlılık kontrollü olmalıdır.

Geçiş Rehberi: Monolitik @Scheduled'dan Dağıtık Çözüme

Eski sistemlerde yoğun şekilde @Scheduled kullanılmış olabilir. Modern dağıtık ortama geçiş için adım adım bir plan önerisi:

  • İşlerin sınıflandırılması: Kısa/uzun, idempotent/non-idempotent, kritik/non-kritik olarak ayırın.
  • İdempotent hale getirme: Tekrar çalıştırma güvenli değilse iş mantığını idempotent yapın (ör. dedup mekanizması, saga/transaction pattern).
  • Koordinasyon katmanı ekleme: Basit senaryolar için DB lock, daha karmaşıklar için Redis/Consul/Zookeeper veya mesaj-kuyruğu tabanlı tetikleme tercih edin.
  • Monitoring ve rollback planı: Yeni yöntemi Canary veya dark-launch ile küçük bir trafik üzerinde test edin, metrikleri takip ederek ilerleyin.

Unutmayın, dışarıya taşıma (Kubernetes CronJob vb.) uygulama kodunu basitleştirir fakat işletimsel bağımlılıkları artırır. Hangi yolu seçeceğiniz kullanım senaryonuza, SLA gereksinimlerinize ve operasyonel yeteneklerinize bağlıdır.

Sık Sorulan Sorular

@Scheduled ve ThreadPoolTaskScheduler birlikte kullanılmalı mı?

Eğer işlerin paralel çalışması, uzun süren görevlerin kuyruğa girmemesi veya farklı önceliklere göre yürütülmesi gerekiyorsa evet. Varsayılan single-thread davranışı küçük uygulamalar için yeterli olabilir, fakat üretimde genellikle özelleştirilmiş bir TaskScheduler tercih edilir.

Zaman dilimi ve DST sorunlarını nasıl önlerim?

Cron ifadelerinde explicit olarak zone parametresi kullanın veya tüm zamanlamaları UTC olarak belirleyip gösterimleri uygulama katmanında yerel zamana çevirin. İşlerin taşınabilirliği ve tutarlılığı için UTC çoğunlukla daha güvenlidir.

Dağıtık ortamda işi tek bir instance'a nasıl garantilerim?

Redis/DB tabanlı kilitleme, lider seçim (leader election) veya dışardan tetikleme (Kubernetes CronJob, mesaj kuyruğu) kullanabilirsiniz. Seçim, uygulamanızın ihtiyaçlarına ve mevcut altyapınıza göre değişir; kritik işleri DB kilidi ile garanti altına almak yaygın bir yaklaşımdır.

Sonuç

Spring’in scheduling yetenekleri küçük ve orta ölçekli uygulamalar için yeterli ve pratiktir. Ancak mikroservisler ve dağıtık ortamlar için planlama, koordinasyon ve gözlemlenebilirlik katmanlarının kurgulanması gerekir. TaskExecutor ile thread yönetimi, retry ve metrikler ile sağlamlaştırılmış bir yapı, sisteminizi daha dayanıklı yapar. Dağıtık zamanlama ihtiyaçlarında Redis, Zookeeper, mesaj kuyruğu veya bulut tabanlı zamanlayıcılar gibi modern alternatifleri değerlendirerek güvenli ve ölçeklenebilir bir geçiş planı uygulayın.

Eğitim bağlantısı EKLEME

📚 Bu konuyu Opendart Akademi'de öğrenin: Spring Boot eğitimi · Java Spring Boot eğitimi hakkında · YouTube video dersleri · Abone ol