Laravel Queue ve Supervisor ile Asenkron İşlemler
Laravel'de Queue ve Supervisor ile asenkron iş kuyruğu kurulumu ve arka plan işlemleri.
Bir web isteğinin tek bir görevi vardır: kullanıcıya hızlı yanıt dönmek. Ama bazı işler hızlı değildir — e-posta göndermek, bir görseli işlemek, üçüncü parti bir API’yi beklemek. Bu işleri istek-yanıt döngüsünün içinde yaparsanız, kullanıcı sizin yavaş işinizi bekler. Laravel Queue tam burada devreye giriyor: işi şimdi sıraya koy, yanıtı hemen dön, asıl işi arka planda yap.
Bu yazıda Laravel’de bir iş kuyruğunu nasıl kurduğumu ve onu production’da ayakta tutmak için Supervisor’ı nasıl kullandığımı anlatıyorum.
Kuyruk sürücüsü seçimi
Laravel Queue; veritabanı, Redis, Amazon SQS gibi farklı sürücülerle çalışır. Sürücü, sıraya alınan işlerin nerede saklanacağını belirler. .env dosyasındaki QUEUE_CONNECTION değeri bunu kontrol eder ve Laravel 10.x Queue dokümanının belirttiği gibi yeni uygulamalarda varsayılan sürücü sync’tir — yani iş hiç sıraya alınmaz, anında çalışır. Geliştirme sırasında pratiktir; ama production’da gerçek bir sürücüye geçmeden Queue’nun hiçbir faydasını görmezsiniz.
Bu yazıda veritabanı sürücüsünü kullanıyorum. Küçük ve orta yükte yeterli, ek bir altyapı gerektirmiyor. İşler jobs tablosunda saklanır; dokümanın tarif ettiği gibi tabloyu oluşturmak için:
php artisan queue:table
php artisan migrate
Bir iş tanımlamak
Kuyruğa atılacak her iş bir sınıftır. Örnek olarak arka planda e-posta gönderen bir SendMailJob oluşturuyorum:
php artisan make:job SendMailJob
Üretilen sınıfı, alacağı parametreleri ve yapacağı işi netleştirerek dolduruyorum:
<?php
namespace App\Jobs;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
class SendMailJob implements ShouldQueue
{
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
public function __construct(
private string $name,
private string $mail,
) {}
public function handle(): void
{
$subject = 'Merhaba ' . $this->name;
$msg = 'Sitemize üye olduğunuz için teşekkür ederiz!';
if (!mail($this->mail, $subject, $msg)) {
throw new \Exception('Mail gönderilemedi.');
}
}
}
İki nokta önemli. Birincisi: __construct ile aldığım veriyi sınıfın özelliklerinde saklıyorum — Laravel bu veriyi serialize edip kuyrukta tutuyor, iş çalışınca geri yüklüyor. Bu yüzden constructor’a koca nesneler değil, sade değerler (id’ler, kısa string’ler) vermek iyi bir alışkanlıktır. İkincisi: handle içinde işin başarısız olduğu durumda bir exception fırlatıyorum. Bu isteğe bağlı değil. Doküman şöyle diyor: iş işlenirken bir exception fırlarsa, iş otomatik olarak kuyruğa geri bırakılır ve izin verilen deneme sayısına ulaşana kadar tekrar denenir. İşi kuyruğa elle geri bırakmanın bir yolu daha var — release() — ama sessizce return eden bir job ikisini de yapmaz: kuyruk onu bitmiş kabul eder, başarısız olduğunda bile başarılı sayılır.
İşi kuyruğa atmak tek satır:
\App\Jobs\SendMailJob::dispatch('Muhammet', 'info@muhammetsafak.com.tr');
İşleri yürütmek
Kuyruğa atılan işler kendiliğinden çalışmaz; onları işleyen bir worker gerekir:
php artisan queue:work
Bu komut kuyruğu sürekli dinler ve yeni iş geldikçe işler. Farklı öncelikteki işleri ayırmak isterseniz onQueue() ile adlandırılmış kuyruklar kullanabilir, worker’ı da o kuyruğa yönlendirebilirsiniz:
\App\Jobs\SendMailJob::dispatch($name, $mail)->onQueue('medium');
php artisan queue:work --queue=high,medium,default
Buradaki sıralama önceliği belirler: doküman bunu açıkça söylüyor — high kuyruğundaki işlerin tamamı işlenmeden medium’a geçilmez.
Supervisor: worker’ı ayakta tutmak
queue:work uzun ömürlü bir süreçtir — ve uzun ömürlü süreçler ölür. Bir hata, bir bellek sınırı, bir deploy… worker durur ve kimse fark etmezse kuyruk sessizce birikir. Production’da bu kabul edilemez.
Supervisor, Unix sistemlerde arka plan süreçlerini izleyen ve durduklarında onları yeniden başlatan bir araçtır. Laravel worker’ları için tam da bunu istiyorum.
sudo apt-get update
sudo apt-get install supervisor -y
/etc/supervisor/conf.d/ altına bir yapılandırma dosyası açıyorum:
[program:laravel-worker]
process_name=%(program_name)s_%(process_num)02d
command=php /proje/yolu/artisan queue:work --queue=default --tries=3
autostart=true
autorestart=true
user=deploy
numprocs=8
redirect_stderr=true
stdout_logfile=/proje/yolu/storage/logs/worker.log
stopwaitsecs=3600
Birkaç satır kritik. autorestart=true, worker öldüğünde onu geri getirir. numprocs=8 sekiz paralel worker çalıştırır — bu sayıyı yükünüze göre ayarlayın. stopwaitsecs=3600, deploy sırasında çalışan bir işin yarıda kesilmemesi için ona bitmesi adına süre tanır: Supervisor dokümanına göre bu değer, sürece durdurma sinyali gönderildikten sonra kapanmasını beklemek için tanınan saniye sayısıdır ve varsayılanı 10’dur — bir saatlik bir işi on saniyede öldürürsünüz. Laravel’in kendi örnek yapılandırması da aynı sebeple 3600 kullanıyor. Yapılandırmayı tanıtıp başlatmak için:
sudo supervisorctl reread
sudo supervisorctl update
sudo supervisorctl start laravel-worker:*
Son bir not
Kod değiştirip deploy ettiğinizde, çalışan worker’lar eski kodu bellekte tutmaya devam eder. Doküman bunu ayrı bir başlık altında uyarıyor: worker’lar uzun ömürlü süreçler olduğu için yeniden başlatılmadıkça kodunuzdaki değişiklikleri fark etmezler. Her deploy’dan sonra php artisan queue:restart çalıştırmayı dağıtım adımlarınıza ekleyin — yoksa “kodu güncelledim ama hâlâ eski davranıyor” diye saatlerce uğraşırsınız. Bunu birkaç kez yaşadıktan sonra öğrendim.
Laravel Queue ve Supervisor birlikte, basit ama sağlam bir asenkron işleme katmanı veriyor: Laravel işi tanımlar ve sıraya koyar, Supervisor da o işi yapan süreçlerin hiç durmamasını sağlar. İkisi de tek başına eksik; değer, birlikte çalışmalarında.
Kaynaklar
Bu konuda sorulanlar
API sunucumu Cloudflare Tunnel arkasına alıp 443'ü internete kapatmalı mıyım?
Tünel sağlam bir desen ama tek yol bağımlılığıdır; mevcut Caddy kurulumunda kalıp origin'i Cloudflare IP'lerine kilitleyin ve origin pulls ekleyin.
Bakiye güncellemede race condition: Pessimistic Lock mı, Redlock mı?
Parayı DB'nin garantisinde tutun: atomik koşullu `UPDATE ... WHERE balance >= 40` ya da `SELECT ... FOR UPDATE` kullanın, Redlock'u DB dışına bırakın.
Kuyrukta poison pill (zehirli mesaj) ve Dead Letter Queue'yu nasıl yönetirim?
İşe `tries` ve `backoff` verin: sınır dolunca Laravel işi `failed_jobs`'a taşır, `JobFailed` listener'ı alert eder, düzelen işi `queue:retry` geri oynatır.
Dağıtık sistemde Saga Pattern ile eventual consistency'yi nasıl tasarlarım?
Tek atomik işlemi telafi aksiyonlu yerel işlemlere bölün: adımları orchestrator kuyruktan sürsün, her adım idempotent olsun, olaylar outbox'tan çıksın.
Ödeme webhook'u aynı bildirimi tekrar gönderiyor; idempotency'i nasıl kurarım?
HMAC'i ham gövdede constant-time doğrulayın, hızlı 200 dönüp işi kuyruğa alın ve tekilliği event_id üzerindeki UNIQUE constraint'e yaptırın.
Logları OpenSearch'e gönderirken Fluent Bit ile arasına Kafka/Redis buffer koymalı mıyım?
Önce Fluent Bit'in filesystem buffer'ını açın; 40k/s kalıcıysa Kafka'ya geçin, Redis listesini yalnızca kaybını göze aldığınız loglarda kullanın.
Bu konudaki deneyler
Üretim veritabanına giden ad-hoc SQL'i onaya, maskelemeye ve değiştirilemez bir ize bağlayan self-hosted portal; QueryProxy ürününe dönüştü.
Şu an ne yapıyor
Geliştiricinin yazdığı SQL'i üretim veritabanında doğrudan değil, bir onaydan geçirerek çalıştırıyor; sonuçlar diske yazılırken maskeleniyor ve her istek değiştirilemez bir kayda düşüyor. Prod erişimi tek kişide toplanmış ekipler kurabilir.
academia.sh: teori ile sahadan gelen problem aynı derste durabilir mi?
Ücretsiz ve herkese açık, metin merkezli öğrenme platformu; ders kitabı teorisiyle sektörde karşılaşılan problemleri tek ders akışında tutuyor.
Şu an ne yapıyor
Ücretsiz ve kayıt duvarı olmayan bir öğrenme platformu olarak yayında: alan-müfredat-kurs-ünite-ders hiyerarşisi, tam metin arama, ilerleme takibi ve doğrulanabilir sertifika çalışıyor. Bilgisayar bilimleri müfredatını okumak isteyen herkes bugün kullanabilir.
Yorumlar
Yorum yapmak için GitHub hesabınızla giriş yapmanız yeterli. Yorumlar GitHub Discussions üzerinde saklanır.