Her projede bir gün aynı soru gelir: gece 03:00'te rapor üretmek, her beş dakikada bir ödeme durumlarını kontrol etmek, kullanıcı kayıt olunca hoş geldin e-postası yollamak. Bunların hepsi "arka plan işi" başlığı altında toplanır ama çözümleri aynı değildir. Cron mu job queue mu sorusunun tek bir doğru cevabı yok; doğru cevap, işi tetikleyen şeyin zaman mı yoksa olay mı olduğuna bağlı.
Bu rehberde iki yaklaşımın gerçekte neyi çözdüğünü, birinin diğerinin yerine geçmeye çalıştığında ortaya çıkan klasik sorunları anlatacağım. Cron'un neden hâlâ vazgeçilmez olduğunu ve nerede duvara tosladığını, kuyruk tabanlı bir sistemin hangi problemleri kökünden çözdüğünü, ikisini birlikte kullanan hibrit deseni ve üretimde en sık düşülen tuzakları — özellikle üst üste binen cron çalıştırmalarını — örneklerle konuşacağız.
İki Yaklaşımın Temel Farkı: Zaman mı Olay mı#
Cron bir takvimdir. Ona "her gün saat üçte şu komutu çalıştır" dersin, o da tam olarak bunu yapar; komutun ne kadar sürdüğü, başarılı olup olmadığı, aynı anda ikinci bir kopyasının çalışıp çalışmadığı umurunda değildir. Cron'un dünyası tek boyutludur: saat geldi mi, çalıştır.
Job queue ise bir iş listesidir. Bir yerde bir olay olur (kullanıcı kaydolur, sipariş verilir, dosya yüklenir), uygulaman bu işi kuyruğa yazar ve hemen kullanıcıya cevap döner. Arka planda bekleyen işçiler (worker) kuyruktan iş çeker, yapar ve onaylar. Burada zaman değil, işin ortaya çıkması tetikleyicidir. İşin ne zaman biteceği garanti değildir ama işin kaybolmayacağı garantidir; hata alırsa yeniden denenir.
| Kriter | Cron | Job Queue |
|---|---|---|
| Tetikleyici | Saat / takvim | Uygulama olayı |
| Gecikme | Bir sonraki tetiğe kadar | Saniyeler içinde |
| Hata durumu | Kayıp (yakalamazsan) | Yeniden denenir |
| Eşzamanlılık | Kontrolsüz, üst üste biner | İşçi sayısıyla kontrol edilir |
| Ölçekleme | Yeni makineye kopyalanamaz | İşçi ekle, biter |
| Görünürlük | Log dosyası | Kuyruk derinliği, başarısız iş sayısı |
| Kurulum yükü | Sıfır | Broker + işçi süreçleri |
Bu tabloyu okurken şu ayrımı akılda tutmak yeterli: "her gün", "her pazartesi", "ayın birinde" ile başlayan cümleler cron işidir. "...olduğunda", "...yüklendiğinde", "...kaydolduğunda" ile başlayan cümleler kuyruk işidir. Cron ile temelden ilgilenmediysen Linux cron görevleri yazısı sözdizimini ve zamanlama alanlarını ayrıntılı biçimde anlatıyor.
Cron Nerede Doğru, Nerede Yetersiz Kalır#
Cron'un en büyük avantajı hiçbir şey kurmuyor olmandır. Sistemde zaten vardır, tek satırlık bir kayıtla iş görür ve bir bağımlılık eklemez. Günlük yedek alma, log döndürme, sertifika yenileme kontrolü, geçici dosya temizliği gibi işlerde cron'un yerine geçebilecek hiçbir şey yoktur ve kuyruk kurmak gereksiz bir karmaşıklıktır.
# crontab -e ile açılan dosyaya:
# Her gece 03:15'te veritabanı yedeği al, çıktıyı logla
15 3 * * * /usr/local/bin/yedek-al.sh >> /var/log/yedek.log 2>&1
# Her 10 dakikada bir sertifika yenileme kontrolü
*/10 * * * * /usr/bin/certbot renew --quiet
Cron'un duvara tostladığı üç nokta vardır. Birincisi üst üste binme: */5 ile beş dakikada bir çalışan bir görev yedi dakika sürerse, ikinci kopya birincisi bitmeden başlar. Kısa sürede beş, on, yirmi kopya aynı anda çalışır, sunucu tıkanır ve veritabanında aynı satırı işleyen paralel süreçler birbirini bozar. Çözüm flock ile bir kilit dosyası kullanmaktır:
# -n: kilit alınamazsa bekleme, sessizce çık
*/5 * * * * /usr/bin/flock -n /tmp/rapor.lock /usr/local/bin/rapor-uret.sh
İkincisi hata yönetimidir. Cron başarısız bir komutu yeniden denemez; komut hata verirse iş o gün yapılmamış olur ve MAILTO ayarlamadıysan kimsenin haberi olmaz. Üçüncüsü ölçeklenememesidir: aynı crontab'ı iki sunucuya koyarsan iş iki kez çalışır, tek sunucuda bırakırsan o sunucu düştüğünde iş hiç çalışmaz. Cron'un çalışmadığı durumları teşhis ederken cron çalışmıyor, neden yazısındaki kontrol listesi zaman kazandırır.
Job Queue Neyi Kökünden Çözer#
Kuyruk tabanlı bir sistemde iş, bir mesaj olarak broker'a yazılır ve orada durur. Bu tek fark, cron'un üç zayıflığını birden ortadan kaldırır. Eşzamanlılık kontrol altındadır: kaç işçi çalıştırırsan o kadar paralellik olur, bir işçi meşgulken iş kuyrukta bekler. Hata yönetimi yerleşiktir: işçi mesajı onaylamazsa mesaj kuyruğa geri döner ve yeniden denenir. Ölçekleme trivialdir: yeni bir sunucuda bir işçi daha başlatırsın, aynı kuyruğa bağlanır ve yükü paylaşır.
Tipik akış şöyledir. Kullanıcı formu gönderir, uygulama işi kuyruğa yazar ve HTTP cevabını 40 milisaniyede döner:
# Üretici tarafı — uygulama içinde, isteğin akışında
def kullanici_kaydet(veri):
kullanici = db.kaydet(veri)
kuyruk.yayinla("eposta.hosgeldin", {
"kullanici_id": kullanici.id,
"eposta": kullanici.eposta,
})
return {"durum": "ok"} # e-posta gitmesini beklemez
İşçi tarafı ayrı bir süreçtir ve tek işi kuyruktan çekip yapmaktır:
# Tüketici tarafı — ayrı bir süreç / konteyner
def isle(mesaj):
try:
eposta_gonder(mesaj["eposta"], sablon="hosgeldin")
mesaj.ack() # başarılı: kuyruktan düş
except GeciciHata:
mesaj.nack(requeue=True) # tekrar dene
except KaliciHata:
mesaj.reject() # ölü mektup kuyruğuna gitsin
Buradaki ack / nack ayrımı, kuyruk sistemlerinin tüm değerini taşıyan yerdir. İş yapılmadan onaylanmazsa mesaj kaybolmaz. Buna karşılık dikkat edilmesi gereken bir gerçek var: yeniden deneme sınırsız olursa, bozuk bir mesaj sonsuza kadar döner ve kuyruğu tıkar. Bu yüzden her ciddi kurulumda azami deneme sayısı ve bir ölü mektup kuyruğu (dead letter queue) tanımlanır. Bu mekanizmanın pratikte nasıl kurulduğunu RabbitMQ kurulumu yazısında adım adım gösteriyorum; çok daha hafif bir alternatif arıyorsan NATS ile hafif mesajlaşma yazısındaki JetStream kurulumu da aynı işi görür.
Hibrit Desen: Cron Tetikler, Kuyruk Yapar#
Uygulamada en sağlam çözüm çoğu zaman ikisini birlikte kullanmaktır ve bu desen hak ettiği kadar bilinmiyor. Fikir basit: cron işi yapmaz, işi kuyruğa atar. Yani cron yalnızca zamanlayıcı rolünde kalır, ağır işi kuyruk ve işçiler üstlenir.
Diyelim ki her gece 20.000 müşteriye fatura hatırlatma e-postası göndereceksin. Bunu doğrudan cron'da yaparsan tek süreç saatlerce çalışır, ortada bir hata alırsa kaçıncı müşteride kaldığını bilemezsin ve baştan başlarsın. Hibrit desende cron tek bir küçük komut çalıştırır:
# Her gece 02:00'de: gönderilecekleri bul ve kuyruğa yaz (saniyeler sürer)
0 2 * * * /usr/bin/flock -n /tmp/hatirlatma.lock \
/usr/local/bin/php /var/www/app/bin/hatirlatma-kuyrukla.php
Bu komut 20.000 satırı okur, 20.000 mesajı kuyruğa yazar ve biter. Ardından altı işçi bu kuyruğu tüketir; biri patlarsa yalnızca o mesaj yeniden denenir, diğer 19.999 iş etkilenmez. İşi hızlandırmak istersen işçi sayısını artırırsın, yavaşlatmak istersen azaltırsın; cron satırına hiç dokunmazsın.
Bu desenin üç somut kazancı vardır:
- İlerleme kaybolmaz. İş parça parça takip edilir, yarıda kalan tek bir mesajdır.
- Hız ayarlanabilir. E-posta sağlayıcın dakikada 300 mesaj kabul ediyorsa, işçi sayısını ona göre kısarsın; cron'da böyle bir kontrol yoktur.
- Gözlemlenebilir olur. Kuyruk derinliğine bakarak işin ne kadarının bittiğini anlık görürsün.
Doğru Seçimi Yapmak İçin Karar Listesi#
Somut bir iş önünde kaldığında şu soruları sırayla sor. Cevapların çoğunluğu hangi sütunu işaret ediyorsa oradan devam et.
| Soru | Cron | Job Queue |
|---|---|---|
| İşi zaman mı tetikliyor? | Evet | Hayır, olay tetikliyor |
| Kullanıcı sonucu bekliyor mu? | Hayır | Hayır ama hızlı cevap gerekiyor |
| Başarısızlıkta yeniden denenmeli mi? | Şart değil | Şart |
| Aynı anda birden çok kopya sorun mu? | Sorun (kilit gerekir) | Kontrol edilebilir |
| Yükü birden çok sunucuya yaymalı mı? | Zor | Doğal |
| İş sayısı binlerce mi? | Riskli | Uygun |
| Yeni altyapı kurmaya değer mi? | Gerek yok | Broker gerekir |
Pratik bir kural: haftada birkaç kez çalışan, birkaç saniye süren, hata alırsa ertesi gün tekrar denenmesi yeterli olan işler cron'da kalsın. Kullanıcı etkileşimiyle tetiklenen, sayısı öngörülemeyen, gecikmesi kullanıcıyı rahatsız eden işler kuyruğa taşınsın. Arada kalan durumlarda hibrit deseni seç; cron'u kaldırmak zorunda değilsin, sadece rolünü küçültüyorsun.
Kuyruğa geçmenin bir maliyeti olduğunu da unutma: bir broker kurup ayakta tutman, işçi süreçlerini yönetmen ve bunları izlemen gerekir. İşçilerin çökünce kendiliğinden ayağa kalkması için systemd servis dosyası yazma rehberindeki Restart=always yapılandırması ya da uygulama sağlık kontrolü ve otomatik yeniden başlatma yazısındaki desenler doğrudan işine yarar.
Sık Yapılan Hatalar ve Tuzaklar#
En yaygın hata, cron görevine kilit koymamaktır. Görev normalde 30 saniye sürerken bir gün veritabanı yavaşlar ve 6 dakikaya çıkar; */5 ile çalışan iş üst üste binmeye başlar ve sunucu birkaç saat içinde tıkanır. flock -n tek satırlık bir eklemedir ve bu sınıf sorunu tamamen kapatır.
İkincisi, cron çıktısını hiçbir yere yazmamaktır. >> /var/log/gorev.log 2>&1 eklemezsen çıktı yerel posta kutusuna gider ve pratikte kaybolur; iş sessizce başarısız olur. Bir görevin gerçekten çalışıp çalışmadığını anlamanın en ucuz yolu, sonunda bir "başarılı" satırı yazdırmak ve o satırın zaman damgasını izlemektir.
Üçüncüsü, kuyruk işçisinde hatayı yutmaktır. try bloğunun içinde hatayı yakalayıp ack göndermek, işin yapılmadığı hâlde tamamlanmış sayılmasına yol açar; bu, kuyruğun sana verdiği tek garantiyi elinle iptal etmektir. Kalıcı hatayı ölü mektup kuyruğuna yolla, geçici hatayı yeniden denenmek üzere geri bırak.
Dördüncüsü, işçi süreçlerini elle başlatmaktır. nohup ... & ile başlatılan bir işçi, sunucu yeniden başlayınca geri gelmez ve kimse fark etmeyene kadar kuyruk sessizce büyür. İşçiler mutlaka bir süreç yöneticisi altında çalışmalıdır. Beşincisi ise kuyruk derinliğini izlememektir: bekleyen iş sayısı için bir eşik alarmı kurmadıysan, işçilerin durduğunu ancak müşteri şikâyetiyle öğrenirsin.
Son bir not: cron çalıştıran kullanıcının ortam değişkenleri, oturum açtığın kabuktakiyle aynı değildir. PATH çoğu zaman çok kısıtlıdır; bu yüzden komutları tam yol ile yazmak (/usr/bin/php, /usr/local/bin/script.sh) sonradan saatlerce süren "elimde çalışıyor ama cron'da çalışmıyor" araştırmasını baştan engeller. Paylaşımlı bir ortamda çalışıyorsan cron kayıtlarını panelden yönetmek daha güvenlidir; cPanel'de cron job kurma yazısı bu akışı anlatıyor.
Sıkça Sorulan Sorular#
Cron yerine job queue kullanmalı mıyım#
İşi tetikleyen şey saat ise cron doğru araçtır ve değiştirmeye gerek yoktur; günlük yedek, log temizliği, sertifika yenileme gibi işlerde kuyruk kurmak gereksiz karmaşıklık getirir. İşi tetikleyen şey bir kullanıcı eylemiyse, iş sayısı öngörülemiyorsa ve başarısızlıkta yeniden denenmesi gerekiyorsa kuyruk kullanmalısın. Çoğu projede doğru cevap ikisini birlikte kullanmaktır.
Cron görevi üst üste binmesin diye ne yapmalıyım#
En pratik çözüm flock ile bir kilit dosyası kullanmaktır: flock -n /tmp/gorev.lock /yol/komut.sh biçiminde yazdığında, önceki çalıştırma hâlâ sürüyorsa yeni kopya sessizce çıkar. Uygulama seviyesinde bir kilit tablosu ya da Redis anahtarı da aynı işi görür ama flock hiçbir bağımlılık gerektirmediği için en ucuz yoldur. Kilit dosyasını /tmp yerine kalıcı bir dizinde tutmak, yeniden başlatmalarda daha öngörülebilir davranır.
Kuyruk işçisi çökerse iş kaybolur mu#
Doğru yapılandırılmış bir kuyrukta kaybolmaz. İşçi mesajı işlemeye başlar ama onaylamadan (ack) çökerse, broker bağlantının koptuğunu görür ve mesajı kuyruğa geri koyar; başka bir işçi devralır. Kaybın tek yolu, işçinin işi yapmadan mesajı onaylamasıdır. Bu yüzden onayı her zaman işin başarıyla bittiği noktadan sonra göndermeli, hata durumunda ise mesajı geri bırakmalısın.
Kaç işçi çalıştırmalıyım#
İşin doğası belirler. İş CPU yoğunsa (görüntü işleme, sıkıştırma) işçi sayısını çekirdek sayısının biraz üstünde tutmak yeterlidir. İş beklemeye dayalıysa (dış API çağrısı, e-posta gönderimi) çok daha fazla işçi çalıştırabilirsin, çünkü zamanın çoğu boşta geçer. Dış bir servise gidiyorsan o servisin hız sınırı asıl tavanını belirler; işçi sayısını ona göre kısmak, sınırı aşıp engellenmekten iyidir.
Cron çıktısını nereye yazmalıyım#
Her cron satırının sonuna >> /var/log/gorev-adi.log 2>&1 eklemek iyi bir varsayılandır; hem standart çıktıyı hem hata çıktısını aynı dosyaya toplar. Log dosyalarının sonsuza kadar büyümemesi için logrotate yapılandırması eklemeyi unutma. Alternatif olarak MAILTO değişkenini gerçek bir adrese ayarlayıp çıktıyı e-posta ile alabilirsin, ancak sık çalışan görevlerde bu kısa sürede gürültüye dönüşür.
Zamanlanmış görev için hangi kuyruk sistemini seçmeliyim#
Ölçek küçük ve orta düzeydeyse RabbitMQ olgun ve dokümantasyonu güçlü bir seçimdir; öncelikli kuyruk, gecikmeli teslim ve ölü mektup gibi ihtiyaçları hazır karşılar. Çok hafif bir çözüm istiyorsan NATS JetStream tek ikili dosyayla aynı işi görür ve çok daha az kaynak tüketir. Zaten Redis kullanıyorsan, Redis tabanlı hafif kuyruk kütüphaneleri de yeni bir servis kurmadan başlamanı sağlar.
Kapanış#
Cron ile job queue rakip değil, farklı sorulara verilen cevaplardır. Aklında kalması gereken dört şey var: zaman tetikliyorsa cron, olay tetikliyorsa kuyruk; her cron satırına kilit ve log ekle; kuyruk işçisinde onayı asla işten önce gönderme; ve büyük toplu işlerde cron'u yalnızca tetikleyici olarak kullanıp asıl yükü kuyruğa devret. Bu dört alışkanlık, arka plan işlerinden kaynaklanan sorunların çoğunu baştan siler.
Arka plan işleri sunucunun sessiz yüküdür; CPU ve bellek payını hesaba katmadan planlanırsa asıl uygulamanın hızını düşürür. Kendi işçilerini rahatça çalıştırabileceğin kaynak için VDS ve bulut sunucu paketlerimize göz atabilir, zamanlanmış görevlerin ve kuyrukların kurulumunu bize bırakmak isterseniz sunucu yönetimi hizmetimizden yararlanabilirsiniz. Toplu e-posta gönderimini kuyruğa taşıyacaksanız e-posta pazarlama ve SMTP sunucu çözümlerimiz teslim tarafını üstlenir.