php artisan queue:work i php artisan queue:listen wyglądają na komendy robiące to samo — a różnica między nimi jest jedną z najczęstszych przyczyn „poprawka działa lokalnie, ale nie na produkcji". Ten artykuł wyjaśnia różnicę, mechanizm queue:restart, wszystkie parametry, które warto ustawić w produkcji, i komendę queue:monitor, której brakuje w większości wdrożeń.
Najważniejsze: Najważniejsze — na produkcji używaj wyłącznie
queue:work(wydajny, trzyma kod w pamięci, wymagaqueue:restartpo wdrożeniu).queue:listenprzeładowuje kod po każdym zadaniu i nadaje się tylko do developmentu.queue:restartto obowiązkowy krok każdego deployu — bez niego workery wykonują stary kod.
queue:work vs queue:listen — tabela różnic
| Cecha | queue:work |
queue:listen |
|---|---|---|
| Przeładowanie kodu | Nie — kod w pamięci od startu | Tak — po każdym zadaniu |
| Wydajność | Wysoka (brak bootstrapu na zadanie) | Niska (pełny bootstrap Laravela per zadanie) |
| Przeznaczenie | Produkcja | Development |
| Restart po zmianie kodu | Wymaga queue:restart |
Automatyczny |
| Uruchamianie procesu potomnego | Nie | Tak (nadzoruje proces queue:work) |
| Zużycie pamięci | Rośnie z czasem (użyj --max-time) |
Stabilne (świeży proces per zadanie) |
Mechanizm jest prosty. queue:work uruchamia aplikację Laravel raz i w pętli pobiera zadania z kolejki — cały framework, konfiguracja i klasy są już załadowane. queue:listen działa inaczej: dla każdego zadania uruchamia proces potomny queue:work --once, który od zera bootuje aplikację, wykonuje jedno zadanie i kończy się.
To dlatego queue:listen „widzi" zmiany w kodzie — każde zadanie startuje ze świeżym bootstrapem. I dlatego jest kilkukrotnie wolniejszy: bootstrap Laravela to typowo 30–80 ms, które przy queue:work płacisz raz, a przy queue:listen — przy każdym zadaniu.
Dlaczego queue:work nie widzi zmian w kodzie
PHP ładuje klasy do pamięci procesu raz — nie ma mechanizmu hot reload jak w Node.js. Worker uruchomiony o 8:00 z wersją kodu A będzie wykonywał wersję A o 15:00, nawet jeśli w międzyczasie wdrożyłeś wersję B. Poprawiony bug? Worker nadal wykonuje stary kod z bugiem.
Uwaga: Objaw tego problemu jest mylący: testujesz poprawkę lokalnie (
queue:listenalbo ręcznequeue:work), działa — a na produkcji „nie działa". Zanim zaczniesz debugować kod, sprawdźsupervisorctl statusi datę startu procesów. Jeśli workery działają od przedwczoraj, wykonują kod sprzed wczoraj.
queue:restart — jak działa i kiedy go wywołać
php artisan queue:restart nie restartuje procesów. Zapisuje w cache (Redis/database) znacznik czasu „restart signal". Każdy worker po zakończeniu bieżącego zadania sprawdza ten znacznik — jeśli został zapisany po jego starcie, worker kończy proces z kodem 0. Nadzorca (Supervisor/systemd) widzi zakończony proces i startuje go ponownie — już z nowym kodem.
php artisan queue:restart
Trzy konsekwencje, które warto znać
- Zadanie w trakcie nie jest przerywane — restart następuje między zadaniami. To bezpieczne.
- Wymaga wspólnego cache — znacznik musi być widoczny dla wszystkich workerów; przy wielu serwerach używaj wspólnego Redisa (nie
filecache). - Nie działa, jeśli workery nie mają nadzorcy — proces kończy się i nikt go nie wskrzesi. Dlatego
queue:restartzakłada Supervisora/systemd.
Przy Laravel Horizon odpowiednikiem jest php artisan horizon:terminate — Horizon kończy bieżące zadania, zwalnia procesy i kończy się, a Supervisor startuje go ponownie.
Parametry queue:work, które warto ustawić w produkcji
Pełna, produkcyjna komenda wygląda tak
php artisan queue:work redis --queue=high,default,low --tries=3 --timeout=90 --max-time=3600 --sleep=3 --memory=256
| Parametr | Co robi | Rekomendacja |
|---|---|---|
--queue=high,default,low |
Priorytet kolejek — najpierw high, potem reszta |
Ustaw, jeśli rozdzielasz zadania na kolejki |
--tries=3 |
Maksymalna liczba prób zadania | 3–5; więcej tylko dla niestabilnych API |
--timeout=90 |
Sekundy, po których zadanie zostaje ubite | Dłuższy niż najdłuższe zadanie, krótszy niż stopwaitsecs nadzorcy |
--max-time=3600 |
Worker kończy się po godzinie pracy | Zawsze ustawiaj — chroni przed wyciekami pamięci |
--sleep=3 |
Ile sekund czekać, gdy kolejka jest pusta | 3 s to dobry kompromis; 0 = agresywne pollowanie i CPU |
--memory=256 |
Limit pamięci workera (MB) | 256 MB dla typowych aplikacji; worker kończy się po przekroczeniu |
--once |
Wykonaj jedno zadanie i zakończ | Do testów i debugowania, nie na produkcję |
--tries ustawiony na poziomie procesu jest wartością domyślną — pojedyncze zadanie może ją nadpisać właściwością public int $tries = 5;. To wygodny układ: sensowny default dla wszystkich, precyzja tam, gdzie trzeba.
queue:monitor — alert, gdy kolejka rośnie
Od Laravela 8 dostępna jest komenda, która zwraca błąd (kod wyjścia ≠ 0), gdy kolejka przekroczy zadany próg
php artisan queue:monitor redis:default,redis:integrations --max=100
Podłączasz ją do crona albo zewnętrznego monitoringu (UptimeRobot, Better Stack) — jeśli w kolejce zalega więcej niż 100 zadań, dostajesz alert, zanim użytkownicy zauważą problem. To najprostszy sposób na monitoring kolejek bez Laravel Horizon.
Kiedy queue:listen ma sens
Mimo całej krytyki queue:listen ma dwa legitne zastosowania
- Development — pracujesz nad logiką zadania i chcesz widzieć zmiany kodu natychmiast, bez restartu workera po każdej edycji.
- Bardzo rzadkie zadania z ciężkim bootstrapem — teoretycznie proces per zadanie izoluje wycieki pamięci; w praktyce
--max-timewqueue:workrozwiązuje ten sam problem wydajniej.
Na produkcji nie ma scenariusza, w którym queue:listen wygrywa. Jeśli widzisz go w konfiguracji produkcyjnej — to dług techniczny do natychmiastowej poprawy.
Najczęściej zadawane pytania
Czy queue:listen jest wolniejszy od queue:work?
Tak, wyraźnie — uruchamia osobny proces i pełny bootstrap Laravela dla każdego zadania (30–80 ms narzutu na zadanie). Przy tysiącach zadań dziennie różnica jest odczuwalna. queue:work bootuje aplikację raz i przetwarza zadania w pętli.
Dlaczego po wdrożeniu moje workery nadal wykonują stary kod?
Bo queue:work trzyma kod w pamięci od momentu startu. Po każdym deployu musisz wywołać php artisan queue:restart (albo horizon:terminate przy Horizonze). Worker dokończy bieżące zadanie i zakończy się, a Supervisor uruchomi go z nowym kodem.
Czy queue:restart zabije zadanie w trakcie wykonywania?
Nie. queue:restart działa „grzecznie" — worker kończy bieżące zadanie, dopiero potem kończy proces. Upewnij się tylko, że nadzorca (Supervisor/systemd) czeka na zakończenie procesu: stopwaitsecs dłuższy niż --timeout.
Jak przetestować pojedyncze zadanie bez uruchamiania workera?
Użyj php artisan queue:work --once — worker pobierze i wykona dokładnie jedno zadanie, potem zakończy się. Do podglądu bez wykonania posłuży php artisan queue:failed (nieudane) albo Tinker z dispatchSync() dla synchronicznego wywołania.
Jak monitorować długość kolejki bez Horizon?
Komendą php artisan queue:monitor redis:default --max=100 — zwraca kod błędu, gdy próg zostanie przekroczony, więc łatwo podpiąć ją pod cron i alerting. Przy większej skali rozważ Laravel Horizon albo Pulse — dają wykresy i historię, nie tylko bieżący stan.
👉 Po wdrożeniach „nic się nie zmienia", a workery działają od tygodni? To brak restartu workerów w procesie wdrożenia. Opisz projekt na kontakt@informatix.com.pl lub przez formularz — uporządkujemy proces wdrożeń i pracy kolejek.
📖 Zobacz również: Kolejki w Laravel i Laravel Horizon — poradnik, Supervisor i systemd dla kolejek Laravel, CI/CD i DevOps dla Laravel oraz usługa Laravel development.