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 varsayılanı 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; 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 — kuyruğun yeniden deneme mekanizması ancak bir exception görürse devreye girer. Sessizce return eden bir job, 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: high boşalmadan 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. 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. 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.