home / blog / Laravel Kuyrukları Derinlemesine: Temell...
Laravel Feb 06, 2026

Laravel Kuyrukları Derinlemesine: Temellerden Üretime Hazır Kalıplara

Laravel kuyrukları hakkında bilmeniz gereken her şey — iş tasarımı, yeniden deneme stratejileri, hız sınırlama, toplu işleme ve üretimde izleme.

Laravel Kuyrukları Derinlemesine: Temellerden Üretime Hazır Kalıplara

Neden Queues Kullanmaliyiz?

Anlik HTTP yaniti icin gerekli olmayan her islem kuyruga alinmalidir: e-posta gondermek, gorsel islemek, rapor olusturmak, ucuncu parti API'lerle senkronizasyon yapmak, webhook gondermek. Kullanicinin bunlari beklemesine gerek yoktur.

E-posta gonderen tipik bir istek 2-5 saniye surer. E-postayi kuyruga alin ve yanit aninda doner.

Iyi Job Tasarimi

Iyi tasarlanmis bir job kucuk, idempotent ve serializasyon yapilabilir olmalidir.

// Good: small, focused, idempotent
class SendInvoiceEmail implements ShouldQueue
{
    use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;

    public function __construct(
        public readonly int $invoiceId
    ) {}

    public function handle(Mailer $mailer): void
    {
        $invoice = Invoice::with('customer')->findOrFail($this->invoiceId);

        // Idempotency check: don't send twice
        if ($invoice->email_sent_at) {
            return;
        }

        $mailer->to($invoice->customer->email)->send(
            new InvoiceMail($invoice)
        );

        $invoice->update(['email_sent_at' => now()]);
    }
}

// Bad: stores entire Eloquent model (serialization issues)
class SendInvoiceEmail implements ShouldQueue
{
    public function __construct(
        public readonly Invoice $invoice // Don't do this for complex models
    ) {}
}

Kural: Job'lara tam nesneler degil, ID'ler gecirin. Bu, buyuk modellerdeki serializasyon sorunlarini onler ve job'un her zaman en guncel verilerle calismasini saglar.

Yeniden Deneme Stratejileri

Ag cagrilari basarisiz olabilir. API'ler cokebilir. Veritabanlari anlik kesintiler yasayabilir. Yeniden deneme stratejiniz, sisteminizin bu aksakliklari ne kadar zarif bir sekilde ele aldigini belirler.

class SyncOrderToErp implements ShouldQueue
{
    use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;

    public int $tries = 5;
    public int $maxExceptions = 3;

    // Exponential backoff: 10s, 30s, 90s, 270s, 810s
    public function backoff(): array
    {
        return [10, 30, 90, 270, 810];
    }

    public function handle(ErpClient $erp): void
    {
        $order = Order::findOrFail($this->orderId);

        $erp->syncOrder($order->toErpFormat());
    }

    // Called when all retries are exhausted
    public function failed(Throwable $exception): void
    {
        Log::critical('ERP sync permanently failed', [
            'order_id' => $this->orderId,
            'error' => $exception->getMessage(),
        ]);

        Notification::route('slack', config('services.slack.alerts'))
            ->notify(new ErpSyncFailed($this->orderId, $exception));
    }
}

Hiz Sinirlandirma

Harici API'leri cagirirken, onlarin hiz sinirlamarina uymalisiniz:

class CallExternalApi implements ShouldQueue
{
    use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;

    public function middleware(): array
    {
        return [
            new RateLimited('external-api'),
        ];
    }

    public function handle(): void
    {
        // Your API call
    }
}

// In AppServiceProvider
RateLimiter::for('external-api', function (object $job) {
    return Limit::perMinute(30); // Max 30 API calls per minute
});

Job Batch Isleme

Cok sayida ogesi islemeniz ve genel ilerlemeyi takip etmeniz gerektiginde:

// Dispatch a batch of jobs
$batch = Bus::batch([
    new ProcessRow($rows[0]),
    new ProcessRow($rows[1]),
    new ProcessRow($rows[2]),
    // ... hundreds of jobs
])
->then(function (Batch $batch) {
    // All jobs completed successfully
    Notification::send($user, new ImportComplete($batch->id));
})
->catch(function (Batch $batch, Throwable $e) {
    // First job failure
    Log::error("Batch {$batch->id} had a failure: {$e->getMessage()}");
})
->finally(function (Batch $batch) {
    // Batch finished (regardless of success/failure)
    ImportJob::where('batch_id', $batch->id)->update(['status' => 'finished']);
})
->name('CSV Import')
->allowFailures()
->dispatch();

// Check progress
$batch = Bus::findBatch($batchId);
echo $batch->progress(); // 67 (percent)

Job Zincirleme

Job'larin sirayla calistirilmasi gerektiginde:

Bus::chain([
    new ExtractDataFromCsv($uploadId),
    new ValidateExtractedData($uploadId),
    new ImportToDatabase($uploadId),
    new SendImportReport($uploadId),
])->dispatch();

Kuyruk Secimi

Tum job'lar esit oneme sahip degildir. Farkli onceliklere sahip birden fazla kuyruk kullanin:

// Critical: payment webhooks
ProcessPaymentWebhook::dispatch($event)->onQueue('critical');

// Default: most jobs
SendWelcomeEmail::dispatch($user)->onQueue('default');

// Low priority: analytics, reports
GenerateMonthlyReport::dispatch($month)->onQueue('low');

// Run workers with priority
// php artisan queue:work --queue=critical,default,low

Uretim Ortaminda Izleme

Kuyruklar izlenmezse sessizce basarisiz olur. Temel izleme unsurlari:

  • Kuyruk derinligi: Kac job bekliyor? Sayi artiyorsa, worker'lariniz yetisemiyor demektir
  • Basarisiz job'lar: failed_jobs tablosunu her gun kontrol edin. Uyarilari yapilandirin.
  • Job suresi: Job'larin ne kadar surdugunu izleyin. Ani bir artis, bir seylerin degistigine isaret eder.
  • Worker sagligi: Coken worker'lari yeniden baslatmak icin Supervisor gibi surec yoneticileri kullanin
// Supervisor config for production
[program:laravel-worker-critical]
process_name=%(program_name)s_%(process_num)02d
command=php /var/www/artisan queue:work redis --queue=critical --sleep=1 --tries=3
autostart=true
autorestart=true
numprocs=4
redirect_stderr=true
stdout_logfile=/var/www/storage/logs/worker-critical.log

[program:laravel-worker-default]
process_name=%(program_name)s_%(process_num)02d
command=php /var/www/artisan queue:work redis --queue=default,low --sleep=3 --tries=3
autostart=true
autorestart=true
numprocs=2
redirect_stderr=true
stdout_logfile=/var/www/storage/logs/worker-default.log
tum yazilara don