Laravel Kuyrukları ve Job'lar: Asenkron Mimari ve Mülakat Soruları 2026

Laravel kuyrukları ve job mimarisine derinlemesine bakış. Job dispatch, batching, chaining, middleware, başarısız job yönetimi ve queue worker yönetimini Laravel 12 örnekleriyle ele alır.

Worker süreçleri ve job dispatch pipeline ile Laravel kuyrukları ve job'ların asenkron mimari diyagramı

Laravel kuyrukları, zaman alıcı görevleri — e-posta gönderimi, yükleme işleme, rapor üretimi — arka plan worker'larına ertelemek için tek tip bir API sunar. Kullanıcıları beklemeye zorlamak yerine uygulama, işi bir kuyruğa atar ve yoluna devam eder. Bu mekanizma ölçeklenebilir her Laravel uygulamasının çekirdeğinde yer alır.

Kuyruk mimarisine genel bakış

Laravel, tek bir sürücüden bağımsız API üzerinden birden fazla kuyruk arka ucunu (Redis, Amazon SQS, veritabanı, Beanstalkd) destekler. Job'lar, ShouldQueue arayüzünü uygulayan serileştirilmiş PHP sınıflarıdır. Worker'lar kuyruktan job çeker, serileştirmeyi geri çözer ve handle() metodunu çalıştırır. Başarısız job'lar, yeniden deneme veya inceleme için özel bir failed_jobs tablosuna düşer.

Laravel Job Dispatch Mekanizması Perde Arkasında

Bir job sınıfında dispatch() çağrıldığında Laravel, job örneğini — public özellikleri dahil — serileştirir ve payload'ı yapılandırılmış kuyruk bağlantısına iletir. Serileştirilmiş payload tam nitelikli sınıf adını, serileştirilmiş özellikleri, hedef kuyruk adını ve izin verilen deneme sayısı ile timeout gibi metadata'yı içerir.

Worker süreci (php artisan queue:work) yeni job'lar için kuyruk arka ucunu yoklayan uzun ömürlü bir daemon olarak çalışır. Bir payload aldığında worker, job'u serileştirmeden çıkarır, bağımlılıklarını servis container üzerinden çözer ve handle() metodunu çağırır.

App/Jobs/ProcessInvoice.phpphp
namespace App\Jobs;

use App\Models\Order;
use App\Services\PdfGenerator;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;

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

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

    public function __construct(
        public readonly Order $order
    ) {}

    public function handle(PdfGenerator $pdf): void
    {
        // Generate PDF invoice for the order
        $invoice = $pdf->generate($this->order);

        // Store the generated file
        $this->order->update([
            'invoice_path' => $invoice->path(),
            'invoiced_at' => now(),
        ]);
    }

    public function failed(\Throwable $e): void
    {
        // Notify the ops team when invoice generation fails
        logger()->error('Invoice generation failed', [
            'order_id' => $this->order->id,
            'error' => $e->getMessage(),
        ]);
    }
}

SerializesModels trait'i, tüm Eloquent modeli yerine yalnızca modelin birincil anahtarını ve sınıf adını saklar. Worker job'u işlerken modeli veritabanından taze olarak çeker. Bu yaklaşım eski veriyi önler ve payload boyutunu küçük tutar.

Paralel İş Yükleri için Job Batching

Job batching, birden fazla job'u tek bir batch içinde gruplar, kolektif ilerlemeyi izler ve tüm job'lar tamamlandığında — ya da herhangi biri başarısız olduğunda — callback'leri tetikler. Bu desen veri içe aktarma, toplu bildirimler ve son adımdan önce birden fazla bağımsız iş biriminin tamamlanması gereken rapor üretimi için uygundur.

App/Http/Controllers/ImportController.phpphp
use App\Jobs\ImportRow;
use Illuminate\Bus\Batch;
use Illuminate\Support\Facades\Bus;

public function import(Request $request)
{
    $rows = $this->parseCSV($request->file('data'));

    // Create a batch of import jobs, one per CSV row
    $batch = Bus::batch(
        collect($rows)->map(fn ($row) => new ImportRow($row))
    )
    ->then(function (Batch $batch) {
        // All jobs completed successfully
        Notification::send(
            auth()->user(),
            new ImportComplete($batch->totalJobs)
        );
    })
    ->catch(function (Batch $batch, \Throwable $e) {
        // First failure in the batch
        logger()->warning('Batch import partial failure', [
            'batch_id' => $batch->id,
            'failed' => $batch->failedJobs,
        ]);
    })
    ->finally(function (Batch $batch) {
        // Runs after all jobs finish (success or failure)
        Cache::forget("import_lock_{$batch->id}");
    })
    ->allowFailures()
    ->dispatch();

    return response()->json(['batch_id' => $batch->id]);
}

Laravel 12, batch payload'larını kuyruk bekleme süresi ve worker tanımlama dahil metadata ile zenginleştirir. allowFailures() metodu, tek bir başarısız job'un tüm batch'i iptal etmesini engeller — kısmi başarının kabul edilebilir olduğu büyük içe aktarmalar için kritiktir.

Sıralı İş Akışları için Job Chaining

Batching paralel iş yüklerini ele alırken, chaining sıralı çalıştırmayı garanti eder. Zincirdeki her job yalnızca öncekinin başarılı olmasının ardından çalışır. Herhangi bir job başarısız olursa zincirin geri kalanı terk edilir ve catch callback'i devreye girer.

App/Services/OrderWorkflow.phpphp
use App\Jobs\ValidatePayment;
use App\Jobs\ReserveInventory;
use App\Jobs\SendConfirmation;
use App\Jobs\GenerateShippingLabel;
use Illuminate\Support\Facades\Bus;

public function processOrder(Order $order): void
{
    // Each job runs only after the previous one succeeds
    Bus::chain([
        new ValidatePayment($order),
        new ReserveInventory($order),
        new GenerateShippingLabel($order),
        new SendConfirmation($order),
    ])
    ->onQueue('orders')
    ->catch(function (\Throwable $e) use ($order) {
        // Roll back the order if any step fails
        $order->update(['status' => 'failed']);
        logger()->error('Order chain failed', [
            'order_id' => $order->id,
            'step' => $e->getMessage(),
        ]);
    })
    ->dispatch();
}

Chaining, adım sırasının önemli olduğu domain iş akışlarında öne çıkar — ödeme, envanter rezerve edilmeden önce doğrulanmalıdır ve kargo etiketleri onaylanmış envantere bağlıdır.

Laravel mülakatlarında başarılı olmaya hazır mısın?

İnteraktif simülatörler, flashcards ve teknik testlerle pratik yap.

Çapraz Kesen Konular için Queue Middleware

Queue middleware, job çalıştırmasını yeniden kullanılabilir mantıkla sarar: rate limiting, deduplikasyon veya circuit breaker. Bu konuları her job'un içine gömmek yerine middleware, job'ların iş mantığına odaklanmasını sağlar.

App/Jobs/Middleware/RateLimitedJob.phpphp
namespace App\Jobs\Middleware;

use Closure;
use Illuminate\Support\Facades\RateLimiter;

class RateLimitedJob
{
    public function __construct(
        private string $key,
        private int $maxAttempts = 10,
        private int $decaySeconds = 60
    ) {}

    public function handle(object $job, Closure $next): void
    {
        // Release job back to queue if rate limit exceeded
        if (RateLimiter::tooManyAttempts($this->key, $this->maxAttempts)) {
            $job->release($this->decaySeconds);
            return;
        }

        RateLimiter::hit($this->key, $this->decaySeconds);

        $next($job);
    }
}

Middleware, job sınıfında middleware() metodu tanımlanarak uygulanır:

App/Jobs/CallExternalApi.phpphp
public function middleware(): array
{
    return [
        new RateLimitedJob(
            key: 'external-api',
            maxAttempts: 30,
            decaySeconds: 60
        ),
        // Prevent duplicate jobs from running concurrently
        (new WithoutOverlapping($this->apiResource->id))
            ->releaseAfter(300)
            ->expireAfter(600),
    ];
}

WithoutOverlapping middleware'i, bir job'un (anahtarla tanımlanan) yalnızca tek bir örneğinin aynı anda çalışmasını sağlamak için atomik kilitler kullanır. Rate limiting ile birleştirildiğinde hem mükerrer işleme hem de API throttling'i önler.

Başarısız Job İşleme ve Yeniden Deneme Stratejileri

Üretim kuyruk sistemleri sağlam hata yönetimine ihtiyaç duyar. Laravel, başarısız job'ları tam payload, exception trace ve hatayı üreten kuyruk/bağlantı ile birlikte failed_jobs tablosunda saklar. Her job sınıfındaki failed() metodu tüm yeniden deneme girişimleri tükendikten sonra çalışır.

Yeniden deneme davranışını job bazında yapılandırmak ince ayar imkânı verir:

App/Jobs/SyncExternalData.phpphp
class SyncExternalData implements ShouldQueue
{
    use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;

    public int $tries = 5;

    // Exponential backoff: 10s, 30s, 60s, 120s, 300s
    public function backoff(): array
    {
        return [10, 30, 60, 120, 300];
    }

    // Job-specific timeout
    public int $timeout = 180;

    // Maximum exceptions before marking as failed
    public int $maxExceptions = 3;

    public function retryUntil(): \DateTime
    {
        // Keep retrying for up to 24 hours
        return now()->addHours(24);
    }

    public function handle(): void
    {
        $response = Http::timeout(30)
            ->retry(2, 1000)
            ->get('https://api.vendor.com/data');

        if ($response->failed()) {
            // Release back to queue with delay for transient failures
            $this->release(60);
            return;
        }

        DataSync::process($response->json());
    }

    public function failed(\Throwable $e): void
    {
        Notification::route('slack', config('services.slack.ops_channel'))
            ->notify(new SyncFailed($e));
    }
}

$tries, $maxExceptions ve retryUntil() arasındaki ayrım mülakatlarda önem kazanır. $tries manuel release dahil her denemeyi sayar. $maxExceptions yalnızca yakalanmamış istisnaları sayar. retryUntil() deneme sayısından bağımsız olarak bir zaman penceresi belirler.

Queue Worker Yönetimi ve Deployment

Üretimdeki queue worker'ları süreç denetimi, deployment sırasında nazik yeniden başlatma ve kaynak yönetimi gerektirir. Worker'ları ayakta tutmak için Supervisor standart araçtır.

ini
; /etc/supervisor/conf.d/laravel-worker.conf
[program:laravel-worker]
process_name=%(program_name)s_%(process_num)02d
command=php /var/www/app/artisan queue:work redis --sleep=3 --tries=3 --max-time=3600
autostart=true
autorestart=true
stopwaitsecs=3600
user=www-data
numprocs=4
redirect_stderr=true
stdout_logfile=/var/log/worker.log
stopasgroup=true
killasgroup=true

Önemli deployment hususları:

  • Nazik yeniden başlatma: php artisan queue:restart worker'lara mevcut job'larını bitirip yeniden başlamalarını sinyalize eder. Deploy sırasında job bozulmasını önler.
  • Maks. süre/job: --max-time=3600 ve --max-jobs=1000, worker süreçlerini periyodik olarak yeniden döngüleyerek bellek sızıntılarını önler.
  • Uyku aralığı: --sleep=3, worker'ın boş bir kuyruğu yeniden yoklamadan önce ne kadar bekleyeceğini kontrol eder. Düşük değerler tepki süresini artırır ancak veritabanı/Redis yükünü de yükseltir.
  • Birden çok kuyruk: --queue=critical,default,low, kuyrukları öncelik sırasıyla işler. Worker'lar default kuyruğa dokunmadan önce critical'i tamamen boşaltır.

Laravel 12.37, Concurrently::defer() kullanarak job'ları erteleyen background queue connection'ı tanıttı. Bu sürücü, job'u serileştirir ve ayrı bir PHP sürecinde çalıştırır — tam bir kuyruk altyapısını gerektirmeyecek hafif job'lar için kullanışlıdır.

Unique Job'lar ve Şifrelenmiş Payload'lar

Üretimde ve mülakat tartışmalarında sıkça karşılaşılan iki desen vardır: belirli bir anahtar için bir job'un yalnızca bir kez çalışmasını sağlamak ve job payload'larındaki hassas verileri korumak.

App/Jobs/RebuildSearchIndex.phpphp
use Illuminate\Contracts\Queue\ShouldBeUnique;

class RebuildSearchIndex implements ShouldQueue, ShouldBeUnique
{
    // Lock duration in seconds
    public int $uniqueFor = 3600;

    public function __construct(
        public readonly string $indexName
    ) {}

    // Unique key scopes the lock to this specific index
    public function uniqueId(): string
    {
        return $this->indexName;
    }

    public function handle(): void
    {
        SearchIndex::rebuild($this->indexName);
    }
}

Hassas veri (kullanıcı kimlik bilgileri, ödeme token'ları) taşıyan job'lar için ShouldBeEncrypted arayüzü tüm serileştirilmiş payload'ı saklanırken şifreler:

App/Jobs/ProcessPayment.phpphp
use Illuminate\Contracts\Queue\ShouldBeEncrypted;

class ProcessPayment implements ShouldQueue, ShouldBeEncrypted
{
    public function __construct(
        private string $paymentToken,
        private float $amount
    ) {}

    public function handle(PaymentGateway $gateway): void
    {
        $gateway->charge($this->paymentToken, $this->amount);
    }
}

Payload, Redis veya veritabanında saklanmadan önce uygulama anahtarıyla şifrelenir. Worker'lar, deserialize etmeden önce payload'ı otomatik olarak çözer.

Laravel Kuyrukları Üzerine Sık Mülakat Soruları

Teknik mülakatlar yüzeysel API bilgisinin ötesinde queue mimarisi anlayışını sıkça test eder.

Bir kuyrukta bekleyen job, silinmiş bir Eloquent modeline atıfta bulunduğunda ne olur? SerializesModels ile worker, job'u işlerken modeli ID üzerinden çekmeye çalışır. Model artık yoksa Laravel ModelNotFoundException fırlatır. Bunu zarif şekilde ele almak için $deleteWhenMissingModels özelliği true olarak ayarlanmalıdır — job, başarısız olmak yerine kuyruktan sessizce silinir.

ShouldBeUnique ile WithoutOverlapping middleware arasındaki fark nedir? ShouldBeUnique, aynı unique key'e sahip bir job kuyrukta zaten varsa bir job'un dispatch edilmesini önler. WithoutOverlapping dispatch'e izin verir ancak eşzamanlı çalıştırmayı engeller — aynı anahtara sahip bir job zaten çalışıyorsa yeni örnek kuyruğa geri bırakılır. Farklı sorunları çözerler ve birleştirilebilirler.

retryUntil() ne zaman $tries'a tercih edilmelidir? retryUntil(), kurtarma süresinin öngörülemediği harici servislerle etkileşen job'lar için uygundur. Sabit bir yeniden deneme sayısı ($tries = 3) kısa bir kesinti sırasında denemeleri tüketebilir. retryUntil() bir zaman penceresi (örn. 24 saat) belirler ve servis kurtarılana ya da pencere dolana kadar backoff ile yeniden denemeye devam eder.

Birden fazla kuyrukla queue priority nasıl çalışır? queue:work --queue=critical,default,low çalıştırmak bir öncelik sistemi oluşturur. Worker, default'a bakmadan önce critical kuyruğu tamamen boşaltır, low'dan önce default'u tamamlar. Bu, düşük öncelikli job'ların yoğun yükte aç kalabileceği anlamına gelir. Sıkı SLA'lar için kuyruk başına dedicated worker'lar daha iyi garantiler sağlar.

Pratik yapmaya başla!

Mülakat simülatörleri ve teknik testlerle bilgini test et.

Sonuç

  • Laravel kuyrukları, Redis, SQS, veritabanı ve Laravel 12.37'deki yeni background connection'ı destekleyen sürücüden bağımsız bir API'nin arkasında kuyruk arka ucunu soyutlar
  • Job batching, kolektif ilerleme takibiyle paralel iş yüklerini ele alırken chaining domain iş akışları için sıralı çalıştırmayı zorunlu kılar
  • Queue middleware (rate limiting, WithoutOverlapping) çapraz kesen konuları job iş mantığının dışında tutar
  • Başarısız job işleme; dirençli yeniden deneme stratejileri için $tries, $maxExceptions, retryUntil() ve üstel backoff'u birleştirir
  • ShouldBeUnique mükerrer dispatch'i önler; ShouldBeEncrypted saklanan hassas payload'ları korur
  • Üretim worker'ları Supervisor, deploy'lar sırasında nazik yeniden başlatmalar ve --max-time ile --max-jobs üzerinden bellek yönetimi gerektirir
  • Mülakat hazırlığı dispatch zamanı uniqueliği ile çalıştırma zamanı kilitlemesi arasındaki ayrımı, model serileştirme davranışını ve queue priority açlığını kapsamalıdır

Laravel mülakat soruları üzerinde uygulamalı pratik için SharpSkill soru bankası kuyrukları, middleware'i ve Eloquent desenlerini ayrıntılı açıklamalarla kapsar.

Pratik yapmaya başla!

Mülakat simülatörleri ve teknik testlerle bilgini test et.

Etiketler

#laravel
#queues
#jobs
#php
#async
#architecture

Paylaş

İlgili makaleler