Laravel ile çalışan bir projede, arka planda yürüyen işler doğru yönetilmediğinde ilk darbe genellikle kullanıcı deneyimine vurur: sipariş sonrası e-posta geç gider, raporlar saatlerce sürer, yoğun trafikte sayfalar beklenenden daha yavaş yanıt verir. Bu belirtiler arka plan işlerinin ve kuyruk tüketicilerinin yanlış yönetilmesinden kaynaklanabilir; ancak veritabanı sorguları, uygulama kodu ve kaynak darboğazları da araştırılmalıdır. Özellikle VPS üzerinde kendi altyapınızı yönettiğinizde, Laravel queue, Supervisor, systemd ve PM2 gibi araçların nasıl konumlanacağını bilmek kritik hale gelir.
Bu rehber, VPS üzerinde Laravel kuyruklarını ve worker süreçlerini örnek bir mimari üzerinden ele alır. PHP worker’ları için Supervisor ile systemd seçeneklerini, ayrı Node.js tüketicileri için PM2’yi, loglama ve kapasite planlamasını inceleyeceğiz. Kodlar Laravel 13 ve PHP 8.3 veya üzerini esas alır. Debian/Ubuntu yolları ve www-data kullanıcısı örnektir; sunucunuzdaki PHP CLI yolu, uzantılar, uygulama kullanıcısı ve izinler doğrulanmalıdır. Aynı worker grubunu tek bir süreç yöneticisi sahiplenmelidir; aşağıdaki alternatifleri aynı kuyruk için birlikte başlatmayın.
Arka Plan İşleri ve Kuyruklar Neden Bu Kadar Önemli?
Modern web uygulamalarında kullanıcı isteğine anında cevap vermek ile ağır işleri arka plana atmak arasındaki denge, performansın kalbinde yer alır. Özellikle PHP-FPM veya klasik PHP çalıştırma modellerinde, HTTP istekleri PHP, web sunucusu veya proxy yapılandırmasına bağlı süre sınırlarına takılabilir. Ağır işleri senkron çalıştırırsanız kullanıcıyı bekletir ve istek işleyen süreçleri daha uzun süre meşgul edersiniz. Kuyruk kullanmak bu işleri istek döngüsünden ayırır; toplam iş yükünü tek başına azaltmaz.
İş kurallarına göre arka plana alınabilecek görevler şunlardır:
- E-posta, SMS, push bildirim gönderimi
- Fatura PDF üretimi, rapor oluşturma, Excel dışa aktarma
- Görsel işlemeleri, thumbnail üretimi, video transkodlama
- Dış API’lere (ödeme, kargo, entegrasyonlar) toplu istekler
- Önbellek ısıtma, arama indekslerini güncelleme, veri migrasyonları
Bu tür işler için düzgün bir kuyruk ve worker mimarisi kurmazsanız, uygulamanızın ön yüzü ile arka plandaki ağır işler birbirine karışır; hem tepki süreleri artar hem de hata yönetimi zorlaşır. Örneğin yüksek trafikli WooCommerce, Laravel veya SaaS projelerinde yanlış zamanlanmış cron job’lar ve dağınık queue worker süreçleri kaynak çekişmesine yol açabilir. Bu yüzden önce mimariyi netleştirmek gerekiyor.
VPS Üzerinde Kuyruk Mimarisi: Temel Bileşenler
VPS üzerinde kendi Laravel altyapınızı yönettiğinizde, kuyruk mimariniz genellikle şu bileşenlerden oluşur:
- Uygulama: Laravel job sınıfları ve iş üreten dispatch çağrıları.
- Kuyruk arka ucu: Laravel’in desteklediği database veya Redis gibi sürücüler.
- Worker süreçleri: Laravel işlerini çalıştıran PHP süreçleri; Node.js tüketicileri kendi uyumlu kuyruk protokolünü kullanır.
- Süreç yöneticisi: PHP için Supervisor veya systemd; Node.js için PM2 ya da systemd.
- Gözlem: İş başarısı, gecikme ve kaynak tüketimini birlikte izleyen log, metrik ve alarmlar.
Bu bileşenlerin hepsini doğru konumlandırmadan performans optimizasyonu yapmak çoğu zaman boşa efor oluyor. Örneğin sadece PHP-FPM ayarları ile oynamak genellikle yeterli olmaz; Laravel prod ortamındaki bütün bileşenleri birlikte ele almak gerekir. Bu konuda daha geniş bir resim görmek isterseniz, Laravel prod ortam optimizasyonu rehberimizde PHP-FPM, OPcache, Octane ve queue yapılarını birlikte ele aldığımız yazıya göz atabilirsiniz.
Laravel Queue Temelleri: Job Tasarımı ve Sürücüler
Laravel Queue, asenkron bir sürücü ve worker kullanıldığında işleri arka plana taşımanızı sağlar; aşağıda değinilen sync sürücüsü ise işi hemen çalıştırır. Asenkron akış basitçe şöyledir:
- Yapılacak işi bir
Jobsınıfında tanımlarsınız. - Bu job’ı
dispatch()ile kuyruğa atarsınız. - Arka planda çalışan worker süreçleri bu job’ı kuyruktan çekip çalıştırır.
Job Sınıflarını Doğru Tasarlamak
Job sınıfları, kuyruk mimarisinin çekirdeğidir. Temel pratikler:
- Job’ı belirli bir iş birimine ayırın; tekrar kullanılabilir iş mantığını servis katmanına taşıyın.
- Payload’ı küçük tutun. ID ve küçük diziler uygundur. Laravel’in Queueable içindeki SerializesModels desteği Eloquent modelini tüm haliyle saklamak yerine kimliğini serileştirir; worker modeli ve yüklenmiş ilişkileri yeniden sorgular. Kuyruğa ekleme anındaki verinin değişmez kopyası olduğunu varsaymayın.
- İşi tekrar yürütmenin aynı dış etkiyi oluşturmasını önleyen idempotency tasarlayın. Ödeme veya e-posta gibi işlemlerde sağlayıcının desteklediği idempotency anahtarı ya da kalıcı iş kaydı ve mutabakat akışı gerekebilir. Süreç yöneticisi tek başına tam bir kez işleme garantisi vermez.
Önce job dosyasını oluşturun. Aşağıdaki sınıf Laravel 13’ün Queueable trait’ini kullanır. Projede App\Models\Order, customer_email alanı ve App\Mail\OrderCreatedMail bulunmalıdır. Bu örnek idempotent değildir: e-posta sağlayıcısı gönderimi kabul ettikten sonra worker kapanırsa tekrar deneme ikinci e-posta gönderebilir. Siparişin bulunmaması burada işi sessizce tamamlar; silinmiş siparişleri atlamanın iş kuralınıza uygun olup olmadığını belirleyin.
php artisan make:job SendOrderEmail
<?php
namespace App\Jobs;
use App\Mail\OrderCreatedMail;
use App\Models\Order;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Queue\Queueable;
use Illuminate\Support\Facades\Mail;
class SendOrderEmail implements ShouldQueue
{
use Queueable;
public function __construct(public int $orderId) {}
public function handle(): void
{
$order = Order::find($this->orderId);
if (! $order) {
return;
}
Mail::to($order->customer_email)->send(new OrderCreatedMail($order));
}
}
Sipariş bir veritabanı transaction’ında oluşturuluyorsa işi commit sonrasında yayımlayın. Aşağıdaki çağrıda $order, uygulamanızın oluşturduğu sipariş modelidir:
SendOrderEmail::dispatch($order->id)->onQueue('mail')->afterCommit();
Çağrının bulunduğu dosyada App\Jobs\SendOrderEmail import edilmelidir. afterCommit(), worker’ın henüz commit edilmemiş siparişi okumasını önler; veritabanı commit’i ile dış kuyruk yazısını tek atomik işlem yapmaz. İşin mutlaka teslim edilmesi gereken akışlarda kalıcı outbox ve yeniden gönderim/mutabakat tasarımı değerlendirilmelidir.
Bu çağrı mail kuyruğuna gönderir. Aşağıdaki default worker örneği yalnızca default kuyruğunu dinler; e-posta işini tüketmek için –queue=mail seçin veya ileride açıklanan mail service örneğini kurun.
Sürücü Seçimi: Database mi, Redis mi?
Laravel Queue için birkaç sürücü seçeneği var. VPS ortamında değerlendirilebilecek seçenekler:
- sync: İşi anında, aynı süreçte çalıştırır. Geliştirmede veya bilerek senkron çalıştırılacak işlerde kullanılabilir; arka planda işleme ihtiyacını karşılamaz.
- database: Job’ları veritabanındaki bir tabloda saklar. Kurulumu basit olmakla birlikte yoğun iş yükünde veritabanına ek yük getirebilir.
- redis: Bellek tabanlı bir kuyruk seçeneğidir. Yüksek iş hacminde değerlendirilebilir; uygunluğu kaynak kapasitesine ve işlerin özelliklerine bağlıdır.
Küçük ölçekli bir SaaS veya kurumsal panel için database sürücüsü başlangıçta gayet yeterli olabilir. Ancak arka plan işlerinin hacmi ve kuyrukta biriken kayıt sayısı arttıkça, kuyruk tablolarının yükü sorgu performansını ve yedekleme/restore operasyonlarını etkileyebilir. Ölçümler kuyruk yükünün veritabanında darboğaz oluşturduğunu gösteriyorsa Redis’e geçiş değerlendirilebilir.
Yeni bağlantıya yönlendirmek, eski backend’de bekleyen işleri kendiliğinden taşımaz. Eski kuyruğu boşaltın veya kontrollü taşıma planı uygulayın; bu sürede eski backend için gerekli tüketicileri tutun. Geçişi iş kimliği ve dış etkiler üzerinden izleyin.
Queue sürücü seçimi yaparken, veritabanı tarafındaki yükün sitenizi ne kadar etkileyebileceğini anlamak için veritabanı indeksleme ve sorgu optimizasyonu rehberimizde anlattığımız sorgu yükü analiz yöntemlerini kuyruk tablolarınız için de uygulayabilirsiniz.
Retry, Timeout ve Failed Jobs
Üretim ortamında job hata yönetimi ayrıca planlanmalıdır. Laravel tarafında şu ayarları mutlaka düşünmelisiniz:
- –tries=3: İlk çalıştırma dahil en fazla üç deneme; üç ek retry değildir. İşe özel deneme sınırı CLI değerini geçersiz kılabilir. Release ve bazı middleware davranışları da deneme tüketebilir. maxAttempts, genel bir job ayarı adı gibi kullanılmamalıdır.
- –timeout=60: Örnekte bir iş için worker zaman aşımı. PHP CLI’de PCNTL gerekir; HTTP istemcisi ve diğer dış I/O işlemlerinin bağlantı/istek zaman aşımını da daha kısa sürelerle ayarlayın.
- retry_after=90: Redis bağlantısındaki rezervasyon süresi. Worker veya işe özel timeout bunun birkaç saniye altında kalmalıdır; aksi halde ilk yürütme sürerken iş yeniden alınabilir. SQS’de karşılık gelen visibility timeout ayrıca yönetilir.
- –backoff=10: Başarısız denemeler arasındaki örnek bekleme süresi; rezervasyon süresiyle aynı ayar değildir.
- Failed jobs: Kullanılan failed-job sürücüsü ve tablosu hazır olmalıdır. Kalıcı hataları inceleyin; yeniden denemeden önce dış işlemin gerçekleşip gerçekleşmediğini kontrol edin.
60 saniyelik timeout, 90 saniyelik retry_after ve 75 saniyelik durdurma beklemesi bu rehberin örnek bütçesidir. Uzun raporlar için bu değerleri birlikte yeniden hesaplayın; yalnızca –timeout değerini yükseltmeyin. Dış API beklemeleri ve alt süreçlerin kapanması da bütçeye sığmalıdır. Daha az deneme yapmak mükerrer yan etki riskini ortadan kaldırmaz.
VPS Üzerinde Queue Worker Çalıştırma Stratejisi
Worker, web isteklerini karşılayan PHP-FPM’den ayrı bir PHP CLI sürecidir. PHP sürümü, yüklü uzantılar, bellek limiti ve çalışma kullanıcısı FPM ile aynı olmayabilir. Tipik bir VPS kurulumu şu katmanları içerir:
- Nginx veya Apache + PHP-FPM ile HTTP trafiğini karşılayan web katmanı
- Laravel artisan komutları ile çalışan queue worker süreçleri
- Bu süreçleri yöneten Supervisor veya systemd unit dosyaları
Aşağıdaki örnek standalone Redis kullanır. Laravel’in redis adlı kuyruk bağlantısı, database.php içindeki queue adlı Redis bağlantısına yönlendirilir. Önce ortam değerlerini belirleyin:
QUEUE_CONNECTION=redis
REDIS_QUEUE_CONNECTION=queue
REDIS_QUEUE=default
REDIS_QUEUE_RETRY_AFTER=90
REDIS_QUEUE_DB=3
config/database.php içindeki redis dizisine aşağıdaki queue bağlantısını ekleyin; mevcut client ve options ayarlarını koruyun. REDIS_QUEUE_DB özel değişkeni ancak bu kod onu okuduğu için çalışır. Redis’e erişim, kimlik doğrulama ve seçilen PhpRedis/Predis istemcisi önceden hazır olmalıdır:
'queue' => [
'host' => env('REDIS_HOST', '127.0.0.1'),
'username' => env('REDIS_USERNAME'),
'password' => env('REDIS_PASSWORD'),
'port' => env('REDIS_PORT', '6379'),
'database' => env('REDIS_QUEUE_DB', '3'),
],
config/queue.php içindeki connections.redis girdisini şöyle eşleştirin:
'redis' => [
'driver' => 'redis',
'connection' => env('REDIS_QUEUE_CONNECTION', 'default'),
'queue' => env('REDIS_QUEUE', 'default'),
'retry_after' => (int) env('REDIS_QUEUE_RETRY_AFTER', 90),
'block_for' => null,
'after_commit' => true,
],
DB 3 yalnızca mantıksal ad alanıdır; aynı Redis instance’ındaki bellek, eviction, kalıcılık ve arıza sınırlarını ayırmaz. Kuyruk verisini cache eviction politikasıyla kaybetmeyin; kayıp toleransı ve kapasiteye göre ayrı instance değerlendirin. Bu örnek Redis Cluster yapılandırması değildir. block_for=null sürekli bloklamayı önler; block_for=0 boş kuyrukta sinyal işlenmesini geciktirebilir. Worker komutu:
/usr/bin/php /var/www/myapp/artisan queue:work redis --queue=default --sleep=1 --tries=3 --backoff=10 --timeout=60 --max-time=3600
–max-time=3600, worker’ı işler arasında yaklaşık bir saat sonunda yeniler; tek bir işe bir saatlik süre sınırı koymaz. –max-jobs da işler arasında yeniden başlatma içindir. Üretimde bu uzun ömürlü süreci Supervisor veya systemd ile yönetin. Kod ya da yapılandırma dağıtımında config cache’i güncel değerlerle üretin, ardından queue:restart ile mevcut işten sonra çıkışı isteyin; süreç yöneticisi yeni worker’ı açar. Restart sinyali cache üzerinden taşındığından komut ve worker’lar aynı desteklenen, kalıcı ve erişilebilir cache deposunu kullanmalıdır.
Supervisor ile Laravel Queue Worker Yönetimi
Supervisor, ini biçimli dosyalarla birden çok süreç örneğini yönetir. Aşağıdaki paket komutları Debian/Ubuntu içindir; diğer dağıtımlarda paket adı, servis ve include dizinini ayrıca kontrol edin.
Supervisor Kurulumu
sudo apt update
sudo apt install supervisor
Debian/Ubuntu paketinde /etc/supervisor/conf.d/ genellikle include dizinidir; çalışan supervisord.conf içindeki include ayarını doğrulayın. Örneği /etc/supervisor/conf.d/myapp-queue.conf olarak kaydedin. /var/www/myapp, /usr/bin/php, www-data kullanıcısı ve /var/log/supervisor dizini mevcut ve uygun izinlerle erişilebilir olmalıdır.
Laravel Queue İçin Örnek Supervisor Konfigürasyonu
[program:myapp-queue-default]
process_name=%(program_name)s_%(process_num)02d
command=/usr/bin/php /var/www/myapp/artisan queue:work redis --queue=default --sleep=1 --tries=3 --backoff=10 --timeout=60 --max-time=3600
directory=/var/www/myapp
user=www-data
numprocs=3
autostart=true
autorestart=true
startsecs=5
startretries=3
stopsignal=TERM
stopasgroup=false
killasgroup=true
stopwaitsecs=75
redirect_stderr=true
stdout_logfile=/var/log/supervisor/%(program_name)s_%(process_num)02d.log
stdout_logfile_maxbytes=10MB
stdout_logfile_backups=5
Burada kritik birkaç nokta var:
- process_name ve numprocs: Üç süreç örneği için benzersiz ad üretilir. Üç worker kapasite tavsiyesi değildir; iş yüküne göre azaltın veya artırın.
- user: Kısıtlı uygulama kullanıcısı seçin. Supervisor setuid ile kullanıcıyı değiştirir; login shell veya doğru HOME ortamı otomatik oluşmaz. Uygulamanın gerekli ortamını ayrıca tanımlayın.
- Loglar: Her sürecin ayrı dosyası ve Supervisor’ın kendi boyut tabanlı rotasyonu vardır. Aynı dönen dosyayı birden çok worker’a kullandırmayın ve aynı dosyaya ikinci bir rotasyon yöneticisi eklemeyin.
- Durdurma: stopasgroup=false önce ana PHP sürecine TERM gönderir; PCNTL ile worker mevcut işi bitirip çıkabilir. 75 saniye dolarsa killasgroup=true süreç grubuna KILL uygular. İşin açtığı alt süreçlerin yaşam döngüsünü ayrıca yönetin; bu ayar detached süreçleri ve dış yan etkileri güvenceye almaz.
- Yeniden başlatma: autorestart=true, –max-time gibi başarılı çıkışları da yeniden başlatır. startsecs ve startretries başlangıç hatalarını sınırlar; FATAL durumuna alarm kurun.
Dosyayı kaydettikten sonra reread ile yapılandırma okumasını kontrol edin. Hata yoksa update, eklenen/değişen süreç gruplarını uygular; çalışan grupları yeniden başlatabileceği için kontrollü dağıtım sırasında kullanın:
sudo supervisorctl reread
sudo supervisorctl update
sudo supervisorctl status
RUNNING, sürecin ayakta olduğunu gösterir; doğru kuyruğu tükettiğini veya e-postaların ulaştığını kanıtlamaz. Temsilî bir işi, en eski işin bekleme yaşını ve hata kayıtlarını birlikte kontrol edin.
Supervisor’ın Artıları ve Eksileri
Artıları:
- Laravel dokümantasyonu ve ekosistemi ile çok uyumlu.
- İni formatı sayesinde yapılandırması basit ve okunaklı.
- Aynı dosya içinde birden çok kuyruk ve worker tanımlayabilirsiniz.
Eksi Yanları:
- Kaynak sınırları için systemd/cgroup gibi ek işletim sistemi düzenlemeleri gerekebilir.
- Supervisor stdout/stderr rotasyonunu yapabilir; Laravel’in ayrı uygulama logları için ayrıca saklama ve rotasyon gerekir.
- Supervisor daemon’ının da açılışta başlaması ve izlenmesi gerekir.
Yine de pek çok proje için Supervisor hâlâ son derece pratik ve güvenilir bir çözüm. Özellikle tek bir VPS üzerinde birkaç Laravel uygulaması koşturduğunuz senaryolarda, her proje için ayrı Supervisor programları tanımlamak temizlik ve yönetilebilirlik getirir.
systemd ile Laravel Queue Worker Çalıştırmak
systemd kullanan bir dağıtımda worker’ları doğrudan service unit olarak yönetebilirsiniz. Açılış, yeniden başlatma, cgroup kaynak sınırları ve journal logları aynı servis yönetimine bağlanır. Supervisor programı ile aşağıdaki systemd servisi aynı worker grubunun alternatif sahipleridir.
Basit Bir systemd Service Tanımı
Örneği /etc/systemd/system/laravel-queue-default.service olarak kaydedin. Yol ve kullanıcı önkoşulları Supervisor örneğiyle aynıdır. After=network.target yalnızca sıralamadır; Redis’in hazır veya erişilebilir olduğunu garanti etmez.
[Unit]
Description=Laravel Queue Worker (default)
After=network.target
StartLimitIntervalSec=60
StartLimitBurst=5
[Service]
Type=simple
User=www-data
Group=www-data
WorkingDirectory=/var/www/myapp
ExecStart=/usr/bin/php /var/www/myapp/artisan queue:work redis --queue=default --sleep=1 --tries=3 --backoff=10 --timeout=60 --max-time=3600
Restart=always
RestartSec=5
KillSignal=SIGTERM
KillMode=mixed
TimeoutStopSec=75
StandardOutput=journal
StandardError=journal
[Install]
WantedBy=multi-user.target
Önce unit sözdizimini kontrol edin. Çıktıdaki hata ve uyarıları giderdikten sonra servisi yükleyip etkinleştirin:
sudo systemd-analyze verify /etc/systemd/system/laravel-queue-default.service
sudo systemctl daemon-reload
sudo systemctl enable --now laravel-queue-default.service
sudo systemctl status laravel-queue-default.service
sudo journalctl -u laravel-queue-default.service -n 100 --no-pager
Restart=always normal worker yenileme çıkışlarını da yeniden başlatır; yöneticinin açık stop talebi ise servisi kapalı bırakır. StartLimitIntervalSec ve StartLimitBurst [Unit] bölümündedir; başlatma sınırına takılan servis kendiliğinden sonsuza kadar toparlanmaz. KillMode=mixed önce ana sürece TERM gönderir, ana süreç çıktığında kalan alt süreçler veya 75 saniye sonunda cgroup zorla sonlandırılabilir. İşin ve alt süreçlerinin bitişi bu süreye sığmalıdır.
systemd Kullanırken Dikkat Edilecek Noktalar
- PHP CLI: Doğru binary, PHP 8.3+, PCNTL ve seçtiğiniz PhpRedis/Predis istemcisini doğrulayın.
- Ortam: Laravel .env ve systemd EnvironmentFile farklı sözdizimleridir; .env dosyasını körlemesine EnvironmentFile olarak bağlamayın. Environment= yalnızca açıkça verilen değerleri geçirir. Config cache kullanılıyorsa Laravel .env dosyasını yeniden okumaz; dağıtımda cache’i üretip worker’ları yenileyin.
- Kaynaklar: CPUQuota=150% bir buçuk CPU zamanına karşılık gelir; bütün VPS’in yüzde 150’si değildir. MemoryMax sert sınırdır ve aşımda süreç öldürülebilir. Değerleri ölçümle seçin, ana web/veritabanı yüküne pay bırakın.
- Loglar: Örnek journal kullanır; journald saklama/persistency ayarını kapasiteye göre belirleyin. StandardOutput=append: bir dosyaya yazabilir ama dosyayı kendiliğinden döndürmez. Laravel dosya logları bundan ayrıdır.
Cron işlerini de systemd timer ile yönetmek isterseniz, cron mu systemd timer mı rehberimizde detaylı anlattığımız gibi, schedule edilmiş görevleri de tek çatı altında toplamanız mümkün.
Supervisor mı systemd mi?
İki yaklaşımın da güçlü tarafları var. Özet bir karşılaştırma yapalım:
- Supervisor daha uygun olabilir:
- Birden çok proje için hızlıca worker tanımlamak istiyorsanız.
- Laravel dökümantasyonundaki örneklerle birebir ilerlemek istiyorsanız.
- Sunucu yönetimi ekibiniz Supervisor’a alışkınsa.
- systemd daha uygun olabilir:
- Ekstra bağımlılık istemiyor ve sadece Linux’un sunduklarıyla yetinmek istiyorsanız.
- Kaynak limitlerini, restart politikalarını, log’ları tek merkezden yönetmek istiyorsanız.
- DevOps tarafında zaten systemd ile standardize bir pratik benimsediyseniz.
Seçim yalnızca küçük/büyük proje ayrımına dayanmaz. Ekip alışkanlığı, dağıtım akışı, log erişimi ve kaynak sınırları belirleyicidir. Aynı karar dedicated sunucularda da geçerlidir; her worker grubunun tek sorumlu yöneticisi olsun.
Node.js Tüketiciler İçin PM2: Laravel ile Yan Yana
Laravel ana API ve paneli sunarken bazı görevler Node.js ile yazılmış olabilir. PM2 bu Node.js süreçlerini yönetebilir. Ancak Laravel’in Redis queue payload’ı PHP job bilgisi ve serileştirilmiş komut içerir; BullMQ’nun iş formatıyla aynı değildir. Aynı Redis instance’ını kullanmak tüketicileri uyumlu yapmaz.
PM2 ile Basit Worker Süreci
Node.js uygulaması ve kuyruk kütüphanesinin bağımlılıkları /var/www/myapp-node altında kurulmuş, worker.js çalışır durumda olsun. PM2’yi kısıtlı Node.js uygulama kullanıcısının ortamında kurun; global npm dizinine yazma izni bu kullanıcı için düzenlenmiş olmalıdır. ecosystem.config.cjs dosyası örneği:
module.exports = {
apps: [{
name: 'myapp-node-worker',
cwd: '/var/www/myapp-node',
script: './worker.js',
exec_mode: 'fork',
instances: 1,
autorestart: true,
restart_delay: 5000,
kill_timeout: 75000,
time: true
}]
};
npm install -g pm2
cd /var/www/myapp-node
pm2 start ecosystem.config.cjs
pm2 startup systemd
pm2 startup çıktısındaki ayrıcalıklı kurulum komutunu inceleyip aynı uygulama kullanıcısı, home dizini ve Node.js yolu için çalıştırın. Ardından aynı kullanıcıyla:
pm2 save
pm2 list
pm2 logs myapp-node-worker --lines 100
Bu komutlar ile:
- PM2 Node.js worker’ını izler; restart_delay yeniden açılışlar arasına bekleme koyar.
- Startup servisi doğru kurulmuşsa kayıtlı süreç listesi açılışta geri yüklenir. pm2 save tek başına açılış servisi kurmaz; Node.js yolu değiştiğinde startup kaydını yenileyin.
- kill_timeout=75000 milisaniyedir. Worker’ın SIGINT/SIGTERM işleyicisi yeni iş almayı durdurmalı, yürüyen işi ve bağlantıları düzgün kapatmalıdır. BullMQ’da await worker.close() TypeScript/JavaScript worker’ı için aktif işleri bekler; kendi bitiş süresi yoktur. İş/I/O sürelerini sınırlandırın ve bu bütçeye sığdırın.
- PM2 cluster veya reload, genel kuyruk işlerinizin mükerrer yan etki olmadan veya kesintisiz tamamlanacağını garanti etmez. Bu örnek tek fork süreci kullanır.
Node.js uygulamalarını üretim ortamına taşırken PM2 ve systemd entegrasyonunu adım adım görmek isterseniz, Node.js’i canlıya alırken PM2 ve systemd kullanımı rehberimiz iyi bir referans olacaktır.
Laravel ve Node.js Arasında Kuyruk Sınırlarını Kurmak
Laravel Redis queue ile Bull/BullMQ’yu aynı queue adına bağlayarak ortak tüketici havuzu oluşturmayın. Ayrı ad alanları ve kuyruklar kullanın. Diller arası iş aktarımında Node.js tarafındaki API’nin BullMQ Queue.add ile iş üretmesi veya her iki tarafta açıkça desteklenen bir olay/mesaj sözleşmesi gerekir. Bağlantı adresinin aynı olması sözleşme değildir.
- mail, report gibi iş tiplerinin hangi uygulama ve kuyruk protokolüne ait olduğunu belirtin.
- Köprüde payload sürümü, kimlik doğrulama, idempotency anahtarı, teslim alındı yanıtı ve tamamlandı durumunu ayrı tanımlayın.
- Her tarafta deneme sınırı, bekleme, hata deposu ve gözlem akışı olsun. HTTP kabul yanıtı veya kuyruğa ekleme sonucu dış işin tamamlandığı anlamına gelmez.
Node.js asenkron I/O için uygun olabilir; CPU yoğun JavaScript event loop’u bloke edebilir. Gerektiğinde worker_threads, sandboxed processor veya ayrı işlem kullanın. İşin Node.js’e taşınması kendiliğinden hız kazandırmaz; dil, kütüphane ve paralellik seçimini temsilî yükle ölçün.
Hangi Aracı Ne Zaman Seçmeli?
Kısa bir karar matrisi üzerinden gidelim:
- Laravel/PHP: Ekip ve işletim sistemi gereksinimlerine göre Supervisor veya systemd seçin.
- Laravel + Node.js: PHP grubunun yöneticisini seçin; ayrı Node.js grubuna PM2 veya doğrudan systemd uygulayın. Aynı broker kullanılıyorsa protokol ve ad alanı sınırlarını koruyun.
- CI/CD: Kod ve config cache’i yayımlayın; klasik Laravel worker’larına queue:restart uygulayın. Horizon kullanıyorsanız onun horizon:terminate akışını ve süreç yöneticisini kullanın. PM2 tarafında kütüphaneye uygun graceful shutdown gerekir.
VPS tarafında sıfır kesinti dağıtım, rollback ve benzeri konulara girmek istiyorsanız, VPS’e sıfır kesinti CI/CD nasıl kurulur rehberimizde anlattığımız systemd tabanlı dağıtım teknikleri bu mimariyle çok iyi örtüşüyor.
Loglama, İzleme ve Alarm: Kuyrukların Sağlığını Ölçmek
Worker ayakta görünürken yanlış kuyruğu dinleyebilir, API’de takılabilir veya işleri sürekli başarısız tamamlayabilir. Bu nedenle yalnızca süreç durumuna değil, kuyruğun iş üretme/tamamlama hızına ve bekleme yaşına bakın. Ani kuyruk büyümesini kaynak, uygulama ve dış servis hatalarıyla ilişkilendirin.
Log Yönetimi
Supervisor veya systemd kullanıyor olsanız da, şu prensiplere dikkat edin:
- Supervisor’da her süreç için ayrı dönen dosya; systemd’de unit bazlı journal; PM2’de süreç logları kullanın. Laravel’in uygulama logunu da izleyin.
- Her logun tek bir rotasyon/saklama sahibi olsun. Supervisor’ın kendi rotasyonunu ve journal sınırlarını kullanın; diğer dosyalar için
logrotateplanlayın. Disk dolma problemlerini önlemek için VPS disk kullanımı ve logrotate rehberimizde anlattığımız pratikleri uygulayın. - Laravel iç logları ile sistem loglarını korele edebilmek için zaman damgalarını ve korelasyon ID’lerini tutarlı kullanın.
Metrik ve Alarm
Kuyruk sağlığını izlemek için şunlara bakmak iyi bir başlangıçtır:
- Kuyruk başına bekleyen iş sayısı ve en eski işin bekleme yaşı
- İşin yürütme süresi, üretilen/tamamlanan iş hızı ve hizmet hedefini aşan gecikme
- Failed jobs, retry, timeout ve dış servis hataları
- Worker sayısı, restart/FATAL durumları, CPU, RAM, disk I/O ve DB/Redis bağlantı baskısı
Basitten başlayıp zaman içinde olgunlaştırmak için, önce Prometheus ve Grafana ile VPS izleme rehberimizde anlattığımız temel node exporter metriklerini kurabilir; ardından Laravel tarafında job metriklerini Prometheus’a işleyerek daha ayrıntılı grafikler elde edebilirsiniz.
Güvenlik ve Kaynak Yönetimi Perspektifinden Kuyruklar
Arka plan işleriniz de sonuçta sunucuda çalışan süreçlerdir; dolayısıyla güvenlik ve kaynak yönetimi açısından web katmanı kadar önemlidir.
Kullanıcı, İzinler ve İzolasyon
- Queue worker süreçlerini root kullanıcı ile kesinlikle çalıştırmayın.
- Web sunucusunun kullandığı kullanıcı (www-data, nginx vs.) veya projeye özel açtığınız kısıtlı bir kullanıcı ile çalıştırın.
- Dosya izinlerini ve sahiplikleri doğru kurgulamak için Linux dosya izinleri rehberimizde anlattığımız prensipleri uygulayın.
CPU, RAM ve IO Kullanımı
Özellikle görsel işleme, PDF üretimi, rapor oluşturma gibi CPU ve disk ağırlıklı işler, yanlış yapılandırılmış worker sayılarıyla birleştiğinde tüm VPS’inizi kilitleyebilir. Şu pratikleri öneriyoruz:
- Worker sayısını CPU çekirdeğine otomatik eşitlemeyin. İş başına bellek, CPU/I/O süresi, DB bağlantıları, API kotaları ve web katmanı payını birlikte hesaplayın; düşük eşzamanlılıkla ölçmeye başlayın.
- Ağır işler için heavy gibi ayrı kuyruk ve sınırlı worker grubu kullanın; ayrı kuyruk aynı host/Redis/veritabanı kaynaklarını izole etmez.
- systemd CPUQuota ve bellek sınırlarını ölçümle belirleyin. Sınır koymak yetersiz kapasiteyi artırmaz; timeout ve zorla kapanmayı tetikleyebilir.
- Disk I/O kullanımını izlemek için VPS kaynak kullanımı izleme rehberimizde anlatılan araçlardan yararlanın.
DCHost VPS Üzerinde Örnek Bir Kuyruk Mimarisi
Aşağıdaki e-ticaret mimarisi kapasite garantisi değil, ölçüme başlayabileceğiniz bir taslaktır:
- Sunucu: Örneğin 4 vCPU, 8 GB RAM; disk seçimi ölçülen I/O ve dayanıklılık gereksinimine göre.
- Uygulama: Laravel 13, PHP 8.3+, Nginx + PHP-FPM; worker’lar ayrı PHP CLI süreçleri.
- Backend: Kalıcılık, erişim ve bellek politikası belirlenmiş standalone Redis.
- Yönetim: PHP için systemd veya Supervisor; varsa ayrı Node.js iş grubu için PM2.
Önerilen Queue Yapıları
mail: Sipariş e-postaları ve bildirimler için başlangıçta bir worker.report: Süre ve bellek bütçesi ayrı hesaplanmış rapor/PDF işleri için bir worker.realtime: Gecikme hedefi ölçülen stok ve entegrasyon işleri için bir worker; kuyruk adı gerçek zaman garantisi değildir.
Her kuyruk için ayrı service oluşturabilir veya laravel-queue@.service şablonunda –queue=%i kullanabilirsiniz; laravel-queue@mail tek mail worker’ı açar. Aynı kuyruğa iki worker isteniyorsa iki ayrı unit’te aynı –queue=mail eşlemesini bilinçli yapın; @mail-2 kendiliğinden mail kuyruğuna bağlanmaz. Önceki default örneğini gereksiz yere açık bırakmayın. Kuyruk ayrımı iş tüketimini ayırır; host, broker ve DB darboğazı diğer kuyrukları hâlâ etkileyebilir.
Laravel uygulamanızı bu yapıyla birlikte DCHost VPS üzerinde ayağa kaldırma sürecinde, Laravel uygulamalarını VPS’te yayınlama rehberimizde anlattığımız Nginx, PHP-FPM ve dağıtım adımlarını takip edebilir, oradaki Horizon ve queue yapılarını bu yazıdaki süreç yönetimi pratikleriyle değerlendirebilirsiniz. Horizon Laravel’in Redis kuyruklarını yönetir; Laravel 13 dokümanına göre Redis Cluster ile uyumlu değildir. Horizon’ın yönettiği kuyruğa ayrıca kontrolsüz queue:work grupları açmayın; süreç sayısı ve log/izleme sahipliği belirli olsun.
Özet ve Yol Haritası: Arka Plan İşlerini Rayına Oturtmak
Laravel kuyruk yönetimi; job tasarımı, uygun backend, süre/deneme politikası ve gözlemle birlikte çalışır. Supervisor ve systemd PHP worker’larını yönetebilir; PM2 ayrı Node.js tüketicilerine uygundur. Diller arası ortak Redis kullanımı için kuyruk protokolünü ayrıca tasarlamak gerekir.
Pratik bir yol haritası çizelim:
- HTTP yanıtından ayrılabilecek işleri belirleyin; her ağır işlemi koşulsuz kuyruğa taşımayın. Kabul edildi ve tamamlandı durumlarını ayırın.
- Database veya Redis seçimini hacim etiketiyle değil ölçüm, bağımlılıklar ve kurtarma ihtiyacıyla yapın.
- Her worker grubuna tek süreç yöneticisi, benzersiz adlar ve tutarlı timeout/retry_after/stop bütçesi tanımlayın.
- Log saklamasını, en eski iş yaşı ve failure alarmlarını kurun; config değişikliği ve dağıtım sonrası worker yenilemesini doğrulayın.
- Temsilî işlerle kontrollü ortamda yeniden deneme, API timeout, graceful stop ve kapasite davranışını sınayın. Hangi dış işlemlerin yeniden oynatılabileceğini iş kuralları belirlesin.
VPS, dedicated veya colocation üzerinde bu prensipleri kendi iş yükünüze uyarlayabilirsiniz. CPU, RAM, disk ve ağ planını VPS kaynak planlama rehberimizde anlatılan ölçümlerle birlikte değerlendirin. Ek sunucu, broker erişimi ve veri sürekliliği planlanmadan tek başına güvenilirlik sağlamaz.





