Ghiduri

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țiuneCum funcționeazăAvantajeDezavantaje
Fire-and-forget în același procesRăspunzi, apoi continui munca în serverFără configurareSe pierde dacă serverul se oprește; fără reîncercări
Cronometru în proces (setInterval, setTimeout)Un cronometru în serverul care ruleazăTrivialNu rulează când doarme; se dublează cu mai multe copii
Coadă în baza de dateJoburile sunt salvate ca rânduri; un worker le preia și le ruleazăFolosește baza pe care o ai; durabilă; ușor de inspectatTrebuie 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 workeriFuncții mature de reîncercare și programareUn alt serviciu de rulat și de plătit
Serviciu găzduit de joburi sau workflow-uriUn terț îți apelează endpoint-urile cu reîncercări și programeProgramarea și reîncercările sunt gestionateDependență de furnizor; alt cont și altă cheie
Programator extern care apelează un endpointUn cronometru din altă parte trimite o cerere HTTP după programFuncționează pe orice găzduire, inclusiv coborâre la zeroIntervale minime și întârzieri; protejează endpoint-ul
Cron-ul integrat al găzduiriiPlatforma rulează o comandă după programSimplu 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:

  1. Creează un tabel jobs: tip, payload (JSON), status (pending, running, done, failed), attempts, run_after, last_error, timestamp-uri.
  2. 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ă.
  3. Un worker preia joburile scadente, le marchează running, face munca și le marchează done.
  4. La eșec, crește attempts, setează run_after mai târziu (de exemplu 1, 5, apoi 30 de minute) și stochează eroarea. După un maxim, marchează-l failed ș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:

  1. Adaugă un endpoint precum POST /api/cron/send-reminders.
  2. Protejează-l: cere un token secret într-un header, stocat ca secret la ambele capete, și respinge orice altceva.
  3. Fă-l idempotent: rularea de două ori în același minut nu trebuie să trimită două memento-uri. Înregistrează ce a fost trimis.
  4. Păstrează fiecare apel scurt: procesează un lot (de exemplu 100 de elemente) și lasă următorul apel să preia restul.
  5. Î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.

Mythex · Pricing · Construiește orice cu AI · Șabloane pentru site-uri și aplicații · Mythex comparat · Glosar pentru crearea de aplicații cu AI · Instrumente gratuite pentru crearea de aplicații cu AI · English

English አማርኛ العربية Български বাংলা Bosanski Català Čeština Dansk Deutsch Ελληνικά Español Español (España) Eesti فارسی Suomi Français Français (Canada) ગુજરાતી हिन्दी Hrvatski Magyar Հայերեն Bahasa Indonesia Íslenska Italiano 日本語 ქართული Қазақ тілі ಕನ್ನಡ 한국어 Lietuvių Latviešu Македонски മലയാളം Монгол मराठी Bahasa Melayu မြန်မာ Norsk bokmål Nederlands ਪੰਜਾਬੀ Polski Português (Brasil) Português (Portugal) Русский Slovenčina Slovenščina Soomaali Shqip Srpski Svenska Kiswahili தமிழ் తెలుగు ไทย Tagalog Türkçe Українська اردو Tiếng Việt 简体中文 繁體中文(香港) 繁體中文(台灣)