Go (Golang), statik olarak derlenen tekil binary çıktısı, son derece düşük bellek ayak izi ve hafif iş parçacıkları (goroutine) sayesinde arka plan operasyonları ve cron job mimarileri için sektördeki en verimli dillerden biridir. Yüksek eşzamanlılık gerektiren mikroservislerde veya kaynak kısıtı bulunan sunucularda Go ile periyodik görev koşturmak hem işlemci maliyetini düşürür hem de operasyonel bağımlılıkları ortadan kaldırır.
Go ekosisteminde zamanlanmış görev geliştirmek için iki ana yol izlenir: Ya derlenen binary dosyayı işletim sisteminin crontab servisine bağlamak ya da uygulama içine gömülü bir zamanlayıcı kütüphanesi dahil etmektir. Bu alandaki en popüler ve olgun çözüm şüphesiz github.com/robfig/cron/v3 kütüphanesidir. Robfig cron; standart cron sözdizimini, saniye bazlı hassas tetiklemeyi, görev çakışma kilitlerini ve saat dilimi desteğini native Go mimarisiyle birleştirir.
Bu rehberde, Go projelerinizde robfig/cron v3 kurulumunu, 5 alanlı ve saniye destekli yapılandırmaları, görev çakışmalarını engelleyen SkipIfStillRunning sarmalayıcısını, sinyal dinleme tabanlı kontrollü kapanma (graceful shutdown) adımlarını ve minimal Docker konteyner dağıtımını inceleyeceğiz. Ayrıca sunucu içi scheduler yerine HTTP webhook tetikleme alternatifini adım adım ele alacağız.
Go (Golang) Ekosisteminde Cron Mimarisi: Sistem Cron vs In-App Scheduler
Go projelerinde zamanlanmış görev planlarken altyapı gereksinimlerinize göre iki temel mimari model arasında karar vermelisiniz:
- Sistem Cron ile Bağımsız Binary Çalıştırma: Go projenizi
go build -o /usr/local/bin/veri-aktarim ./cmd/aktarimşeklinde derleyip Linux crontab dosyasına eklersiniz. Bu modelde Go uygulaması görev süresince çalışır ve iş bittiğinde bellekten tamamen silinir. Avantajı sıfır sürekli RAM tüketimidir; dezavantajı ise her tetiklemede veritabanı bağlantı havuzlarının sıfırdan kurulması ve saniyelik hassasiyet sunulamamasıdır. Sistem düzeyinde yapılandırma için Linux cron job kılavuzumuzdaki adımları uygulayabilirsiniz. - Uygulama İçi (In-App) Zamanlayıcı (robfig/cron): Go uygulamanız bir daemon (arka plan servisi) olarak 7/24 çalışır. Görevler uygulama içinde birer goroutine olarak yürütülür. Veritabanı bağlantıları açık tutulur, paylaşımlı önbelleğe (cache) erişilir ve saniye çözünürlüğünde zamanlama yapılabilir. Ancak sürecin kesintisiz çalışması için systemd veya konteyner yönetim araçlarına ihtiyaç duyulur.
Bu "sistem zamanlayıcısı mı, uygulama içi zamanlayıcı mı" ikilemi Go'ya özgü değildir: Java tarafında Spring @Scheduled ile zamanlanmış görev tanımlarken de aynı süreklilik, çakışma ve saat dilimi kararları verilir; Node.js'te ise node-cron ile uygulama içi zamanlama yazısında her iki modelin seçim kriterleri ayrıca ele alınır.
robfig/cron/v3 Kurulumu ve Temel Yapılandırma
Go modülünüze kütüphaneyi dahil etmek için terminalde şu komutu çalıştırın:
go get github.com/robfig/cron/v3
Kütüphanenin v3 sürümünde dikkat edilmesi gereken en önemli yenilik, varsayılan olarak standart 5 alanlı Linux sözdizimini (dakika, saat, gün, ay, haftanın günü) benimsemesidir. Önceki v1 ve v2 sürümlerinde varsayılan olan saniye alanı, v3 ile birlikte opsiyonel hale getirilmiştir.
Standart 5 Haneli Cron vs Saniye Hassasiyetli (cron.WithSeconds()) Zamanlama
Eğer klasik Linux crontab sözdizimiyle çalışıyorsanız, standart kurucu fonksiyonu kullanabilirsiniz. Zaman formatının ayrıntıları için crontab sözdizimi sayfamıza göz atabilirsiniz. Zaman dilimi belirtmek için cron.WithLocation seçeneği eklenmelidir:
package main
import (
"log"
"time"
"github.com/robfig/cron/v3"
)
func main() {
// İstanbul saat dilimiyle 5 alanlı zamanlayıcı
loc, err := time.LoadLocation("Europe/Istanbul")
if err != nil {
log.Fatal(err)
}
c := cron.New(cron.WithLocation(loc))
// Her gün saat 04:30'da çalışır
c.AddFunc("30 4 * * *", func() {
log.Println("Günlük veri temizliği başladı")
})
c.Start()
defer c.Stop()
// Servisi ayakta tut
select {}
}
Finansal işlemler, anlık telemetri veya kuyruk izleme gibi saniye düzeyinde hassasiyet gerektiren görevler için zamanlayıcıyı cron.WithSeconds() opsiyonu ile başlatmanız gerekir. Bu durumda cron ifadeleri 6 alana çıkar:
// 6 alanlı (saniye dahil) zamanlayıcı oluşturma
c := cron.New(cron.WithSeconds())
// Her 15 saniyede bir tetiklenir (saniye dakika saat gün ay haftanın_günü)
c.AddFunc("*/15 * * * * *", func() {
log.Println("Her 15 saniyede bir sensör verisi okunuyor")
})
Görevlerin Çakışmasını Engelleme (cron.SkipIfStillRunning)
Varsayılan olarak robfig/cron, zamanı gelen her görevi yeni bir goroutine içinde başlatır. Eğer 30 saniyede bir çalışan bir veri senkronizasyon göreviniz ağ gecikmesi nedeniyle 50 saniye sürerse, önceki iş henüz bitmeden ikinci bir iş başlar. Bu durum veritabanında deadlock'lara ve kaynak tükenmesine yol açar.
Bu sorunu çözmek için kütüphane cron.SkipIfStillRunning adında bir JobWrapper sunar. Bu sarmalayıcı, önceki çalıştırma tamamlanmamışsa bir sonraki periyodu sessizce atlar. Aynı koruma diğer dillerde de aranmalıdır; .NET tarafında Hangfire veya Quartz ile zamanlanmış görev kurarken eşdeğer eşzamanlılık kilidi açıkça yapılandırılmalıdır:
// Çakışmayı engelleyen sarmalayıcı zinciri
chain := cron.NewChain(
cron.SkipIfStillRunning(cron.DefaultLogger),
)
// Görevi zincirle sarmalayarak ekleme
c.AddJob("@every 10s", chain.Then(cron.FuncJob(func() {
log.Println("Ağır hesaplama başladı...")
time.Sleep(15 * time.Second) // 10 saniyeyi aşan iş
log.Println("Hesaplama tamamlandı.")
})))
Graceful Shutdown ve Goroutine Yönetimi
Sürekli çalışan arka plan uygulamalarında en büyük risk, servis yeniden başlatılırken veya Kubernetes pod'u kapatılırken o an çalışan işlerin yarıda kesilip veri bozulmasına (corruption) yol açmasıdır. Go'da işletim sistemi sinyallerini (SIGINT, SIGTERM) dinleyerek kontrollü kapanma (graceful shutdown) sağlamak şarttır.
robfig/cron v3'te c.Stop() metodu bir context.Context nesnesi döndürür. Bu context, o an yürütülmekte olan tüm görevler bittiğinde kapanır:
// İşletim sistemi sinyallerini dinle
sigChan := make(chan os.Signal, 1)
signal.Notify(sigChan, os.Interrupt, syscall.SIGTERM)
// Kapatma sinyali gelene kadar bekle
<-sigChan
log.Println("Kapatma sinyali alındı. Görevlerin tamamlanması bekleniyor...")
// Zamanlayıcıyı durdur ve aktif işlerin bitmesini bekle
ctx := c.Stop()
select {
case <-ctx.Done():
log.Println("Tüm cron görevleri temiz bir şekilde tamamlandı.")
case <-time.After(30 * time.Second):
log.Println("Bekleme süresi doldu; çalışan görevler c.Stop() tarafından iptal edilmez.")
}
Gerçek Kod Örnekleri
Aşağıda üretim ortamında kullanıma hazır, tam teşekküllü bir daemon örneği yer almaktadır. Bu uygulama saniye hassasiyetli sağlık kontrolü yapar, çakışmaları engeller ve SIGTERM sinyalinde temiz bir şekilde durur:
package main
import (
"context"
"database/sql"
"log"
"os"
"os/signal"
"syscall"
"time"
"github.com/robfig/cron/v3"
)
type SaglikServisi struct {
db *sql.DB
}
func (s *SaglikServisi) KontrolEt() {
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
log.Println("[INFO] Veritabanı ping kontrolü yapılıyor...")
// Bağlantı havuzu yapılandırıldıysa gerçek ping kontrolü yap
if s.db == nil {
log.Println("[HATA] Veritabanı bağlantısı yapılandırılmamış.")
return
}
if err := s.db.PingContext(ctx); err != nil {
log.Printf("[HATA] Veritabanı ping başarısız: %v", err)
return
}
log.Println("[OK] Veritabanı sağlıklı yanıt verdi.")
}
func main() {
log.Println("Go Cron Daemon başlatılıyor...")
servis := &SaglikServisi{}
// Saniye desteği ve loglama ile başlat
c := cron.New(
cron.WithSeconds(),
cron.WithChain(cron.SkipIfStillRunning(cron.DefaultLogger)),
)
// Her 15 saniyede bir veritabanı sağlık kontrolü
_, err := c.AddFunc("*/15 * * * * *", servis.KontrolEt)
if err != nil {
log.Fatalf("Görev eklenemedi: %v", err)
}
c.Start()
// Graceful shutdown mekanizması
sigChan := make(chan os.Signal, 1)
signal.Notify(sigChan, os.Interrupt, syscall.SIGTERM)
<-sigChan
log.Println("Kapatma sinyali alındı...")
ctx := c.Stop()
<-ctx.Done()
log.Println("Tüm arka plan goroutine'leri sonlandı. Çıkış yapılıyor.")
}
Minimal Docker Konteyneri ile Dağıtım (Scratch / Alpine)
Go uygulamanızı Docker üzerinde çalıştırmak için çok aşamalı (multi-stage) derleme kullanarak boyutu uygulamanın bağımlılıklarına bağlı olan küçük imajlar üretebilirsiniz. Konteyner ortamında cron yönetimi hakkında daha fazla bilgiye Docker cron job rehberimizden ulaşabilirsiniz:
# Multi-stage Dockerfile
FROM golang:1.22-alpine AS builder
WORKDIR /app
COPY go.mod go.sum ./
RUN go mod download
COPY . .
RUN CGO_ENABLED=0 GOOS=linux go build -ldflags="-s -w" -o cronapp .
FROM alpine:3.19
RUN apk --no-cache add ca-certificates tzdata
WORKDIR /app
COPY --from=builder /app/cronapp .
ENV TZ=Europe/Istanbul
ENTRYPOINT ["/app/cronapp"]
Sık Karşılaşılan Hatalar ve Çözümleri
Go ile zamanlanmış görev geliştirirken geliştiricilerin en sık karşılaştığı dört kritik hata şunlardır:
expected exactly 5 fields, found 6 Hatası
robfig/cron kütüphanesinde cron.New() varsayılan olarak 5 alan beklerken 6 veya 7 haneli ifade girildiğinde alan sayısını belirten bir ayrıştırma hatası alınır. Saniye alanını kullanmak istiyorsanız nesneyi mutlaka cron.New(cron.WithSeconds()) ile başlatmalısınız.
Go Programının Çalışıp Hemen Kapanması (Process Exits Immediately)
c.Start() fonksiyonu zamanlayıcıyı arka planda çalışan ayrı bir goroutine olarak başlatır ve ana akışı bloke etmez. Eğer main() fonksiyonu içinde bir sinyal dinleyici (<-sigChan) veya select {} bloğu koymazsanız program milisaniyeler içinde sonlanır. Çözüm olarak ana akışı sinyal kanalıyla bekletmelisiniz.
Goroutine Leak ve Bellek Sızıntısı
Cron tarafından tetiklenen fonksiyonlar içinde zaman aşımı (timeout) belirlenmemiş HTTP istekleri veya sonsuz döngüde kalan kanal okumaları varsa, her tetiklemede yeni bir goroutine bellekte asılı kalır. Binlerce asılı goroutine sunucu RAM'ini tüketir. Çözüm olarak her görev içinde context.WithTimeout kullanmalı ve kaynakları defer cancel() ile temizlemelisiniz.
Uzun Süren Görevlerin Üst Üste Binmesi
Beklenmeyen ağ gecikmeleri nedeniyle süresi uzayan bir görevin yeni periyodu başladığında iki iş parçacığı aynı veriyi işlemeye çalışır. Bu durumu engellemek için cron.SkipIfStillRunning sarmalayıcısını yapılandırma zincirine eklemelisiniz.
cronjob.com.tr ile HTTP Tabanlı Alternatif Kurulum (Go HTTP Handler Tetikleme)
Özellikle Kubernetes pod'larında veya AWS Lambda/Google Cloud Run gibi sunucusuz (serverless) mimarilerde Go uygulamasının içinde 7/24 uyanık kalan bir cron kütüphanesi çalıştırmak kaynak israfına ve ölçekleme anında çoklu mükerrer tetiklemelere yol açabilir. Bunun yerine Go servisinizde hafif bir HTTP uç noktası açıp dışarıdan periyodik istek almak en temiz mimaridir.
Go'nun yerleşik net/http kütüphanesi ile güvenli bir webhook endpoint'i oluşturabilirsiniz:
package main
import (
"crypto/subtle"
"log"
"net/http"
"os"
)
func cronHandler(w http.ResponseWriter, r *http.Request) {
if r.Method != http.MethodPost {
http.Error(w, "Metot desteklenmiyor", http.StatusMethodNotAllowed)
return
}
// Gizli Bearer token doğrulaması
secret := os.Getenv("CRON_SECRET_TOKEN")
if secret == "" {
http.Error(w, "Cron anahtarı yapılandırılmamış", http.StatusServiceUnavailable)
return
}
beklenenToken := "Bearer " + secret
gelenToken := r.Header.Get("Authorization")
if subtle.ConstantTimeCompare([]byte(gelenToken), []byte(beklenenToken)) != 1 {
http.Error(w, "Yetkisiz erişim", http.StatusUnauthorized)
return
}
log.Println("Harici cron tetiklendi: Veri senkronizasyonu başlatılıyor...")
// İşlemi yürüt
w.WriteHeader(http.StatusOK)
w.Write([]byte("Gorev basariyla tamamlandi"))
}
func main() {
http.HandleFunc("/internal/cron/sync", cronHandler)
log.Println("HTTP Cron dinleyici 8080 portunda aktif...")
log.Fatal(http.ListenAndServe(":8080", nil))
}
cronjob.com.tr yönetim paneline giriş yaparak görevinizi şu parametrelerle tanımlayabilirsiniz:
- Hedef URL:
https://api.siteniz.com/internal/cron/sync - HTTP Metodu:
POST - Özel Header:
Authorization: Bearer gizli-token-degeri - Zamanlama: Hazır zaman aralıkları veya 5 alanlı cron ifadesi (örneğin
0 3 * * *). Saat dilimiEurope/Istanbulolarak seçilebilir. - Yanıt Bekleme (Timeout): Cron ifadeli işlerde sistem 290 saniyeye kadar yanıt bekler. 1 dakikalık işlerde 55 saniye, 2 dakikalık işlerde 115 saniyedir.
- Kesinti İzleme: Uç noktanız HTTP 200-399 dışında bir kod dönerse veya sunucuya erişilemezse, 3 ardışık hata sonrasında e-posta ile bildirim alırsınız.
Sunucunuzda ayrı bir zamanlayıcı daemon çalıştırmadan HTTP uç noktalarıyla işlerinizi nasıl otomatikleştirebileceğinizi cron alternatifleri rehberimizden detaylıca inceleyebilirsiniz.
Sıkça Sorulan Sorular (SSS)
robfig/cron v3 sürümünde saniye desteği var mı?
Evet, ancak robfig/cron v3 sürümünde varsayılan sözdizimi standart 5 alandır. Saniye desteğini etkinleştirmek için cron nesnesi cron.New(cron.WithSeconds()) seçeneği ile açıkça başlatılmalıdır.
Go cron uygulamasını arka planda nasıl çalıştırırım?
Go ile derlenen tekil binary dosyasını Linux üzerinde bir systemd servisi (daemon) olarak yapılandırabilir ya da scratch veya alpine tabanlı minimal bir Docker konteyneri içinde arka planda kesintisiz çalıştırabilirsiniz.
Go'da time.Ticker yerine neden cron kütüphanesi kullanılmalı?
time.Ticker yalnızca belirli sabit aralıklarla (örneğin 10 saniyede bir) tetikleme yapar. Her gece saat 04:00'te veya her ayın ilk günü gibi takvim ve saat dilimi bağımlı kuralları time.Ticker ile yönetmek çok karmaşık olduğundan cron kütüphanesi tercih edilmelidir.
SkipIfStillRunning ne yapar?
SkipIfStillRunning bir JobWrapper işlevidir; uzun süren bir önceki görev henüz tamamlanmamışsa, bir sonraki periyotta başlayacak yeni görevi atlayarak aynı iş parçacığının üst üste binmesini ve sunucu kaynaklarının kilitlenmesini engeller.
Hemen Ücretsiz Cron Job Oluşturun
Go servisinizdeki /internal/cron/sync uç noktasını Authorization header'ıyla koruyup cronjob.com.tr panelinden cron ifadesiyle zamanlayın; ardışık hatalarda e-posta bildirimi, her çalışmanın durum kodu ve süresi 7 günlük kayıtta. Ücretsiz; kayıtta kredi kartı gerekmez.