Cum rulezi joburi de fundal într-o aplicație web: cozi, programatori și cron
Pentru a rula joburi de fundal într-o aplicație web, împarte mai întâi munca în două feluri. Joburile declanșate (trimite un e-mail de bun venit, procesează o încărcare) pot fi salvate într-o coadă de joburi, adesea un tabel în baza ta de date, și preluate de un worker cu reîncercări. Joburile programate (un raport nocturn, memento-uri la fiecare 15 minute) au nevoie de ceva care pornește după un program: un cron al găzduirii sau un programator extern care apelează un endpoint protejat din aplicația ta. Fă întotdeauna joburile sigure la rularea de două ori, înregistrează eșecurile și nu te baza niciodată pe un cronometru într-un server care poate adormi sau reporni.
Acest ghid explică opțiunile, când se potrivește fiecare și cum le configurezi cu un constructor de aplicații cu AI, inclusiv pe găzduirile care coboară la zero.
Ce înseamnă un job de fundal
Orice este lent, nesigur sau nu este necesar pentru răspunsul imediat al utilizatorului:
- Trimiterea de e-mailuri, notificări și chitanțe
- Redimensionarea imaginilor, transcodarea video, generarea de PDF-uri
- Apelarea unor API-uri terțe lente, inclusiv modele AI pentru sarcini lungi
- Importul sau sincronizarea datelor dintr-un alt sistem
- Curățare nocturnă, rapoarte și memento-uri
Să faci asta în cadrul cererii face paginile lente și fragile: dacă furnizorul de e-mail e căzut, înregistrarea utilizatorului eșuează. Mutând-o în fundal, utilizatorul primește un răspuns instant, iar jobul poate fi reluat.
Opțiunile
| Opțiune | Cum funcționează | Avantaje | Dezavantaje |
|---|---|---|---|
| Fire-and-forget în același proces | Răspunzi, apoi continui munca în server | Fără configurare | Se pierde dacă serverul se oprește; fără reîncercări |
Cronometru în proces (setInterval, setTimeout) | Un cronometru în serverul care rulează | Trivial | Nu rulează când doarme; se dublează cu mai multe copii |
| Coadă în baza de date | Joburile sunt salvate ca rânduri; un worker le preia și le rulează | Folosește baza pe care o ai; durabilă; ușor de inspectat | Trebuie să construiești reîncercările și blocarea; are nevoie de ceva care să ruleze workerul |
| Coadă dedicată (bazată pe Redis sau gestionată) | Un serviciu de coadă plus workeri | Funcții mature de reîncercare și programare | Un alt serviciu de rulat și de plătit |
| Serviciu găzduit de joburi sau workflow-uri | Un terț îți apelează endpoint-urile cu reîncercări și programe | Programarea și reîncercările sunt gestionate | Dependență de furnizor; alt cont și altă cheie |
| Programator extern care apelează un endpoint | Un cronometru din altă parte trimite o cerere HTTP după program | Funcționează pe orice găzduire, inclusiv coborâre la zero | Intervale minime și întârzieri; protejează endpoint-ul |
| Cron-ul integrat al găzduirii | Platforma rulează o comandă după program | Simplu acolo unde există | Nu toate găzduirile îl oferă |
Nu există un singur răspuns corect. O aplicație mică are adesea nevoie doar de o coadă în baza de date plus un programator extern.
De ce găzduirile care coboară la zero schimbă tabloul
Multe găzduiri moderne îți pun aplicația să doarmă când nimeni nu o folosește și o trezesc la următoarea cerere. Asta economisește bani, dar un server adormit nu rulează cod: cronometrele nu pornesc, iar workerii nu fac poll. Pe aceste găzduiri, munca programată trebuie să pornească dintr-o cerere primită, iar cea mai simplă sursă pentru asta este un programator extern. Fiecare apel trezește aplicația (și adesea baza ei de date), deci apelarea la fiecare minut costă mai mult decât la fiecare 15 minute. Alege cel mai rar program care satisface nevoia.
Tiparul 1: o coadă de joburi în baza de date
Pentru joburi declanșate:
- Creează un tabel
jobs: tip, payload (JSON), status (pending,running,done,failed), attempts, run_after, last_error, timestamp-uri. - Când se întâmplă ceva, inserează un rând de job în aceeași tranzacție cu schimbarea care l-a cauzat, ca ele să reușească sau să eșueze împreună.
- Un worker preia joburile scadente, le marchează
running, face munca și le marcheazădone. - La eșec, crește
attempts, seteazărun_aftermai târziu (de exemplu 1, 5, apoi 30 de minute) și stochează eroarea. După un maxim, marchează-lfailedși alertează pe cineva.
Dacă mai mulți workeri ar putea rula simultan, joburile trebuie revendicate în siguranță. Pe Postgres, SELECT ... FOR UPDATE SKIP LOCKED lasă fiecare worker să revendice rânduri diferite fără să se bată între ele.
Ce rulează workerul? Pe un server mereu pornit, o buclă într-un proces separat. Pe o găzduire care coboară la zero, endpoint-ul programat din Tiparul 2 poate procesa un lot de joburi scadente la fiecare apel.
Tiparul 2: un programator extern care apelează aplicația ta
Pentru joburi programate:
- Adaugă un endpoint precum
POST /api/cron/send-reminders. - Protejează-l: cere un token secret într-un header, stocat ca secret la ambele capete, și respinge orice altceva.
- Fă-l idempotent: rularea de două ori în același minut nu trebuie să trimită două memento-uri. Înregistrează ce a fost trimis.
- Păstrează fiecare apel scurt: procesează un lot (de exemplu 100 de elemente) și lasă următorul apel să preia restul.
- Îndreaptă un programator către el.
Alegeri de programator:
- Workflow-uri programate în GitHub Actions. Gratuite în limitele de utilizare ale GitHub. Din septembrie 2026, documentația GitHub spune că cel mai scurt interval este o dată la 5 minute, rulările pot fi întârziate în perioadele aglomerate (mai ales la începutul fiecărei ore), programările folosesc UTC implicit, iar workflow-urile programate dintr-un repository public sunt dezactivate după 60 de zile fără activitate în repository.
- Servicii cron găzduite. Există multe servicii al căror singur rol este să apeleze un URL după program. Verifică fiabilitatea, intervalul minim și alertarea înainte să te bazezi pe unul.
- Programatorul furnizorului tău de cloud. Dacă folosești deja un cloud mare, are un programator gestionat care poate apela un endpoint HTTPS.
- Un serviciu găzduit de joburi sau workflow-uri. Acestea combină programarea, cozile și reîncercările și îți apelează endpoint-urile pentru fiecare pas. Compară prețurile și limitele pe site-ul fiecărui furnizor.
Tiparul 3: lasă furnizorul să o facă
Uneori cel mai bun job de fundal este niciunul:
- Plăți. Stripe reîncearcă plățile eșuate ale abonamentelor și trimite webhook-uri; tu reacționezi în loc să faci poll. Vezi ce este un webhook.
- E-mail. Furnizorii pun în coadă și reîncearcă livrarea odată ce le-ai predat mesajul.
- Muncă AI în volum. OpenAI și Anthropic oferă API-uri batch pentru cereri needurgente pe care le trimiți și le colectezi mai târziu.
Prompturi pentru constructorul tău de aplicații cu AI
Pentru o coadă:
Adaugă o coadă de joburi de fundal folosind un tabel jobs în baza noastră de date (tip, payload JSON, status, attempts, run_after, last_error). Când un utilizator se înregistrează, inserează un job send_welcome_email în aceeași tranzacție, în loc să trimiți e-mailul pe loc. Adaugă o funcție processJobs(limit) care revendică joburile scadente cu SELECT ... FOR UPDATE SKIP LOCKED, le rulează și reîncearcă eșecurile după 1, 5 și 30 de minute, marcându-le ca failed după 4 încercări.
Pentru un declanșator programat:
Adaugă POST /api/cron/tick protejat de un token bearer din secretul CRON_SECRET; respinge orice cerere fără el. Fiecare apel rulează processJobs(100) și trimite memento-urile de rezervare scadente în următoarele 24 de ore care nu au fost încă trimise, înregistrând memento-urile trimise ca nimic să nu se trimită de două ori. Returnează un rezumat JSON al celor rulate. Apoi scrie un workflow GitHub Actions care apelează acest endpoint la fiecare 15 minute, citind URL-ul și tokenul din secretele repository-ului.
Greșeli frecvente
- Să te bazezi pe
setInterval. Se oprește în tăcere când serverul doarme sau repornește și se dublează când rulează două copii. - Un endpoint cron neprotejat. Oricine îl găsește îți poate declanșa joburile sau îți poate umfla factura de e-mail.
- Joburi care nu sunt idempotente. Reîncercările și apelurile duble ale programatorului se vor întâmpla. Proiectează pentru asta.
- Nicio vizibilitate a eșecurilor. Un job care eșuează în tăcere o săptămână e mai rău decât unul care eșuează zgomotos. Înregistrează erorile și alertează la eșecuri repetate.
- Rulări lungi într-o singură cerere. Cererile expiră. Procesează în loturi.
- Programare prea des. Fiecare apel pe o găzduire care coboară la zero trezește și taxează aplicația. Potrivește intervalul cu nevoia reală.
- Fusuri orare. „9 dimineața” înseamnă lucruri diferite pentru programator (adesea UTC) și pentru utilizatorii tăi. Stochează orele în UTC și convertește pentru fiecare utilizator.
- Secrete în fișierul de workflow. Ține tokenurile în depozitul de secrete al programatorului. Vezi cum păstrezi în siguranță cheile API.
Listă de verificare
- Munca lentă este mutată din cererile utilizatorilor
- Joburile sunt stocate durabil, nu doar în memorie
- Reîncercări cu întârzieri crescătoare și un maxim
- Fiecare job este sigur de rulat de două ori
- Endpoint-ul programat este protejat printr-un token secret
- Fiecare rulare procesează un lot delimitat
- Joburile eșuate sunt vizibile, cu alerte
- Intervalul de programare este ales ținând cont de costul de găzduire
- Orele sunt stocate în UTC
Joburi de fundal în Mythex
Documentația Mythex nu descrie joburi programate sau cron, iar aplicațiile publicate și bazele lor de date dorm când sunt inactive și se trezesc la următoarea cerere (vezi Limitele de găzduire). Deci nu te baza pe cronometre în aplicația ta. Abordarea care funcționează este cea de mai sus: cere agentului un tabel de joburi și un endpoint protejat de token, apoi îndreaptă un programator extern către URL-ul tău publicat. Stochează tokenul ca secret de proiect și ține minte că fiecare trezire consumă credite pentru timpul în care aplicația și baza de date sunt treze, deci alege un interval rezonabil. Dacă aplicația ta are nevoie de un backend API pentru asta, rețeta backend REST API este un punct de plecare, iar cum construiești un REST API acoperă proiectarea endpoint-urilor. Dacă workerii sau boții permanenți sunt nucleul produsului tău, o găzduire construită pentru procese persistente s-ar putea potrivi mai bine.
Întrebări
Ce este un job de fundal?
Un job de fundal este munca pe care o face aplicația ta în afara cererii în care un utilizator așteaptă, cum ar fi trimiterea de e-mailuri, redimensionarea imaginilor, generarea de rapoarte sau sincronizarea datelor. Utilizatorul primește un răspuns rapid, iar jobul rulează separat și poate fi reluat dacă eșuează.
Cum rulez o sarcină programată dacă găzduirea mea nu are cron?
Adaugă un endpoint protejat care face munca, apoi lasă un programator extern să îl apeleze după un program. Variantele includ un workflow programat în GitHub Actions, un serviciu cron găzduit sau programatorul furnizorului tău de cloud. Protejează endpoint-ul cu un token secret.
Pot folosi setInterval sau setTimeout pentru joburi programate?
Doar pentru sarcini mici și neimportante, pe un server care rulează mereu. Dacă serverul repornește, scalează la zero sau rulează mai multe copii, cronometrele se pierd sau rulează de mai multe ori. Stochează munca programată într-o bază de date sau folosește un programator.
Mythex suportă joburi cron?
Documentația Mythex nu descrie joburi programate sau cron, iar aplicațiile publicate dorm când sunt inactive. Abordarea sigură este un programator extern care apelează un endpoint protejat din aplicația ta, ceea ce trezește aplicația și rulează munca.