Cozi de sarcini și echilibrare
1) De ce cozi de sarcini
Coada de lucru/coada de lucru deconectează producătorii și artiștii interpreți sau executanți în funcție de timp și viteză:- Netezește vârfurile: tampon între subsistemele frontale și grele.
- Stabilizează SLA: priorități și izolarea claselor de sarcină.
- Simplifică toleranța la erori: retroactive, DLQ, re-staging.
- Cântare pe orizontală: Adăugați lucrători fără a schimba API.
Domenii tipice: procesare plati, notificari, generare rapoarte/media, postprocesare ETL/ML, integrare cu API-uri externe.
2) Modelul și conceptele de bază
Producător: publică sarcina (sarcină utilă + metadate: cheie de idempotență, prioritate, termen limită).
Coadă/subiect: tampon/jurnal de sarcini.
Lucrător: ia o sarcină, procesează, confirmă (ack) sau se întoarce cu o eroare.
Vizibilitate Timeout/Lease: sarcini de „închiriere” pe durata procesării, după - auto-redelivery.
DLQ (Dead Letter Queue): „îngroparea” sarcinilor după limita încercărilor/erorilor fatale.
Limită de rată/concurență: per lucrător/per coadă/per chiriaș limite de consum.
- Trageți: lucrătorul însuși solicită sarcina (dozează sarcina).
- Push: Broker fluffs; au nevoie de protecție împotriva „umplerii” lucrătorilor slabi.
3) Semantica de livrare și confirmare
At-most-once: fără retraverse; mai repede, dar posibilă pierdere.
Cel puțin o dată (implicit pentru majoritatea cozilor): sunt posibile duplicate → este necesară idempotența manipulatorului.
Efectiv exact o dată: realizat la nivelul aplicației (idempotență, dedup, tranzacții/outbox). Un broker poate ajuta, dar nu un „glonţ magic”.
- Ack/Nack: rezultat clar.
- Requeue/Retry: с backoff + jitter.
- Mesaj otravă - trimite la DLQ.
4) Echilibrare și planificare
4. 1 Secvență și algoritmi
Simplu şi previzibil.
Coadă prioritară: clase prioritare (P0... P3).
WRR/WSR (Round-Robin ponderat/Random): acțiuni CPU/transput între clase.
WFQ/DRR (analog cozilor „corecte” în rețele): acțiuni per chiriaș/client.
Termen limită/FED: pentru sarcini cu termene limită.
Fair Share: limitarea „vecinilor zgomotoși” (cote per chiriaș).
4. 2 Fluxuri de procesare
Un singur zbor/Coalescing: Combinați sarcinile cheie duplicate.
Capace de concurență: limite stricte ale paralelismului în funcție de tipul de sarcină/integrare (API-uri externe).
4. 3 Geo și Shardening
Cioburi de cheie (chiriaș/id) → localitatea de date, ordinea stabilă în cioburi.
Cache-uri/resurse lipicioase: rutarea hash-ului către lucrători cu o stare „atașată”.
5) Retrai, backoff și DLQ
Backoff exponențial + jitter: 'baza 2 ^ încercare ± aleatorie'.
Încercări maxime și termen limită total (timp pentru a muri) pentru fiecare sarcină.
Clasificarea erorilor: „retractabil” (rețea/limită), „neretrizabil” (validare/interdicție comercială).
Coadă de parcare/întârziere: sarcini amânate (de exemplu, repetați după 15 minute).
Politica DLQ: asigurați-vă că indicați unde și în ce condiții primește mesajul „otrăvitor”; furnizează un reprocesor.
6) Idempotență și deduplicare
Idempotency-Key în sarcină; stocați (Redis/DB) cu TTL pentru ultimele taste N:- văzut → sari/îmbinare/rezultat-cache.
- Chei naturale: Use 'order _ id/ payment_id' în loc de UUID-uri aleatorii.
- Outbox - înregistrați faptul că sarcina și starea sa într-o singură tranzacție de baze de date cu o tranzacție de afaceri.
- Exact-o dată în albastru: „UPSERT” prin cheie, versioning, „cel puțin o dată” în coadă + idempotență în baza de date.
7) clase multi-chirie și SLA
Cozi/fluxuri separate după clasă: „critic”, „standard”, „vrac”.
Cote și priorități pentru fiecare chiriaș (Aur/Argint/Bronz).
Izolare: dedicarea bazinelor de lucrători sub P0; fundal - într-un grup/noduri separate.
Controlul admiterii: nu acceptați mai mult decât puteți procesa în termenele limită.
8) Lucrători de autoscalare
Valori pentru scalare: adâncimea cozii, rata de sosire, timpul de procesare, termenele limită SLA.
KEDA/Orizontal Pod Autoscaler: SQS/Iepure/Kafka lag adâncime declanșează.
Factori de restricție: limitează API-urile de rată externă, baza de date (nu distrugeți capătul din spate).
9) Opțiuni și modele de tehnologie
9. 1 RabbitMQ/AMQP
Schimburi: direct/topic/fanout; Cozi с ack/ttl/DLQ (schimb de litere moarte).
Prefetch (QoS) reglementează „câte sarcini sunt asupra lucrătorului”.
ini x-dead-letter-exchange=dlx x-dead-letter-routing-key=jobs.failed x-message-ttl=60000
9. 2 SQS (și analogi)
Vizibilitate Timeout, DelaySeconds, RedrivePolicy (DLQ).
Idempotence - pe aplicație (tabelul dedup).
Limite: butches 1-10 posturi; se concentreze pe vânătăi idempotente.
9. 3 Kafka/NATS JetStream
Pentru conducte pe scară largă: debit mare, retenție/reluare.
Coadă de sarcini peste jurnale: o sarcină = un mesaj; un lucrător pe control cheie prin partiționare/subiect.
Retrai: subiecte individuale/subiecte-sufixe cu backoff.
9. 4 cozi Redis (Sidekiq/Resque/Bull/Țelină-Redis)
Latență foarte scăzută; urmăriți stabilitatea (RDB/AOF), încercați din nou cheile și cheile de blocare pentru un singur zbor.
Potrivit pentru sarcini „ușoare”, nu pentru retensiune pe termen lung.
9. 5 Cadre
Telina (Python), Sidekiq (Ruby), RQ/BullMQ (Node), Huey/Resque - ready-made retrays, programs, middleware, metrics.
10) Scheme de rutare și echilibrare
Round-Robin: Uniform, dar nu ia în considerare „severitatea” sarcinilor.
RR ponderată: distribuție după capacitatea/piscina lucrătorului.
Fair/Backpressure-conștient: Lucrătorul preia doar o nouă sarcină atunci când este gata.
Benzi prioritare: cozi separate pe clasă; lucrătorii citesc în ordine [P0→... →Pn] dacă sunt disponibile.
Hash-routing: 'hash (cheie)% cioburi' - pentru procesarea statală/în cache.
11) Termene, termene și SLA-uri
Per-task timeout: „leasing” intern de muncă (în codul lucrătorului) ≤ Vizibilitate Timeout brokerului.
Termenul limită global: sarcina nu are sens după ora T - NACK→DLQ.
Conștient de buget: reducerea muncii (brownout) atunci când se apropie termenul limită (rezultate parțiale).
12) Observabilitate și management
12. 1 Măsurători
'queue _ depth', 'arrival _ rate', 'service _ rate', 'lag' (Kafka), 'invisible _ messages' (SQS).
'succes/eșec/retras _ total', 'retry _ încercări', 'dlq _ in _ total', 'processing _ time _ ms {p50, p95, p99}'.
'idempotency _ hit _ rate', 'dedup _ drops _ total', 'otravă _ totală'.
12. 2 Busteni/Vectorizare
Corelație: 'job _ id',' corelation _ id', cheie de eliminare a duplicatelor.
Marcați 'retry/backoff/dlq' ca evenimente; conectarea de la cererea inițială span.
12. 3 Tablouri de bord/alerte
Declanșatoare: adâncime> X, p99> SLO, creștere DLQ, sarcini blocate (vizibilitate expirată> N), taste fierbinți.
13) Siguranță și conformitate
Izolarea chiriașilor: cozi individuale/spații cheie, ACL-uri, cote.
Criptare pe transport și/sau „în repaus”.
Minimizarea PII în sarcină utilă; hash/ID în loc de PII brut.
Secrete: nu puneți jetoane în corpul de lucru, utilizați seif/refs.
14) Anti-modele
Retrai fără idempotență → operațiuni duplicate/bani „de două ori”.
O coadă uriașă „pentru orice” → fără izolare, întârzieri imprevizibile.
Retrai fără sfârșit fără DLQ → sarcini eterne „otrăvitoare”.
Vizibilitate Timeout <timp de procesare → duplicate în cascadă.
Sarcină utilă mare în coadă → presiune de rețea/memorie; este mai bine să stocați într-un stor obiect și să transferați un link.
Push model fără backpressure → muncitorii se îneacă.
Amestecarea sarcinilor critice și în vrac într-un grup de lucrători.
15) Lista de verificare a implementării
- Clasificați sarcinile după SLA (P0/P1/P2) și volum.
- Selectați un broker/cadru cu semantica și retenția dorită.
- Design chei, priorități, și rutare (hash/cioburi/benzi prioritare).
- Activați backoff + jitter și politica DLQ.
- Implementarea idempotency (chei, upsert, deadstore cu TTL).
- Setați termenele pentru fiecare sarcină, vizibilitate și termene generale.
- Limitați concurența și rata de integrare/chiriași.
- Adâncime/lag auto-scalare cu siguranțe.
- Măsurători/urmărire/alerte; runbooks pe „furtună” și DLQ overflow.
- Teste pentru eșecuri: căderea lucrătorului, mesajul „otrăvitor”, suprasarcină, sarcini lungi.
16) Configurații de probă și cod
16. 1 Țelină (Redis/Iepure) - fluxul de bază
python app = Celery("jobs", broker="amqp://...", backend="redis://...")
app.conf.task_acks_late = True # ack после выполнения app.conf.broker_transport_options = {"visibility_timeout": 3600}
app.conf.task_default_retry_delay = 5 app.conf.task_time_limit = 300 # hard timeout
@app.task(bind=True, autoretry_for=(Exception,), retry_backoff=True, retry_jitter=True, max_retries=6)
def process_order(self, order_id):
if seen(order_id): return "ok" # идемпотентность do_work(order_id)
mark_seen(order_id)
return "ok"
16. 2 RabbitMQ - DLQ/TTL
ini x-dead-letter-exchange=dlx x-dead-letter-routing-key=jobs.dlq x-message-ttl=600000 # 10 минут x-max-priority=10
16. 3 Kafka - Retrăiri după nivel
orders -> orders.retry.5s -> orders.retry.1m -> orders.dlq
(Transfer cu livrare întârziată prin programator/cron-consumator.)
16. 4 NATS JetStream - backoff pentru с consumatorilor
bash nats consumer add JOBS WORKERS --filter "jobs.email" \
--deliver pull --ack explicit --max-deliver 6 \
--backoff "1s,5s,30s,2m,5m"
17) ÎNTREBĂRI FRECVENTE
Î: Când să alegeți push versus pull?
R: Pull oferă backpressure natural și echilibrare „onestă”; împingerea este mai ușoară la viteze mici și atunci când este nevoie de TTFB minim, dar necesită limitatoare.
Î: Cum să evitați o cheie fierbinte?
R: Shard by compozite key ('order _ id% N'), tampon și lot-proces, introduceți limite per cheie.
Î: Este posibil să „exact o dată”?
R: Practic - prin idempotenta si outbox tranzactional. Complet „matematic” exact-o dată este rareori realizabil și scump tot drumul.
Î: În cazul în care pentru a stoca atașamente de sarcini mari?
R: În stocarea obiectelor (S3/GCS), iar în sarcină - link/ID; reduce presiunea asupra brokerului și a rețelei.
Î: Cum de a alege TTL/vizibilitate?
R: Vizibilitate ≥ timp de procesare p99 × stoc 2-3 ×. Sarcini TTL - mai puțin termen limită de afaceri.
18) Totaluri
Un sistem puternic de așteptare este un echilibru între semantica de livrare, priorități și constrângeri. Proiectați cheile și rutarea, asigurați idempotența, retrăiți cu backoff și DLQ, alocați resurse claselor SLA și monitorizați valorile. Apoi, procesele de fundal vor fi previzibile, stabile și scalabile - fără surprize sub vârfuri.