Timeout и controlul circuitului
1) De ce aveți nevoie de ea
Sistemele nu cad dintr-o defecțiune „fatală”, ci din acumularea de întârzieri și retrasări „val”. Limitarea timpului de așteptare și eliberarea resurselor, iar controlul circuitului (întrerupător + vărsare + competiție adaptivă) împiedică răspândirea degradării de-a lungul lanțului de dependențe. Scopul este de a menține p95/p99 în limitele țintă și de a menține disponibilitatea pentru eșecuri parțiale.
2) Definiții de bază
2. 1 Tipuri de intervale de timp (L7/L4)
Conectați timeout - Stabiliți o conexiune TCP/TLS.
TLS/Strângere de mână timeout - strângeri de mână TLS/HTTP2 prefață.
Scrieți timeout - trimiteți o cerere (inclusiv un corp).
Citiți timeout - așteptați primul octet al răspunsului și/sau al întregului corp.
Idle/Keep-Alive timeout - conexiune inactivă.
Termenul general - termenul limită „greu” pentru întreaga cerere (end-to-end).
2. 2 Bugetul limită
Selectați ținta „deadline _ total” și împărțiți pe etape:- 'ingress (gateway) + authZ + app + DB/cache + PSP de ieșire'.
- gateway: 30 ms,
- cerere: 120 ms,
- DB: 120 ms,
- PSP: 100 ms,
- marjă: 30 ms.
2. 3 Propagarea și anularea
"deadline "/" timeout 'trebuie transmis în jos lanțul (context, anteturi, gRPC Deadline). La expirare - anulați operațiunile de fundal (anulare/anulare ctx), ștergeți încuietorile/semafoarele.
3) Stabilirea strategiilor de timeout
1. Sus în jos: pe baza SLO și p95 - setați termenul limită end-to-end, apoi împărțiți în sub-timeout.
2. Identificați căile „scumpe” (descărcări de fișiere, rapoarte, PSP-uri externe) - individuale mai lungi, dar limitate.
- idempotent (repetiții GET/stare) - mai scurt, mai agresiv;
- scrie/monetar - puțin mai lung, dar cu o singură repetare și idempotență.
4. Absolvirea prin planuri/chiriași (întreprinderea poate avea o perioadă mai lungă de timp, dar mai puțin paralelism).
4) Întrerupător de circuit: modele și parametri
4. 1 Politici de declanșare
Rata de eșec - rata de eroare ≥ X% pe fereastra de interogare/timp N.
Eșecuri consecutive: M eșecuri consecutive.
Rata apelurilor lente - proporția apelurilor mai lungi decât pragul T.
Clase de erori: timeouts/5xx/connection-reset → „fatal”, 4xx - nu luați în considerare.
4. 2 Condiții
Închis - sare peste tot, acumulează statistici.
Deschideți - eșec instantaneu (economisește resurse, nu zdrobește dependența).
Jumătate deschis - mici „eșantioane” (cereri N) pentru „testarea apei”.
4. 3 Adăugări utile
Perete etanș: un bazin de fire/conexiuni pe dependență, astfel încât să nu „suge” totul.
Concurență adaptivă: restricție automată de concurență (algoritmi AIMD/Vegas) prin latență observată.
Descărcarea încărcăturii: eșec precoce/degradare în caz de lipsă de resurse locale (cozi, CPU, GC pauze).
5) Interacțiune: timeout, retrageri, limite
Primul termen limită, apoi retransmiterea: fiecare repetare ar trebui să se încadreze într-un termen comun.
Backoff + jitter pentru reluări; respectați „Retry-After” și încercați din nou bugetul.
Limitarea ratei: Cu întrerupător deschis - limite mai mici pentru a nu intensifica furtuna.
Idempotency: obligatoriu pe operații de scriere (pentru a evita ia cu „prost” timeout).
În cazul în care pentru a retrage: de preferință la marginea (client/gateway), mai degrabă decât adânc în interior.
6) Valori țintă practice (repere)
Public read API: end-to-end "200-500 ms', citiți ora" 100-300 ms ".
Scriere critică (plăți): „300-800 ms” e2e; PSP extern ≤ "250-400 ms'.
Connect/TLS: '50-150 ms' (mai multe - probleme de rețea/lipit).
Inactiv: '30-90' (clienți mobili - mai scurt pentru a economisi baterie).
Ajustați valorile pentru p95/p99 și regiuni.
7) Configurări și exemple
7. 1 Trimis (cluster + traseu, pseudo)
yaml clusters:
- name: payments_psp connect_timeout: 100ms type: STRICT_DNS lb_policy: ROUND_ROBIN circuit_breakers:
thresholds:
- priority: DEFAULT max_connections: 2000 max_requests: 2000 max_retries: 50 outlier_detection:
consecutive_5xx: 5 interval: 5s base_ejection_time: 30s max_ejection_percent: 50
routes:
- match: { prefix: "/api/v1/payments" }
route:
cluster: payments_psp timeout: 350ms # per-request deadline idle_timeout: 30s retry_policy:
retry_on: "reset,connect-failure,refused-stream,5xx,gateways"
num_retries: 1 per_try_timeout: 200ms
7. 2 NGINX (perimetru)
nginx proxy_connect_timeout 100ms;
proxy_send_timeout 200ms; # write proxy_read_timeout 300ms; # read (первый байт/все тело)
keepalive_timeout 30s;
send_timeout 15s;
Быстрый отказ при перегрузке limit_conn_zone $binary_remote_addr zone=addr:10m;
limit_conn addr 50;
7. 3 gRPC (client, Go-pseudo)
go ctx, cancel:= context.WithTimeout(context.Background(), 350time.Millisecond)
defer cancel()
resp, err:= client.Pay(ctx, req) // Deadline передается вниз
7. 4 client HTTP (Du-te)
go client:= &http.Client{
Timeout: 350 time.Millisecond, // общий дедлайн на запрос
Transport: &http.Transport{
TLSHandshakeTimeout: 100 time.Millisecond,
ResponseHeaderTimeout: 250 time.Millisecond,
IdleConnTimeout: 30 time.Second,
MaxIdleConnsPerHost: 100,
},
}
7. 5 Resilience4j (Java, pseudo)
yaml resilience4j.circuitbreaker.instances.psp:
slidingWindowType: TIME_BASED slidingWindowSize: 60 failureRateThreshold: 50 slowCallDurationThreshold: 200ms slowCallRateThreshold: 30 permittedNumberOfCallsInHalfOpenState: 5 waitDurationInOpenState: 30s
resilience4j.timelimiter.instances.psp:
timeoutDuration: 350ms
8) Observabilitate și alertare
8. 1 Măsurători
'http _ client _ requires {endpoint, status}', 'client _ latency _ bucket'
8. 2 Trasee
Spans: handler → de intrare → DB/Redis → extern.
Atribute: 'timeout _ ms _ target', 'circuit _ state', 'queue _ time _ ms'.
Exemplare: Tie p99 vârfuri la anumite urme-id.
8. 3 Alerte
'p99 _ latency {critical}'> vizează X minute la rând.
'timeout _ rate {dependency}' hopped> Y%.
Tranziții frecvente la întrerupătorul „deschis ”/„ flapping”.
Growth of 'shed _ requires _ total' with high CPU/GC.
9) Adaptarea concurenței și a pierderii de sarcină
9. 1 Idee
Automatizarea reduce paralelismul pe măsură ce cozile de latență cresc:- AIMD: creșteți încet, scădeți brusc.
- Vegas-cum ar fi: păstrați timpul de coadă.
- Token-based: fiecare cerere „arde” jetonul; jetoanele sunt emise pe baza vitezei măsurate.
9. 2 Punerea în aplicare
Semafoare locale pe traseu; scopul este de a menține „coadă _ timp” sub prag.
„Siguranța” globală (RPS marginal/competitiv) pe poarta de acces.
Dacă există o lipsă de CPU/conexiuni, eșec timpuriu înainte de execuția logică (429/503 cu 'Retry-After').
10) Scenarii de testare și haos
Injecție de latență: adăugați artificial 50-300 ms pe dependență.
Pierderea pachetului/după/picătură (tc/tbf, Toxiproxy).
Rotirea butonului: reducerea piscinelor de conectare, creșterea sarcinii la saturație.
Kill/Degradați o zonă/ciob (indisponibilitate parțială).
Verificări: nu „eșuează” furtuna din spate; întrerupătorul se deschide previzibil; dacă coada este în creștere.
11) Antipattern
Un „timeout de citire” global fără conectare detaliată/TLS/per-stage.
Lipsa unui termen limită comun → retroactive depășește SLO.
Retrai fără jitter și fără retry-buget.
Conexiuni „eterne” fără temporizări inactive → descriptori de scurgeri.
Breaker numara 4xx ca greseli fatale.
Nu anulați/abandonați activitatea → fundal continuă după timeout-ul clientului.
Timeout-urile sunt prea lungi pentru rețelele mobile/instabile.
12) Specificul iGaming/Finanțe
Scriere critică (depozite/ieșiri): o scurtă repetare cu Idempotency-Key, apoi „202 Acceptat” + sondare în loc de așteptări infinite.
PSP/banking: politici separate pe furnizori/regiuni (unele mai lente).
Plăți și limite responsabile: pentru blocări/recenzii - rapid '423/409', nu întindeți tranzacțiile „agățate”.
Raportare/agregare - rulați asincron (lot + resursă de stare).
13) Lista de verificare Prod Readiness
- End-to-end termen de traseu critic (GET/POST) definit.
- Bugetat pe etape; este activată propagarea termenului limită.
- Connect/TLS/read/write/inactive configs on gateway and clients.
- Întrerupător cu rata de eșec și praguri de apel lent; logica corectă pe jumătate deschisă.
- Pereți etanși pe dependențe; limitele de concurență pe traseu.
- Încărcați vărsarea înainte ca logica de afaceri să fie executată în timpul supraîncărcării.
- Integrarea cu retrageri: backoff + jitter, reîncercați-buget, respectați 'Retry-After'.
- Idempotency scrie, „Idempotency-Key” și outbox pentru evenimente.
- Metrics: timeout/lent-call/breaker/coadă de timp/competitiv.
- Teste de haos: injectarea de întârzieri/pierderi/eșecuri, degradarea zonelor.
- Documentația clientului: calendare de eșantionare, coduri de răspuns, sfaturi de reluare.
14) TL; DR
Dați fiecărei cereri un termen limită greu, aranjați-l în etape și răspândiți-l în jos pe lanț. Gestionați defecțiunile prin întrerupător de circuit + pereți etanși + concurență adaptivă + vărsare de sarcină. Reluări - numai în termenul limită, cu jitter și buget; scrie - numai idempotent. Măsurați timeout/lent-call, statul întrerupătorului și „coadă _ timp”, conduceți în mod regulat teste de haos.