Webhooks: reluări și confirmări
1) Model de livrare de bază
Cel puțin o dată (implicit) - Evenimentul va fi livrat de ≥1 ori. Exact odată ce garanțiile sunt realizate prin idempotența receptorului.
Confirmare (ACK): numai orice 2xx (de obicei 200/204) de la destinatar înseamnă succes. Orice altceva este interpretat ca un eșec și duce la repetare.
ACK rapid: Răspundeți 2xx după plasarea evenimentului la rândul său, nu după procesarea completă a afacerii.
2) Formatul evenimentului și rubricile obligatorii
Sarcină utilă (exemplu)
json
{
"id": "evt_01HXYZ",
"type": "order. created",
"occurred_at": "2025-11-03T18:10:12Z",
"sequence": 128374,
"source": "orders",
"data": { "order_id": "o_123", "amount": "49. 90", "currency": "EUR" },
"schema_version": 1
}
Anteturile expeditorului
'X-Webhook-Id: evt_01HXYZ' - ID eveniment unic (utilizarea pentru eliminarea duplicatelor).
„X-Webhook-Seq: 128374” - secvență monotonă (prin abonament/temă).
'X-Signature: sha256 = <base64 (hmac_sha256 (corp, secret))>' - HMAC- подпись.
"X-Retry: 0,1,2... "este numărul de încercare.
„X-Webhook-Version: 1” - versiunea contractului.
(opțional) „Traceparent” - corelație a urmelor.
Răspuns de la destinatar
2xx - acceptat cu succes (nu vor mai exista repetari pentru acest 'id').
410 Gone - punctul final șters/expeditorul → inactiv termină retriile și dezactivează abonamentul.
429/5xx/timeout - expeditorul repetă în conformitate cu politica de retractare.
3) Politica de Retries
Scară de backoff recomandată (+ jitter)
'1s, 3s, 10s, 30s, 2m, 10m, 30m, 2h, 6h, 24h' (oprire după limită, de exemplu 48-72 de ore).
Reguli:- Backoff exponențial + jitter aleatoriu (± 20-30%) pentru a evita „efectul de turmă”.
- Cvorum de erori pentru defecțiuni temporare (de exemplu, încercați din nou dacă 5xx sau timeout de rețea).
- Respectați 429: setați minimum 'min (antet Retry-After, următoarea fereastră de backoff)'.
Timeout și dimensiuni
Timp de conectare ≤ 3-5 secunde; timp total de răspuns ≤ 10 secunde
Dimensiunea organismului în cadrul contractului (de exemplu, ≤ 256 KB), în caz contrar 413 → logica "chunking" sau "pull URL'.
4) Idempotență și deduplicare
Aplicație idempotentă: procesarea repetițiilor aceluiași 'id' trebuie să returneze același rezultat și să nu schimbe din nou starea.
Stocare Dedup pe partea destinatarului: stocați '(X-Webhook-Id, processed_at, checksum)' cu ferestre TTL ≥ retray (24-72 ore).
Cheie compozițională: dacă mai multe subiecte → '(subscription_id, event_id)'.
5) Ordine și „efecte exact o dată”
Este dificil să se garanteze o ordine strictă în sistemele distribuite. Utilizare:- Partiție după cheie: același set logic (de exemplu, "order _ id') este întotdeauna într-un singur" canal "de livrare.
- Secvență: Respingeți evenimentele cu vechiul „X-Webhook-Seq” și puneți-le în „parcare” înainte ca cele lipsă să ajungă.
- jurnalul operațiunilor aplicate (modelul outbox/inbox)
- upsert tranzactional prin 'event _ id' in baza de date,
- sagas/compensații pentru procese complexe.
6) Rezoluția erorilor după codurile de stare (Tabel)
7) Securitatea canalului
Semnătura HMAC a fiecărui mesaj; verificați la receptor cu „fereastra de timp” (mitm și reluarea atacurilor).
mTLS pentru domenii sensibile (LCC/plăți).
Lista IP a adreselor de ieșire, TLS 1. 2 +, HSTS.
Minimizarea PII: nu trimiteți date cu caracter personal inutile; deghizaţi-vă în buşteni.
Rotația secretelor: două chei valide (active/next) și antetul "X-Key-Id' pentru a indica cea curentă.
8) Cozi, DLQ-uri și reluări
Evenimentele trebuie să fie scrise în coada de ieșire/jurnal pe partea expeditorului (pentru reluare fiabilă).
În cazul în care maximul de retroys este depășit, evenimentul merge la DLQ (Dead Letter Queue) cu cauza.
Replay API (pentru destinatar/operator): retrimiteți prin "id'/interval de timp/subiect, cu restricție SPR și semnătură/autorizație suplimentară.
POST /v1/webhooks/replay
{ "subscription_id": "sub_123", "from": "2025-11-03T00:00:00Z", "to": "2025-11-03T12:00:00Z" }
→ 202 Accepted
9) Contract și versiune
Versiunea evenimentului (câmpul „schema _ versiune”) și transportul („X-Webhook-Version”).
Adăugați câmpuri numai ca opțional; la ștergere - migrație minoră și perioadă de tranziție (dual-write).
Tipuri de evenimente, exemple, scheme (scheme JSON), coduri de eroare.
10) Observabilitate și SLO
Indicatorii cheie ai expeditorului:- 'delivery _ success _ rate' (2xx/toate încercările), 'first _ try _ success _ rate'
- 'retries _ total', 'max _ retry _ age _ seconds',' dlq _ count'
- 'latency _ p50/p95' (occurred_at → ack_received_at)
- 'back _ latency' (receive → 2xx), 'processing _ latency' (enqueue → done)
- 'duplicates _ total', 'nevalid _ signature _ total', 'out _ of _ order _ total'
99. 9% din evenimente primesc primul ACK ≤ 60 secunde (28d).
- DLQ ≤ 0. 1% din total; DLQ reluare ≤ 24 de ore.
11) Sincronizare și întreruperi de rețea
Utilizați UTC în câmpurile de timp; sincronizează NTP.
Trimiteți 'happened _ at' și fixați' delivered _ at' pentru a citi decalajul.
Cu pauze lungi, rețeaua/punctul final se → acumula în coadă, limita creșterea (backpressure + cote).
12) Limite și igienă recomandate
SPR per abonament (ex. 50 RPS, explozie 100) + concurență (de ex. 10).
Max. corp: 64-256 KB; pentru mai multe - „notificare + URL” și semnătura de descărcare.
Numele evenimentului în şarpe. caz „sau” punct. tip „(” ordine. creat ").
Idempotența strictă a operațiunilor de scriere a receptorului.
13) Exemple: Expeditor și destinatar
13. 1 Expeditor (pseudocod)
python def send_event(event, attempt=0):
body = json. dumps(event)
sig = hmac_sha256_base64(body, secret)
headers = {
"X-Webhook-Id": event["id"],
"X-Webhook-Seq": str(event["sequence"]),
"X-Retry": str(attempt),
"X-Signature": f"sha256={sig}",
"Content-Type": "application/json"
}
res = http. post(endpoint, body, headers, timeout=10)
if 200 <= res. status < 300:
mark_delivered(event["id"])
elif res. status == 410:
deactivate_subscription()
else:
schedule_retry(event, attempt+1) # backoff + jitter, respect 429 Retry-After
13. 2 Receptor (pseudocod)
python
@app. post("/webhooks")
def handle():
body = request. data headers = request. headers assert verify_hmac(body, headers["X-Signature"], secret)
evt_id = headers["X-Webhook-Id"]
if dedup_store. exists(evt_id):
return, "" 204 enqueue_for_processing (body) # fast path. dedup_store put(evt_id, ttl=723600)
return, "" 202 # or 204
14) Testarea și practicile haosului
Cazuri negative: semnătură invalidă, 429/5xx, timeout, 410, sarcini utile mari.
Comportament: out-of-order, duplicate, întârzieri de 1-10 minute, pauză timp de 24 de ore.
Încărcare: spargere 10 ×; verificați dacă persistă presiunea și DLQ.
Contracte: Schema JSON, rubrici obligatorii, tipuri de evenimente stabile.
15) Lista de verificare a implementării
- 2xx = ACK și întoarcere rapidă după solicitare
- Backoff exponențial + jitter, respect 'Retry-Aftery'
- Receiver IDempotency și X-Webhook-Id (TTL ≥ Retray)
- Semnături HMAC, rotație secretă, mTLS opțional
- DLQ + Replay API, monitorizare și alerte
- Limite: Timeout, RPS, Dimensiunea corpului
- Ordine: partiție după cheie sau „secvență” + „parcare”
- Documentație: scheme, exemple, coduri de eroare, versiuni
- Teste de haos: întârzieri, duplicate, eșec de rețea, reluare lungă
16) Mini-Întrebări frecvente
Întotdeauna trebuie să răspund la 200?
Orice 2xx contează ca un succes. 202/204 este o practică normală pentru „acceptat la coadă”.
Reluările pot fi oprite?
Da, un răspuns 410 și/sau prin consola expeditorului/API (dezabonare).
Ce zici de sarcini utile mari?
Trimiteți o „notificare + URL securizat”, semnați cererea de descărcare și instalați TTL.
Cum să asigurați ordinea?
Partiție după cheie + „secvență”; în caz de discrepanță - „parcare” și reluare.
Total
Cărțile web fiabile sunt semantică ACK (2xx) clară, repetări rezonabile cu backoff + jitter, idempotență strictă și eliminare a duplicatelor, securitate competentă (HMAC/mTLS), cozi + reluări DLQ + și observabilitate transparentă. Fixați contractul, introduceți limite și valori, rulați în mod regulat scenarii de haos - iar integrările dvs. vor înceta „să se reverse” la primele eșecuri.