Queue workers die onverwacht stoppen zijn een nachtmerrie in productie. Ik heb dit meegemaakt toen een queue worker crashte tijdens een drukke periode en gebruikers geen e-mails meer ontvingen. Sindsdien configureer ik altijd systemd services die automatisch mijn Laravel queue workers herstarten. Systemd is de standaard init-systeem manager op de meeste moderne Linux distributies en biedt krachtige mogelijkheden voor procesmonitoring en automatisch herstarten.
Laravel's queue system werkt fantastisch voor het verwerken van zware taken op de achtergrond, maar queue workers zijn gewone PHP processen die kunnen crashen door memory leaks, fatale fouten of server problemen. Zonder proper monitoring en automatisch herstarten loop je het risico dat jobs zich opstapelen zonder dat je het doorhebt. Systemd lost dit op door continue monitoring en intelligente herstart strategieën.
Service bestanden aanmaken en configureren
Systemd services worden gedefinieerd in .service bestanden die de exacte configuratie bevatten voor het proces dat je wilt beheren. Ik plaats mijn Laravel queue worker service configuratie altijd in /etc/systemd/system/laravel-worker.service. Het basisbestand ziet er zo uit:
[Unit]
Description=Laravel Queue Worker
After=network.target mysql.service redis.service
[Service]
Type=simple
User=www-data
Group=www-data
Restart=always
RestartSec=3
ExecStart=/usr/bin/php /var/www/html/artisan queue:work --sleep=3 --tries=3 --max-time=3600
WorkingDirectory=/var/www/html
StandardOutput=journal
StandardError=journal
[Install]
WantedBy=multi-user.target
De After directive zorgt ervoor dat de service pas start nadat de netwerk- en database services beschikbaar zijn. Dit voorkomt problemen waarbij de queue worker probeert te starten voordat MySQL of Redis klaar zijn. De Restart=always optie instructeert systemd om het proces automatisch te herstarten als het stopt, ongeacht de exit code. RestartSec=3 zorgt voor een korte pauze tussen crashes en herstarts om te voorkomen dat een service die continue crasht het systeem overbelast.
Specifieke parameters voor de queue:work command zijn cruciaal voor stabiele operatie. De --max-time=3600 parameter zorgt ervoor dat workers na één uur automatisch stoppen en herstarten, wat memory leaks voorkomt. --sleep=3 bepaalt hoe lang de worker wacht tussen het controleren op nieuwe jobs, en --tries=3 stelt het maximum aantal pogingen in voor gefaalde jobs.
Meerdere workers en resource management
Voor applicaties met hoge doorvoer configureer ik meerdere queue workers die parallel draaien. Systemd template services maken dit eenvoudig beheerbaar. Ik hernoem het service bestand naar [email protected] waarbij de @ aangeeft dat het een template service is:
[Unit]
Description=Laravel Queue Worker %i
After=network.target mysql.service redis.service
[Service]
Type=simple
User=www-data
Group=www-data
Restart=always
RestartSec=3
ExecStart=/usr/bin/php /var/www/html/artisan queue:work --sleep=3 --tries=3 --max-time=3600 --name=worker-%i
WorkingDirectory=/var/www/html
StandardOutput=journal
StandardError=journal
[Install]
WantedBy=multi-user.target
Nu kan ik individuele worker instances starten met systemctl start laravel-worker@1, systemctl start laravel-worker@2, enzovoort. De %i placeholder wordt vervangen door het nummer na de @. Dit geeft me granulaire controle over elke worker instance en maakt het mogelijk om specifieke workers te stoppen voor onderhoud zonder het hele queue systeem plat te leggen.
Resource limits zijn essentieel voor stabiele productie omgevingen. Ik voeg altijd memory en CPU limitaties toe aan mijn service configuraties:
[Service]
MemoryLimit=256M
CPUQuota=50%
OOMScoreAdjust=100
MemoryLimit voorkomt dat een worker teveel geheugen consumeert en andere processen verstoort. CPUQuota=50% beperkt de CPU usage tot 50% van één core per worker. OOMScoreAdjust=100 verhoogt de kans dat deze service wordt beëindigd als het systeem weinig geheugen heeft, waardoor kritieke services zoals de webserver beschermd blijven.
Monitoring en logging integratie
Systemd's ingebouwde logging via journald integreert naadloos met Laravel's logging systeem. Journal logs zijn toegankelijk via journalctl -u laravel-worker -f voor real-time monitoring. Voor langdurige opslag configureer ik journal persistence in /etc/systemd/journald.conf:
[Journal]
Storage=persistent
SystemMaxUse=1G
SystemMaxFileSize=100M
MaxRetentionSec=2week
Health checks implementeer ik door custom Artisan commands die de status van queue workers controleren. Een simpele health check command kan er zo uitzien:
<?php
namespace App\Console\Commands;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\Queue;
class QueueHealthCheck extends Command
{
protected $signature = 'queue:health-check';
protected $description = 'Check queue worker health status';
public function handle()
{
$size = Queue::size();
$failed = Queue::getRedis()->llen('queues:failed');
if ($size > 1000) {
$this->error("Queue size too large: {$size}");
return 1;
}
if ($failed > 50) {
$this->error("Too many failed jobs: {$failed}");
return 1;
}
$this->info("Queue health OK - Size: {$size}, Failed: {$failed}");
return 0;
}
}
Deze health check kan ik integreren in een systemd timer die regelmatig draait. Timers zijn systemd's equivalent van cron jobs maar met betere logging en integratie. Het timer bestand /etc/systemd/system/queue-health.timer ziet er zo uit:
[Unit]
Description=Queue Health Check Timer
Requires=queue-health.service
[Timer]
OnCalendar=*:0/5
Persistent=true
[Install]
WantedBy=timers.target
Het bijbehorende service bestand /etc/systemd/system/queue-health.service:
[Unit]
Description=Queue Health Check
After=network.target
[Service]
Type=oneshot
User=www-data
Group=www-data
ExecStart=/usr/bin/php /var/www/html/artisan queue:health-check
WorkingDirectory=/var/www/html
Graceful shutdowns en deployment integratie
Graceful shutdowns zijn cruciaal tijdens deployments om job verlies te voorkomen. Laravel queue workers luisteren naar SIGTERM signals en voltooien de huidige job voordat ze stoppen. Systemd verstuurt standaard SIGTERM gevolgd door SIGKILL na een timeout. Ik configureer altijd een langere timeout voor queue workers:
[Service]
TimeoutStopSec=300
ExecStop=/bin/kill -TERM $MAINPID
KillMode=mixed
TimeoutStopSec=300 geeft workers 5 minuten om graceful te stoppen. KillMode=mixed zorgt ervoor dat eerst het main proces een SIGTERM ontvangt, en pas na de timeout worden alle processen geforceerd gestopt. Voor deployment scripts integreer ik systemd commando's om workers netjes te stoppen en te starten:
#!/bin/bash
echo "Stopping queue workers..."
sudo systemctl stop laravel-worker@*.service
echo "Deploying application..."
git pull origin main
composer install --no-dev --optimize-autoloader
php artisan migrate --force
php artisan config:cache
php artisan route:cache
php artisan view:cache
echo "Starting queue workers..."
sudo systemctl start laravel-worker@{1..4}.service
echo "Deployment complete"
Dependency management tussen services voorkomt dat queue workers starten voordat de database migraties voltooid zijn. Voor complexere deployment scenarios gebruik ik systemctl is-active checks:
while ! systemctl is-active --quiet mysql.service; do
echo "Waiting for MySQL..."
sleep 2
done
php artisan migrate --force
systemctl start laravel-worker@*.service
Systemd heeft mijn queue worker management volledig getransformeerd van een bron van zorgen naar een betrouwbaar systeem dat zichzelf beheert. Het automatisch herstarten van crashes, resource limiting en graceful shutdowns geven me het vertrouwen dat mijn background jobs altijd verwerkt worden, ook als ik niet actief monitor. De integratie met deployment scripts maakt updates soepel en voorspelbaar, zonder het risico op verloren jobs of downtime.