Cum să întrerupi și să controlezi cozile în Laravel pas cu pas

Ultima actualizare: 5 decembrie 2025
  • Laravel oferă o API unificată pentru cozi cu mai mulți drivere (bază de date, Redis, SQS…) și separare între conexiuni și cozile logice.
  • Joburile permit mutarea sarcinilor grele în fundal, cu suport pentru serializarea modelelor, middleware, joburi unice, criptare, loturi și șiruri de caractere.
  • Lucrătorii sunt gestionați cu ajutorul comenzilor queue:work, Supervisor și comenzi precum queue:pause/queue:continue, care controlează reîncercările, timeout-urile și prioritățile.
  • Există un ecosistem complet pentru eșecuri, monitorizare și testare (failed_jobs, queue:retry, queue:monitor, Queue::fake) care face ca cozile să fie fiabile în producție.

Gestionarea cozilor în Laravel

Lucrul cu cozi în Laravel este aproape obligatoriu în orice proiect serios: trimiterea de e-mailuri, procesarea fișierelor CSV, generarea de PDF-uri, redimensionarea imaginilor, apelarea API-urilor externe… Dacă toate acestea se fac în cererea HTTP principală, site-ul web devine lent, apar 500 de erori, iar utilizatorul ajunge să se holbeze disperat la un ecran gol.

Vestea bună este că Laravel oferă o infrastructură de așteptare puternică și flexibilă , care vă permite să delegați sarcini grele către procese în fundal, să le gestionați după prioritate, să le grupați în loturi, să le monitorizați, să le reîncercați atunci când eșuează și chiar să întrerupeți temporar procesele de mentenanță. Să aruncăm o privire atentă (și să nu omitem nimic) asupra modului în care putem profita la maximum de aceasta, inclusiv cum să întrerupem, să reluăm și să controlăm comportamentul acesteia în producție.

Concepte de bază: conexiuni, cozi și drivere

În Laravel, înainte de a pune pauză la orice, trebuie să înțelegi diferența dintre conexiuni la coadă și cozile în sine. În config/queue.php ai o matrice connections unde definiți fiecare backend: bază de date, redis, beanstalkd, sqs, sincronizare, nul, failover, rabbitmq (dacă îl adăugați) etc.

Fiecare conexiune poate avea mai multe cozi logice (nume precum default, emails, high, low, importsAtributul queue Fiecare conexiune indică care este coada implicită în care vor merge joburile dacă nu este specificată alta. onQueue()În acest fel poți face lucruri precum:

  • O coadă rapidă pentru e-mailuri sau tranzacții scurte.
  • O altă prioritate scăzută pentru sarcini lungi, cum ar fi procesarea videoclipurilor sau a rapoartelor voluminoase.
  • Cozi segmentate după tipul de sarcină: imports, notificaciones, reportes, Etc

Când lansați lucrătorul cu php artisan queue:workÎn mod implicit, ascultă coada specificată ca Mod implicit pe conexiunea selectată. Dacă doriți să stabiliți priorități, puteți transmite mai multe cozi separate prin virgulă:

php artisan queue:work redis --queue=high,default,low

În acest fel, utilizatorul procesează mai întâi „high” , apoi „default” și abia apoi „low” , ceea ce este crucial atunci când sistemul este încărcat și nu doriți ca un raport de 30 de minute să blocheze e-mailurile cu prioritate ridicată.

Drivere de coadă și cerințe preliminare

Laravel oferă o API unificată pentru mai mulți drivere de coadă , dar fiecare are propriile caracteristici și dependențe specifice. Alegerea driverului potrivit este crucială înainte de a discuta despre pauza worker-ilor.

Baza de date cu drivere

Soferul Baza de date Stochează joburile într-un tabel SQL (de obicei jobsEste ușor de configurat și perfect pentru proiecte mici sau medii de dezvoltare în care nu vrei să te joci cu Redis sau SQS.

Pași de bază:

  • Generați migrarea: php artisan queue:table (sau în versiunile mai noi, apare ca create_jobs_table Mod implicit).
  • Executarea migrărilor: php artisan migrate.
  • configurarea CONEXIUNE_COADĂ=bază_de_date în .env și confirmați config/queue.php că 'default' => env('QUEUE_CONNECTION','database').

Este convenabil și suficient de robust, dar cu multe cozi și mult trafic poate deveni un blocaj , deoarece totul trece prin aceeași bază de date relațională.

Driver Redis

Driverul Redis este standardul în producție. Este rapid, acceptă funcții avansate (blocare, limitare a ratei, blocări atomice, Horizon etc.) și se scalează foarte bine.

Puncte importante:

  • Trebuie să configurați conexiunea Redis în config/database.php.
  • Opțiunile serializer y compression de la Redis Nu sunt compatibile cu șoferul de coadă.
  • Dacă folosești Clusterul RedisNumele cozilor trebuie să aibă un hashtag, de exemplu {default}astfel încât toate cheile din acea coadă să cadă în același slot.
  • Opțiunea block_for Permite utilizatorului să blocheze în timp ce așteaptă joburi timp de câteva secunde, în loc să interogheze continuu: economisește CPU și reduce interogările către serverul Redis.

De exemplu:

'redis' => ,

Dacă pui bloc_pentru = 0Lucrătorul rămâne blocat pe termen nelimitat până când devine disponibil un job, lucru de reținut dacă doriți ca acesta să răspundă rapid la semnale precum SIGTERM sau comenzi de pauză.

Alte drivere: SQS, Beanstalkd, null, failover…

Pe lângă baze de date și redis, Laravel suportă și alte sisteme :

  • Amazon SQSCozi gestionate pe AWS, cu suport pentru FIFOGrupuri de mesaje, deduplicare și setări avansate de vizibilitate. Necesită SDK-ul. aws/aws-sdk-php.
  • BeanstalkdFoarte rapid și ușor, folosind pachetul pda/pheanstalk.
  • sincronizaţi: îndeplinește sarcinile inmediatamente în același proces. Util în dezvoltare sau testare, dar în producție este practic ca și cum nu ai folosi deloc cozi.
  • zero: Elimină joburile imediat ce sunt „expediate”. Folosit pentru a dezactiva complet sistemul de așteptare.
  • reluare la eșecAcest lucru vă permite să configurați mai multe conexiuni, iar dacă cea principală eșuează, Laravel încearcă să mute jobul la următoarea din listă. Perfect pentru mediile în care Valabilitate ridicată este critic.
  Electron JS: Tot ce trebuie să știți

La nivelul pauzei și reluării worker-elor, logica este practic aceeași indiferent de driver, deoarece este gestionată din comenzile Artisan și din clasa Worker , nu din backend-ul cozii.

Crearea de locuri de muncă și serializarea modelului

Un Loc de munca În Laravel, este pur și simplu o clasă care încapsulează o sarcină care urmează să fie executată într-o coadă. De obicei, este generată cu Artisan și salvată în app/Jobs.

Pentru a crea unul:

php artisan make:job ProcessPodcast

Clasa generată unelte ShouldQueue și folosește de obicei trăsături Dispatchable, InteractsWithQueue, Queueable y SerializesModelsInima stă în metodă handle()ceea ce face muncitorul când sosește postul.

O caracteristică deosebit de puternică este serializarea automată a modelelor Eloquent . Dacă acceptați un model în constructorul Job, Laravel nu adaugă întregul obiect în coadă, ci doar identificatorul său. Când jobul este procesat, reconstruiește modelul din baza de date cu relațiile sale încărcate.

Exemplu tipic (în pseudocod, pentru a evita repetarea lui ad litteram):

  • Constructorul primește Podcast $podcast.
  • Trăsătura SerializesModels Este responsabil pentru salvarea ID-ului.
  • En handle(AudioProcessor $processor) Tu faci munca grea (de exemplu, procesarea sunetului).

Dacă nu doriți ca relațiile dintr-un model să fie serializate (astfel încât sarcina utilă să nu fie imensă), puteți utiliza $model->withoutRelations() sau în PHP 8 atributul # pe proprietatea promovată de constructor.

Injecția de dependențe în metoda handle

Un alt avantaj este că poți injectează servicii direct în metoda handle (ca și cum ar fi un controller), deoarece Laravel folosește și containerul de servicii în Jobs. De exemplu, puteți solicita un AudioProcessorClienți HTTP, depozite etc., iar framework-ul se ocupă de rezolvarea lor.

Dacă aveți nevoie de un control ultra-fin al ceea ce se numește handleputeți folosi Container::bindMethod() într-un furnizor de servicii și definiți singur logica de injectare, dar în majoritatea cazurilor nu este nevoie să complicați atât de mult lucrurile.

Middleware pentru joburi

Așa cum rutele au middleware, și joburile pot avea middleware specific care rulează înainte și după job. Acest lucru reduce considerabil codul repetitiv din metode handle() și este util în special pentru:

  • Limitarea ratei cu Redis.
  • evita se suprapune a lucrărilor care ating aceeași resursă.
  • Excepții regulate pentru API-uri instabile.
  • Omiterea sarcinilor în anumite condiții (Skip middleware).

Middleware-ul este de obicei plasat în app/Jobs/Middleware și sunt returnate de metodă middleware() Din Iov. Exemplu conceptual:

public function middleware(): array { return ;

Tipuri speciale de joburi: unice, criptate și blocate

Laravel vă permite să marcați anumite joburi ca unic pentru a se asigura că două instanțe identice nu sunt puse vreodată în coadă în același timp. Acest lucru se realizează prin implementarea interfeței ShouldBeUnique o ShouldBeUniqueUntilProcessingși susține definirea unei chei uniqueId() și un timp uniqueFor.

Acest lucru este ideal pentru lucruri precum:

  • Recalculați indexul de căutare al unui produs: o singură dată per produs până se termină.
  • Sincronizări cu servicii externe care nu permit solicitări simultane pentru aceeași resursă.

Mai jos, Laravel folosește un blocare atomică de cache (Redis, memcached, bază de date etc.). De asemenea, puteți alege driverul de cache pentru blocarea respectivă cu uniqueVia().

În plus, există interfața ShouldBeEncrypted, cu care indicați că Jobul complet este Introdu codul înainte de a intra în coadăFoarte util atunci când transmiteți date sensibile și nu doriți ca acestea să circule în text simplu prin Redis, SQS sau baza de date.

Expediați, amânați și executați sarcini în diferite moduri

Odată ce jobul este definit, îl lansați cu Job::dispatch() de la controlere, ascultători de evenimente, comenzi Artisan sau chiar închideri. Parametrii pe care îi transmiteți dispatch Ei merg direct la constructorul lui Iov.

Variații interesante:

  • dispatchIf() y dispatchUnless() pentru a condiționa transportul într-o singură linie.
  • delay() astfel încât jobul să nu poată fi procesat până la o anumită dată/oră.
  • dispatchAfterResponse() o afterResponse() en dispatch() Închideri, care amână execuția până când răspunsul HTTP a fost trimis, dar în cadrul aceluiași proces PHP.
  • dispatchSync() pentru a rula Jobul sincron (fără a trece prin coadă), dar reutilizând aceeași clasă.
  • Conexiuni speciale, cum ar fi deferred o background să se execute după răspuns, în același proces sau într-un nou proces PHP.

În cele din urmă, puteți specifica conexiunea și coada cu metode înlănțuite onConnection() y onQueue(), fie la expediere, fie în cadrul constructorului de joburi.

Control fin: reîncercări, întreruperi, timeout-uri și gestionarea erorilor

O mare parte a lucrului cu cozi în Laravel constă în a decide de câte ori să se reîncerce o lucrare , cât timp să se aștepte între încercări și când să se considere că aceasta a eșuat.

  Ce este un dezvoltator front-end și ce fac ei?

Optiuni Disponibile:

  • --tries en queue:work pentru a seta numărul maxim de încercări pentru toate joburile acelui lucrător (cu excepția cazului în care Jobul își definește propriul job) $tries o tries()).
  • $tries sau o metodă tries() în clasa Job pentru control per Job.
  • retryUntil() să spui: „încearcă din nou cât vrei până acum, după aceea, e un eșec”. Dacă coincide cu tries, comandă retryUntil.
  • $maxExceptions pentru a întrerupe atunci când problema se datorează unor excepții repetate netratate.
  • $timeout o --timeout pentru a indica câte secunde poate rula un Job înainte ca workerul să oprească procesul fiu.
  • $failOnTimeout pentru a marca jobul ca eșuat în caz de expirare.
  • $backoff metoda backoff() pentru a defini cât timp să aștepte între reîncercări, chiar și cu valori exponențiale.

Dacă un job aruncă o excepție neinterceptată, Laravel va... se eliberează înapoi în coadă până când încercările configurate sunt epuizate. Puteți controla acest lucru manual cu release($segundos) o fail($exception) când vrei să marchezi în mod deliberat o lucrare ca eșuată.

Lucrări eșuate, tabelul failed_jobs și eliminarea erorilor

Când un Iov își epuizează încercările, ajunge să fie considerat muncă eșuată și Laravel îl inserează în tabel failed_jobs (sau în DynamoDB dacă îl configurați în acest fel). Tabela respectivă stochează UUID-ul, conexiunea, coada, sarcina utilă a jobului și excepția care a fost generată.

Comenzi utile:

  • php artisan queue:failed să enumere pe cele eșuate.
  • php artisan queue:retry <id|all> să le încerci din nou.
  • php artisan queue:forget <id> pentru a șterge unul.
  • php artisan queue:flush pentru a șterge tot (cu opțiunea pentru ore în versiunile mai noi).
  • php artisan queue:prune-failed --hours=48 pentru a șterge automat înregistrările vechi.

În Jobs puteți defini o metodă failed(Throwable $exception) Pentru curățare suplimentară: notificați utilizatorul, anulați acțiuni parțiale, scrieți jurnale specializate etc. Rețineți că o nouă instanță a Jobului este creată pentru a apela failed()Prin urmare, nu vă bazați pe modificările proprietății efectuate în handle().

Loturi de joburi: loturi de joburi și combinarea acestora cu șiruri de caractere

Când trebuie să lansați sute sau mii de joburi conexe (de exemplu, importul unui fișier CSV gigantic în blocuri, procesarea fișierelor în loturi, curățarea datelor etc.), este recomandabil să utilizați funcționalitatea batch a Laravel .

Flux de bază:

  • creați tabelul job_batches cu php artisan queue:batches-table && php artisan migrate.
  • Marcați joburile care vor face parte din loturi cu trăsătura respectivă Batchable.
  • Folosi Bus::batch() pentru a trimite un set de Job-uri, cu apeluri inverse then, catch y finally.
  • Accesați lotul din fiecare Job cu $this->batch() și verifică dacă este anulat.
  • Posibilitatea de a adăuga mai multe Job-uri la un lot în desfășurare cu $this->batch()->add().

Loturile se integrează perfect cu lanțurile : puteți avea lanțuri în cadrul unui lot sau loturi ca elemente ale unui lanț mai mare. Acest lucru este util mai ales atunci când doriți ca mai multe lanțuri să ruleze „în paralel” și să declanșeze ceva după ce toate s-au finalizat.

Execuția, prioritățile și implementarea lucrătorilor

Toată magia cozilor se întâmplă pentru că ai unul sau mai mulți lucrători executând comanda queue:work (o queue:listen(deși acesta din urmă este mai puțin eficient). Aceste procese sunt daemoni cu rulare lungă care ascultă cozile lor respective.

Opțiuni cheie ale queue:work:

  • Conexiune: php artisan queue:work redis, queue:work database, Etc
  • Colas: --queue=high,default,low a prioritiza.
  • -unsprezece: procesează un singur job și se închide.
  • –max-jobs: procesează X joburi și se termină (pentru a reporni periodic workerele și a elibera memorie).
  • –timp maxim: procesează timp de N secunde și apoi se închide.
  • -dormi: secunde de așteptare când nu sunt disponibile locuri de muncă.
  • –oprire-când-este-gol: golește coada și se închide perfect (ideal în mediile Docker sau în joburi unice).
  • -pauză: secunde de timeout per job, așa cum am menționat anterior.
  • -Force: pentru a procesa joburi chiar dacă aplicația este în modul de întreținere.

Deoarece aceste procese rulează de mult timp, în producție, un monitor de proces precum Supervisor (pe Linux) este aproape întotdeauna utilizat pentru a porni workerele X, a le reporni dacă se blochează și a gestiona jurnalele. Un bloc de configurare Supervisor tipic pentru Laravel definește:

  • comandă cu php artisan queue:work ... beton.
  • numprocs pentru a indica câți lucrători să fie lansați în paralel.
  • Căi de jurnalizare, utilizator, reporniri automate etc.

Întreruperea și reluarea cozilor în Laravel

Să ajungem la ceea ce este adesea important în mentenanță: cum să punem pe pauză lucrătorii din coadă fără a-i opri brusc și fără a lăsa joburile neterminate. Laravel oferă mai multe mecanisme, unele mai noi, altele clasice, pentru a controla acest lucru.

Pauză: coadă: semnale de pauză și întrerupere

În versiunile recente, Laravel adaugă comenzi specifice pentru a întrerupe și relua execuția workerilor:

  • php artisan queue:pause: spune sistemului să nu mai preia noi joburi, dar să le lase pe cele deja în curs de desfășurare să se termine.
  • php artisan queue:continue: reia procesarea noilor joburi de unde a rămas.
  Calcularea factorului de inflație a varianței (VIF) în C# și design software

Aceste comenzi funcționează prin trimiterea de semnale către workeri prin mecanismul de caching pe care Laravel îl folosește intern. De aceea este important să aveți un driver de cache configurat corect (memcached, redis, database, file etc.), chiar dacă coada de cache reală este alta.

Important: Întreruperea unei cozi sau a unui lucrător nu anulează și nu șterge lucrările deja puse în coadă ; aceasta pur și simplu împiedică eliminarea mai multor lucrări din coadă cât timp durează pauza.

Sondaj privind perturbările și impactul asupra performanței

De fiecare dată când un worker termină un job, Laravel verifică memoria cache pentru semnale de restart, pauză sau continuare . Acest lucru are un cost neglijabil, dar există. Dacă ești extrem de concentrat pe performanță și știi că nu va trebui să întrerupi sau să repornești workerii din comenzi, poți dezactiva această interogare apelând:

Queue::withoutInterruptionPolling();

Sau chiar să ajustezi proprietățile statice ale Illuminate\Queue\Worker pentru a dezactiva anumite atingeri ($restartable, $pausableTotuși, dacă faceți acest lucru și apoi încercați să utilizați queue:pause o queue:restartmuncitorii Nu vor aflaEste o decizie de design care trebuie analizată cu atenție.

Întrerupeți manual cu Supervisor sau Docker

Pe lângă comenzile proprii ale Laravel , în mediile de producție, procesele sunt adesea „puse în pauză” indirect:

  • Oprirea programului în Supervisor (supervisorctl stop laravel-worker:*), care ucide sau oprește muncitorii.
  • folosind –oprire-când-este-gol în implementări pentru a permite lucrătorilor să termine ce se află în coadă și apoi să se oprească.
  • În Docker, permiterea procesării containerului cu --stop-when-empty și apoi se stinge.

Diferența cu queue:pause Prin oprirea completă a proceselor, veți opri procesarea joburilor până când lucrătorii vor reporni. pauză internă Puteți păstra procesele în memorie, așteptând comanda continueAcest lucru este util pentru implementări rapide de tip rollback sau perioade scurte de mentenanță.

Tranzacții în baza de date și after_commit

O greșeală clasică: distribuirea joburilor în cadrul tranzacțiilor bazei de date . Dacă nu ești atent, este posibil ca worker-ul să execute jobul înainte de efectuarea commit-ului, iar modelul sau datele așteptate s-ar putea să nu fie încă în baza de date.

Pentru a evita acest dans, aveți două opțiuni:

  • activa 'after_commit' => true în configurația conexiunii la coadă. Astfel, orice Job expediat în cadrul unei tranzacții este amânat intern până când este confirmat.
  • Utilizați metode per job afterCommit() o beforeCommit() la dispecerizare, pentru a forța comportamentul doar în anumite Joburi.

Dacă tranzacția este inversată, joburile expediate în timpul acesteia sunt eliminate fără a intra vreodată în coadă, ceea ce este exact ceea ce se dorește de obicei pentru a menține consecvența.

Monitorizare, curățare a cozii și testare

Odată ce ai cozile funcționale, nu este suficient să renunți la Jobs și să uiți de ele; ar trebui să monitorizezi și să testezi cum se comportă totul.

Instrumente și comenzi utile:

  • queue:clear pentru a goli o anumită coadă (SQS, Redis, bază de date).
  • queue:monitor pentru a monitoriza numărul de joburi din anumite cozi și a declanșa evenimentul QueueBusy dacă depășesc un prag.
  • Evenimente de locuri de muncă ca Queue::before, Queue::after, Queue::looping y Queue::failing pentru a conecta logica personalizată (jurnale, metrici, reveniri etc.).
  • Falsuri în teste utilizând Queue::fake() y Bus::fake() pentru a verifica dacă joburile, șirurile de caractere sau loturile au fost expediate fără a fi executate efectiv.

În teste puteți afirma lucruri precum „acest Job a fost pus în coadă de X ori” , „un lanț cu aceste Joburi a fost expediat în această ordine” sau „niciun Job anume nu a fost pus în coadă”, ceea ce oferă multă securitate în fluxurile complexe.

Cu întregul ecosistem — drivere precum Redis sau SQS, joburi bine concepute, middleware pentru reglare fină, batch-uri, lanțuri, configurare reîncercări, workeri gestionați de supervizor și comenzi precum queue:pause y queue:continue— Laravel vă permite să stăpâniți procesarea în fundal până la milimetru, de la trimiterea tipică de e-mailuri până la arhitecturi de cozi distribuite cu failover, asigurându-vă că aplicația dvs. continuă să răspundă rapid la nivelul HTTP în timp ce se desfășoară lucrări dificile dedesubt, fără a bloca utilizatorul.

Cadrul Laravel
Articol asociat:
Puternicul cadru Laravel