Laravel

Laravel Queue Sistemi: Job, Worker ve Hata Yönetimi

Laravel Queue sistemi, kullanıcıyı bekletmemesi gereken e-posta, dosya işleme, rapor üretme ve dış API çağrısı gibi işleri arka planda yürütür. Ancak yalnızca bir job oluşturup dispatch() çağırmak üretim ortamı için yeterli değildir. Bağlantı sürücüsü, worker süreci, tekrar deneme sınırı, idempotency, başarısız iş kaydı ve deployment adımı birlikte tasarlanmalıdır.

Bu rehber, Laravel’in resmi Queue belgelerindeki güncel davranışları temel alarak veritabanı veya Redis bağlantısından job sınıfına, Supervisor yapılandırmasından hata incelemeye kadar uygulanabilir bir yol sunar. Örneklerde gerçek müşteri verisi ya da doğrulanmamış performans iddiası kullanılmaz; her ayarın neden gerektiği ve hangi riski azalttığı açıklanır.

Laravel Queue sistemi nasıl çalışır?

Bir HTTP isteği içindeki ağır işi doğrudan çalıştırmak, yanıt süresini uzatır ve uzak servis geçici olarak durduğunda kullanıcı işlemini başarısız gösterebilir. Queue kullanıldığında uygulama önce yapılacak işi bir mesaj olarak seçilen kuyruğa yazar. Ayrı çalışan worker bu mesajı alır, job sınıfının handle() metodunu yürütür ve sonuç başarılıysa işi kuyruktan siler.

Laravel uygulamasından kuyruğa aktarılan job ve onu işleyen worker akışı
İstek, kuyruğa yazma ve worker tarafından işleme adımları birbirinden bağımsız çalışır.

Connection, queue ve worker farkı

Connection, işlerin nerede tutulacağını belirleyen database, Redis, SQS veya sync gibi bağlantıdır. Queue, aynı bağlantı içindeki default, emails ya da reports gibi mantıksal kanaldır. Worker ise belirli bağlantı ve kuyrukları dinleyen uzun ömürlü PHP sürecidir. Bu üç kavramı ayırmak, “job kuyruğa yazıldı ama çalışmadı” sorununu teşhis etmeyi kolaylaştırır.

sync bağlantısı işi gerçekten arka plana taşımaz; job aynı PHP isteğinde çalışır. Yerel geliştirmede hızlı doğrulama için yararlı olsa da üretim davranışını temsil etmez. Gerçek asenkron akışı test etmek için database, Redis veya kullanılan bulut kuyruğu ile çalışan bir worker gerekir.

Queue bağlantısını ve veri tabanını hazırlama

Küçük bir projede ek servis kurmadan başlamak için database bağlantısı anlaşılır bir seçenektir. Yüksek iş hacmi, gelişmiş kuyruk önceliği veya Horizon izleme ekranı gerekiyorsa Redis daha uygun olabilir. Horizon yalnızca Redis tabanlı kuyruklarla çalışır. Seçimi “hangisi daha hızlı?” sorusuyla değil; iş hacmi, operasyon ekibinin yetkinliği, yedekleme ve gözlemleme ihtiyacıyla yapın.

QUEUE_CONNECTION=database

php artisan make:queue-table
php artisan migrate

php artisan make:queue-failed-table
php artisan migrate

Yeni Laravel kurulumlarında gerekli migration dosyaları zaten bulunabilir. Komutu çalıştırmadan önce database/migrations dizinini kontrol etmek, aynı tablo için ikinci migration üretme riskini önler. Yapılandırma önbelleği kullanılıyorsa .env değişikliğinden sonra php artisan config:clear veya deployment akışındaki uygun config cache komutu uygulanmalıdır.

Job’a aktarılacak veriyi kuyruğa göndermeden önce doğrulamak gerekir. Form Request kullanımı ve yalnızca doğrulanmış alanların seçilmesi için Laravel validation rehberine bakabilirsiniz. Model ilişkilerini gereksiz büyüklükte serialize etmemek için de Eloquent ilişkileri rehberi yardımcı olur.

Job oluşturma ve güvenli biçimde dispatch etme

Job sınıfını Artisan ile oluşturun ve sınıfın ShouldQueue sözleşmesini uyguladığını doğrulayın. İş mantığını controller içine veya job constructor’ına yığmak yerine handle() içinde, gerektiğinde ayrı bir service sınıfı üzerinden çalıştırın. Constructor, kuyruğa yazılacak kimlikleri ve küçük değer nesnelerini taşımak için uygundur.

php artisan make:job GenerateMonthlyReport
<?php

namespace App\Jobs;

use App\Services\ReportService;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Queue\Queueable;

class GenerateMonthlyReport implements ShouldQueue
{
    use Queueable;

    public int $tries = 3;
    public int $timeout = 120;

    public function __construct(
        public readonly int $accountId,
        public readonly string $period
    ) {
        $this->onQueue('reports');
    }

    public function backoff(): array
    {
        return [10, 60, 300];
    }

    public function handle(ReportService $reports): void
    {
        $reports->generate($this->accountId, $this->period);
    }
}

Job, service container üzerinden bağımlılık alabilir. Böylece rapor üretme kuralı job sınıfından ayrılır ve HTTP isteği, CLI komutu ya da test tarafından tekrar kullanılabilir. Dispatch işlemi ise doğrulama ve yetki kontrolü tamamlandıktan sonra yapılmalıdır.

GenerateMonthlyReport::dispatch(
    accountId: $account->id,
    period: $request->validated('period')
)->onConnection('database')
 ->onQueue('reports');
Laravel job sınıfının doğrulanmış veriyle reports kuyruğuna gönderilmesi
Job küçük ve doğrulanmış bir payload ile doğru bağlantı ve kuyruğa gönderilir.

Veri tabanı işlemi tamamlandıktan sonra dispatch

Job bir transaction içinden gönderilirse worker, transaction commit edilmeden önce çalışabilir. Bu durumda henüz veri tabanında görünmeyen bir modeli arayıp hata verebilir. Bağlantının after_commit ayarını kullanmak veya job üzerinde afterCommit() çağırmak, işin başarılı commit sonrasında kuyruğa bırakılmasını sağlar.

GenerateMonthlyReport::dispatch($account->id, $period)
    ->afterCommit();

Tekrarlanan işleri idempotent ve benzersiz tasarlama

Queue sistemleri “bir job kesinlikle yalnızca bir kez çalışır” varsayımıyla tasarlanmamalıdır. Worker timeout olduğunda, bağlantı kesildiğinde veya retry_after süresi yanlış ayarlandığında aynı iş yeniden alınabilir. Job ikinci kez çalıştığında ödeme, e-posta ya da rapor kaydını çoğaltmıyorsa idempotent kabul edilir.

Veri tabanında benzersiz anahtar kullanmak, işlem sonucunu iş anahtarıyla kaydetmek ve dış API destekliyorsa idempotency key göndermek sağlam yöntemlerdir. Yalnızca cache kilidine güvenmek yerine kalıcı veri kuralını da uygulayın. Aynı hesap ve dönem için birden fazla rapor job’ının kuyruğa girmesini önlemek gerekiyorsa ShouldBeUnique kullanılabilir.

use Illuminate\Contracts\Queue\ShouldBeUnique;

class GenerateMonthlyReport implements ShouldQueue, ShouldBeUnique
{
    public int $uniqueFor = 3600;

    public function uniqueId(): string
    {
        return $this->accountId.':'.$this->period;
    }
}

Benzersiz job kilitleri cache altyapısını kullanır ve batch içindeki job’lara uygulanmaz. Birden fazla uygulama sunucusu varsa hepsinin aynı merkezi cache’i kullandığını doğrulayın. Dağıtık servis sınırlarında tekrar işleme riskini ayrıca ele almak için mikroservis mimarisine geçiş rehberindeki veri sahipliği ve mesajlaşma ilkeleriyle birlikte değerlendirme yapın.

Retry, backoff ve timeout ayarlarını birlikte kurma

$tries toplam deneme sayısını, backoff() denemeler arasındaki gecikmeyi, $timeout ise tek bir çalışmanın en uzun süresini sınırlar. Ağ hatasında hemen art arda yeniden denemek uzak servisi daha fazla zorlayabilir. Artan gecikme dizisi, geçici sorunlara toparlanma alanı verir.

retry_after ve timeout sıralaması

config/queue.php içindeki retry_after, işin başka bir worker tarafından yeniden alınmadan önce ne kadar süre görünmez kalacağını belirler. Worker veya job timeout değeri, retry_after süresinden birkaç saniye kısa olmalıdır. Tersi durumda ilk worker hâlâ çalışırken aynı job ikinci kez işlenebilir. Supervisor’ın stopwaitsecs değeri de en uzun job’ın tamamlanmasına izin verecek kadar büyük olmalıdır.

Süreye bağlı deneme sınırı

Sabit deneme sayısı yerine belirli bir zamana kadar yeniden denemek gerekiyorsa retryUntil() metodu kullanılabilir. Kalıcı doğrulama hatalarını tekrar denemeyin; bunları açıkça başarısız bırakın. Geçici bağlantı hataları ve rate limit yanıtları ise kontrollü backoff ile yeniden denenebilir.

public function retryUntil(): \DateTime
{
    return now()->addMinutes(20);
}

Başarısız job kayıtlarını inceleme ve tekrar çalıştırma

Deneme sınırı dolan işler failed_jobs tablosuna yazılabilir. Bu tabloyu yalnızca hata arşivi olarak bırakmayın; exception türü, başarısızlık zamanı, bağlantı ve queue adı için izleme oluşturun. Job sınıfındaki failed(?Throwable $exception) metodu bildirim veya temizlik başlatabilir. Laravel bu metodu çağırmadan önce job’ın yeni bir örneğini oluşturduğu için handle() sırasında bellekte değiştirilen özelliklere güvenilmemelidir.

php artisan queue:failed
php artisan queue:retry 7
php artisan queue:retry all
php artisan queue:forget 7
php artisan queue:flush

queue:retry all veya queue:flush komutlarını otomatik refleks olarak çalıştırmayın. Önce kök nedeni düzeltin; ardından job’ın idempotent olduğunu ve eski payload’ın hâlâ geçerli olduğunu doğrulayın. Toplu silme geri alınamaz ve hata inceleme kanıtını ortadan kaldırır.

Worker’ı Supervisor ile sürekli çalıştırma

php artisan queue:work terminal kapandığında sona ererse üretim kuyruğu durur. Linux sunucuda Supervisor veya işletim sistemine uygun başka bir process monitor, worker’ı başlatmalı ve beklenmedik kapanmada yeniden çalıştırmalıdır. Aşağıdaki örnekte kullanıcı adı, PHP yolu, proje yolu ve log konumu sunucunuza göre değiştirilmelidir.

[program:laravel-reports]
process_name=%(program_name)s_%(process_num)02d
command=/usr/bin/php /var/www/example/artisan queue:work database --queue=reports --sleep=3 --tries=3 --timeout=120
autostart=true
autorestart=true
stopasgroup=true
killasgroup=true
user=www-data
numprocs=2
redirect_stderr=true
stdout_logfile=/var/log/supervisor/laravel-reports.log
stopwaitsecs=180

Yapılandırma eklendikten sonra supervisorctl reread, supervisorctl update ve ilgili program için supervisorctl start komutları uygulanır. Worker sayısını yalnızca kuyrukta bekleyen iş sayısına bakarak artırmayın; veri tabanı bağlantı limiti, uzak API rate limit’i, CPU ve bellek tüketimi de sınır oluşturur.

Supervisor tarafından yönetilen Laravel queue worker süreçleri ve izleme katmanı
Process monitor, worker süreçlerini ayakta tutar; log ve başarısız job kayıtları operasyonel görünürlük sağlar.

Deployment sırasında worker’ları güvenle yenileme

Queue worker uzun ömürlü bir süreçtir; başladıktan sonra yüklenen uygulama kodunu bellekte tutar. Yeni kod yayınlandığında worker kendiliğinden değişikliği görmez. Deployment akışında migration ve cache adımlarının uygun noktasında php artisan queue:restart çalıştırılmalıdır. Process monitor, mevcut job tamamlandıktan sonra çıkan worker’ı yeniden başlatır.

php artisan migrate --force
php artisan config:cache
php artisan queue:restart

Restart sinyali cache üzerinden iletildiği için uygulamanın cache sürücüsü ve izinleri çalışır durumda olmalıdır. Otomatik yayın akışının genel yapısı için GitHub Actions ile hosting deploy rehberindeki doğrulama ve rollback adımlarını Queue kontrolüyle genişletebilirsiniz.

Zamanlanmış komutların job dispatch etmesi gerekiyorsa cron ile worker’ın farklı süreçler olduğunu unutmayın. Cron yalnızca zamanlayıcıyı tetikler; kuyruğu tüketmez. Bu ayrım Laravel Schedule kurulum rehberinde ayrıntılı olarak açıklanır.

Horizon, loglama ve güvenlik kontrolleri

Redis kullanan projelerde Laravel Horizon; iş hacmi, çalışma süresi ve başarısızlıkları izlemek için bir panel ve kod tabanlı worker yapılandırması sağlar. Horizon panelini internete açık bırakmayın. Üretim erişimini authorization gate ile sınırlandırın, çalışan sayısını bağlantı kapasitelerine göre belirleyin ve deployment sırasında php artisan horizon:terminate kullanın.

Job payload’ına parola, API anahtarı, tam ödeme verisi veya gereksiz kişisel veri koymayın. Model serialize edilse bile payload kuyruk altyapısında ve hata kayıtlarında görülebilir. Yalnızca gereken kimlikleri taşıyın; iş sırasında yetki ve kayıt durumunu yeniden doğrulayın. Secret yönetimi, hata mesajları ve log erişimi için Laravel güvenlik kontrol listesini uygulayın.

Resmi davranışların ayrıntıları için Laravel Queue dokümantasyonunu; Redis tabanlı panel ve worker dengelemesi için Laravel Horizon dokümantasyonunu kaynak olarak kullanın. Projenizin kurulu Laravel ana sürümü farklıysa aynı sürüme ait belgeyi seçin.

Laravel Queue sorun giderme kontrol listesi

  • QUEUE_CONNECTION değeri ile worker komutundaki connection aynı mı?
  • Worker doğru queue adını dinliyor mu?
  • Yapılandırma önbelleği eski .env değerini tutuyor mu?
  • Job, transaction commit edilmeden önce mi çalışıyor?
  • timeout değeri retry_after süresinden kısa mı?
  • Process monitor worker’ı gerçekten ayakta tutuyor mu?
  • Job ikinci kez çalıştığında aynı sonucu çoğaltıyor mu?
  • failed_jobs, uygulama logu ve Supervisor logu birlikte incelendi mi?
  • Deployment sonrasında queue:restart veya Horizon için horizon:terminate çalıştı mı?

Seeder ve factory ile kontrollü test verisi üretip job davranışını gerçek müşteri kaydı kullanmadan sınamak için Laravel Seeder ve Factory rehberinden yararlanabilirsiniz. Testte aynı job’ı iki kez dispatch ederek idempotency kuralını ayrıca doğrulayın.

Sonuç

Sağlam bir Laravel Queue sistemi; job sınıfından daha fazlasıdır. Doğru connection ve queue seçimi, küçük payload, commit sonrası dispatch, idempotency, ölçülü retry/backoff, uyumlu timeout değerleri, sürekli çalışan worker ve gözlemlenebilir hata akışı tek bir bütün oluşturur. Bu katmanlar birlikte kurulduğunda arka plan işleri kullanıcı isteğinden ayrılırken operasyon ekibi de başarısızlığı sessizce birikmeden görebilir.

Sık Sorulan Sorular

Laravel Queue için database mi Redis mi kullanılmalı?

Database sürücüsü küçük ve orta ölçekli başlangıçlar için ek altyapı gerektirmeden çalışabilir. Yüksek hacim, daha gelişmiş kuyruk yönetimi ve Horizon ihtiyacında Redis değerlendirilebilir. Karar iş hacmi, operasyon bilgisi ve mevcut sunucu kapasitesiyle verilmelidir.

queue:work ile queue:listen arasındaki fark nedir?

queue:work uygulamayı belleğe yükleyen uzun ömürlü bir süreçtir ve üretimde process monitor ile yaygın olarak kullanılır. queue:listen her iş için uygulamayı yeniden başlattığından kod değişikliklerini görür, fakat daha düşük verimlidir. Uzun ömürlü worker’da deployment sonrası restart gerekir.

Job kuyruğa yazılıyor ama neden çalışmıyor?

En sık nedenler worker’ın çalışmaması, yanlış connection veya queue adını dinlemesi, eski config cache ve process monitor hatasıdır. Önce job’ın kuyruğa yazıldığını, sonra worker komutunu, uygulama logunu ve Supervisor logunu aynı zaman aralığında kontrol edin.

Başarısız Laravel job nasıl tekrar çalıştırılır?

php artisan queue:failed ile kayıtları inceleyip kök nedeni düzelttikten sonra php artisan queue:retry ID kullanılabilir. Tüm işleri tekrar çalıştırmadan önce payload geçerliliği ve job’ın idempotent olup olmadığı doğrulanmalıdır.

Laravel Queue worker kod değişikliğini otomatik görür mü?

Hayır. queue:work uzun ömürlü olduğu için başladığında yüklediği kodu kullanır. Deployment sırasında php artisan queue:restart çalıştırın ve worker’ın process monitor tarafından yeniden başlatıldığını doğrulayın. Horizon kullanılıyorsa karşılığı php artisan horizon:terminate komutudur.

ozgur

Özgür Bayram, WordPress, Laravel, yapay zekâ ve web performansı alanlarında çalışan bir yazılım geliştiricisidir. Bilim Meraklısı’nda teknoloji, yazılım, hosting, SEO ve dijital araçlar hakkında anlaşılır, uygulanabilir rehberler hazırlar. Amacı, teknik konuları sade bir dille anlatarak okuyucuların doğru kararlar vermesine ve sorunlarını güvenle çözmesine yardımcı olmaktır.

İlgili Makaleler

Bir yanıt yazın

E-posta adresiniz yayınlanmayacak. Gerekli alanlar * ile işaretlenmişlerdir

Başa dön tuşu