Optimizarea răspunsului backend: cozi, async, backpressure
1) De ce: Obiective și SLOs
Scopul este un răspuns rapid stabil chiar și sub explozii. Business exprimă acest SLO:- API (CRUD/directoare): p95 ≤ 250-400 ms, rata de eroare <1%.
- Plata/decontare (asincron): SLA intern pentru confirmare ≤ 2-5 minute, iar clientul - instant 202/Accepted + status poller/webhook.
- WS/timp real: RTT p95 ≤ 120 мс, deconectați ≤ 0. 5%.
Cheie: pentru a dezlega pașii „lenți” (furnizori, baze de date, API-uri externe) de la răspunsul utilizatorilor prin cozi și limitarea competentă a încărcării.
2) Imaginea de bază: în cazul în care latența este luată
Blocaje: baza de date (piscine/indici), furnizori externi (PSP/joc), blocarea I/O, GC/stop world, serializarea JSON, agregări „grele”.
Simptome: creștere p99, coadă de conexiune DB, explozii retray, încercați din nou furtuna.
Antidot: conducte asincrone + backpressure + timeout/retreats + idempotency.
3) Modele asincrone: SEDA și CQRS
SEDA (arhitectură bazată pe evenimente): împărțirea procesării în etape (intrare validare scriere integrare). Fiecare are propriile limite de rotație și concurență.
CQRS: Separat citește și scrie. Scrierea - la jurnalul/baza de date, citirea - de la proiecții/cache-uri.
Outbox: evenimentul este publicat atomic împreună cu înregistrarea (evitați mesajele „pierdute”).
Saga: procese de afaceri lungi cu tranzacții compensatoare în loc de cele globale.
4) Cozi și fluxuri: selecție și tuning
RabbitMQ/NATS JetStream - comenzi de sarcini (cozi de lucru), Kafka - evenimente/fluxuri cu reluare.
Setări care afectează răspunsul:- Prefetch/max în timpul zborului: limitați numărul de mesaje procesate simultan per lucrător (de exemplu, 16-64) pentru a nu „bloca” baza de date/API extern.
- Acker/repetă: „ack” după înregistrarea idempotentă; întârziere exponențială și jitter se repetă.
- DLQ/parcare: nu există retrageri nesfârșite - după încercările N, merge la Dead Letter Queue.
- Partiționarea (Kafka): cheie după esență (userId/txnId) pentru comandă; paralelism prin numărul de partide.
5) Backpressure - cum să nu se înece
Ideea: Luați doar cât puteți procesa în latența SLO.
Tehnicieni:- Controlul admiterii: concurență limită (semafor/worker-pool) pentru fiecare dependență externă: bază de date, PSP, furnizor de jocuri.
- Modelarea traficului: token-bucket/leaky-bucket la intrarea în serviciu și pe rutele critice.
- Cozi cu o margine superioară: când este plină, tăiați coada (429/503 + Retry-After) sau transferați la un lot asap.
- Concurența adaptivă (AIMD): creșterea paralelismului privind succesul, scăderea timpilor.
- Întrerupător de circuit: „închis → deschis → semi-deschis” prin erori/timeout-uri ale API extern; atunci când sunt deschise - degradare (cache/ciot).
go sem: = make (chan struct {}, 64 )//limită de concurență la DB/PSP
mâner func (req) {
selectați {
case sem <- struct {} {}:
defer funcc () {<-sem} ()
ctx, anulează: = context. WithTimeout (req. ctx, 300time. Millisecond)
amânare anulare ()
res, err: = db. Do (ctx, req)
if err = = context. DeadlineDepășit {metrics. Timeout. Inc (); return TooSlow ()}
retur Ok (res)
implicit:
măsurători. Backpressure. Inc ()
Return TooBusy (429 ", Retry-After: 0. 2")
}
}6) Timeouts, retrageri și jitter: „trei balene de supraviețuire”
Termene mai scurte decât SLO: dacă SLO 400 ms, timp de ieșire la DB/furnizor 250-300 ms; timp total de solicitare <400-600 ms.
Retrai limitat și inteligent: 1-2 încercări max, numai pentru operațiuni sigure (idempotent), cu exponent și jitter.
Coalessing: Reluări agregate pentru o singură cheie.
Pseudocodul (exponent + jitter):python pentru încercarea în intervalul (0, 2):
încercați:
apel retur (rep, timeout = 0. 3)
cu excepția Timeout:
backoff = (0. 05 (2attempt)) + aleatoriu. uniformă (0, 0. 05)
somn (backoff)
ridica UpstreamIndisponibil7) Identitate și eliminare a duplicatelor
Idempotency-Key pe HTTP (depozite, plăți), "operation _ id' în baza de date (index unic).
Inbox/Outbox: cărți web primite - întotdeauna printr-un tabel inbox neschimbabil cu dedupe prin 'event _ id'; outbound - din outbox prin tranzacție.
Exact o dată „în sensul”: permitem livrarea/execuția repetată, dar există un singur efect.
8) API rapid pentru operații lente
Răspuns sincron: 201/202 + URL de stare ('/status/{ id} '), ETA și sugestii retro.
Webhooks/Server-Sent Events/WS - împinge starea atunci când este gata.
Disciplina client: „Retry-After”, idempotenta, limita de votare.
Exemplu de răspuns:json
HTTP/1. 1 202 Acceptat
Location: v1/withdrawals/req_9f2/status
Retry-After: 2
{
„request_id": „req_9f2,” „stat”: „prelucrare” „, next_check_sec": 2
}9) Minimizați munca la cald
Puneți lucruri grele în fundal: transformări, agregări, notificări, scrierea la DWH.
Cache și proiecții: citite în mod obișnuit - cache-deoparte cu TTL scurt și handicap eveniment.
Modele de lot: apeluri externe de grup (ex. cerere limitele furnizorului o dată în N ms).
Serializare: codec-uri rapide (protobuf/msgpack) pentru comunicații service-to-service; Doar JSON la limită.
10) DB sub control
Piscine de conectare: limite superioare (bazate pe nuclee/IO), cozi pentru a piscina activat.
Indici și plan: p95 explica + autotesturi de regresie de planuri.
Solicitați timeout: scurt, 'statement _ timeout' (Postgres).
Rânduri/încuietori fierbinți: cioburi cheie, încuietori optimiste (versiunea de echilibru), saga în loc de o tranzacție „monolitică”.
11) WebSocket/în timp real
Limitator newsletter: difuzare lotched, max msgs/sec per conexiune.
Backpressure intern: coadă de mesaje de ieșire cu un capac; la depășire - picătură cu prioritate scăzută.
Rutarea lipicioasă și PDB în timpul lansărilor - pentru a nu produce o furtună de reconectare.
12) Observabilitate pentru a nu ghici
Valori (RED/USE + backpressure):- 'request _ rate', 'error _ ratio', 'latency _ p95/p99' pe rute.
- 'queue _ adancime', 'lag _ seconds',' consumer _ inflight', 'retries _ total', 'dlq _ rate'.
- 'backpressure _ drops',' admission _ rejects', 'circuit _ open'.
- Для БД: 'connections _ in _ use/max', 'locks',' slow _ queries '.
- Urme: se întinde „coadă → lucrător → db/psp 'cu etichete” operation _ id', „partiție”, „încercați din nou”.
- Jurnale: structurale, cu 'trace _ id', fără PII; evenimente individuale „open/close circuit”.
13) Testarea sarcinii
Model deschis (sosiri/sec) pentru explozii; Model închis (VU) pentru sesiuni.
Profile: scurta spargere 60-120 s si inmuiati 1-4 h.
Eșecuri de injecție: încetiniți API-ul extern cu + 200-500 ms, uitați-vă la p99/retrai/cozi.
Criterii zona verde: nici o creștere „coadă _ lag”, stabil p95, „dlq_rate≈0”.
14) Siguranță și fiabilitate
Cozi TLS/mTLS, semnarea mesajelor, monitorizarea schemelor (Avro/Protobuf + Schema Registry).
Producător idempotent (Kafka), exact o dată tx acolo unde este justificat.
Modul haos: periodic „picătură” dependența și uita-te la degradare (circuit, rezervă).
15) Exemple de „piese” de configurații
Modelarea intrării Nginx/Envoy:nginx limit_req_zone $ binary _ remote _ addr zone = api: 10m rate = 20r/s;
server {
locație/api/{
limit_req zone = api burst = 40 nodelay;
proxy_read_timeout 0. 6s; # este mai scurt decât SLO proxy_connect_timeout 0. 2s;
}
}
de bază. qos (prefetch_count = 32) # echilibru CPU/IOjava recuzită. (ConsumerConfig. MAX_POLL_RECORDS_CONFIG, 200);
recuzită. (ConsumerConfig. FETCH_MAX_BYTES_CONFIG, 5_000_000);
recuzită. (ConsumerConfig. MAX_POLL_INTERVAL_MS_CONFIG, 60_000);16) Lista de verificare a implementării (prod-ready)
- Căile critice sunt împărțite în răspuns sincron și procesare asincronă (SEDA).
- Controlul admiterii și limitele concurenței pentru dependențele externe.
- Timeout-urile sunt mai scurte decât SLO; retrai ≤ 2, cu exponent și jitter; coalizare.
- Întrerupător de circuit + degradare (cache/ciot), politică semi-deschisă.
- Cozi/fluxuri: prefetch/in-zbor, DLQ, loturi cheie.
- Idempotency (operation_id/Idempotency-Key), Outbox/Inbox, deduplication.
- Cache: cache-deoparte, scurt TTL + eveniment de handicap.
- DB: limite de piscină, statement_timeout, indici, strategii anti-blocare.
- WS: limitele mesajului, butching, lipicios-rutare, PDB.
- Observabilitate: backpressure/cozi/metrici de retragere, trasee end-to-end, tablouri de bord.
- Încercări de încărcare și eșec (deschis + închis, izbucnire + înmuiere), criterii de zonă verde.
Rezumat reluare
Un backend rapid nu este un „make another cache”, ci un flux controlat: intrarea este limitată, grea - în fundal, fiecare etapă cu o coadă și limite, retraiele sunt rare și inteligente, iar lanțurile sunt protejate de întrerupător de circuit și idempotență. Adăugați disciplina timeout, observabilitatea și testele regulate de stres - iar p95/p99 va rămâne verde chiar și sub exploziile și capriciile furnizorilor externi.
