Haki internetowe: powtórki i potwierdzenia
1) Podstawowy model dostawy
Co najmniej raz (domyślnie) - Zdarzenie zostanie dostarczone ≥ 1 razy. Dokładnie raz gwarancje są osiągane przez odbiornik iempotencji.
Potwierdzenie (ACK): tylko każde 2xx (zwykle 200/204) od odbiorcy oznacza sukces. Wszystko inne jest interpretowane jako porażka i prowadzi do powtórzenia.
Szybki ACK: Odpowiedź 2xx po umieszczeniu zdarzenia z kolei, nie po pełnym przetwarzaniu biznesowym.
2) Format zdarzeń i obowiązkowe nagłówki
Ładunek użytkowy (przykład)
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
}
Nagłówki nadawcy
'X-Webhook-Id: evt_01HXYZ' - niepowtarzalny identyfikator zdarzenia (użycie do deduplikacji).
'X-Webhook-Seq: 128374' - sekwencja monotonna (według subskrypcji/tematu).
„X-Signature: sha256 = <base64 (hmac_sha256 (ciało, tajemnica))>” - HMAC-бодкиса.
"X-Retry: 0,1,2... "to numer próbny.
„X-Webhook-Version: 1” - wersioning kontraktu.
(nieobowiązkowo) "Traceparent' - korelacja śladowa.
Odpowiedź odbiorcy
2xx - pomyślnie zaakceptowane (nie będzie kolejnych powtórzeń dla tego 'ida').
410 Odszedł - punkt końcowy usunięty/nieaktywny → nadawca kończy ponowne próby i wyłącza subskrypcję.
429/5xx/timeout - nadawca powtarza zgodnie z zasadami retray.
3) Polityka ponownych prób
Zalecana drabina backoff (+ jitter)
„1s, 3s, 10s, 30s, 2m, 10m, 30m, 2h, 6h, 24h” (zatrzymać się po granicy, na przykład 48-72 godziny).
Zasady:- Wykładniczy backoff + losowy jitter (± 20-30%), aby uniknąć „efektu stada”.
- Kworum błędów w przypadku awarii tymczasowych (na przykład ponowne spróbowanie, jeśli 5xx lub network timeout).
- Respect 429: ustaw minimum 'min (nagłówek Retry-After, następne okno backoff)'.
Czasy i rozmiary
Czas połączenia ≤ 3-5 sekund; całkowity czas odpowiedzi ≤ 10 sekund
Wielkość ciała w ramach umowy (na przykład ≤ 256 KB), inaczej 413 → logika "chunking" lub "pull URL'.
4) Idempotencja i deduplikacja
Aplikacja Idempotent: przetwarzanie powtórzeń tego samego 'ida' musi zwrócić ten sam wynik i nie zmieniać ponownie stanu.
Pamięć dedup po stronie odbiorcy: sklep '(X-Webhook-Id, processed_at, checksum)' z TTL ≥ windows retray (24-72 godziny).
Klucz kompozycyjny: jeśli kilka tematów → '(subscription_id, event_id)'.
5) Zamówienie i „dokładnie raz efekty”
Trudno jest zagwarantować ścisłe zamówienie w systemach rozproszonych. Zastosowanie:- Partycja według klucza: ten sam zestaw logiczny (na przykład 'order _ id') jest zawsze w jednym „kanale” dostawy.
- Sekwencja: Odrzuć wydarzenia ze starym 'X-Webhook-Seq' i umieścić je na „parkingu”, zanim brakujące przyjadą.
- dziennik zastosowanych operacji (wzór skrzynki odbiorczej/skrzynki odbiorczej),
- upsert transakcyjny przez 'event _ id' w bazie danych,
- sagi/kompensacje za złożone procesy.
6) Rozdzielczość błędów według kodów stanu (tabela)
7) Bezpieczeństwo kanału
podpis HMAC każdej wiadomości; sprawdź w odbiorniku „okno czasu” (mitm i ataki powtórne).
mTLS dla domen wrażliwych (LCC/płatności).
Lista adresów wychodzących, TLS 1. 2 +, HSTS.
Minimalizacja PII: nie wysyłaj zbędnych danych osobowych; przebranie w dziennikach.
Obrót tajemnic: dwa poprawne klucze (aktywny/następny) i nagłówek 'X-Key-Id', aby wskazać bieżący.
8) Kolejki, DLQ i powtórki
Zdarzenia muszą być zapisane do kolejki wyjściowej/dziennika po stronie nadawcy (dla wiarygodnego powtórzenia).
W przypadku przekroczenia maksymalnej liczby przekładek zdarzenie trafia do DLQ (Dead Letter Queue) z przyczyną.
Powtórz interfejs API (dla odbiorcy/operatora): ponowne przesłanie przez 'id'/time range/subject, z ograniczeniem RPS i dodatkowym podpisem/autoryzacją.
POST /v1/webhooks/replay
{ "subscription_id": "sub_123", "from": "2025-11-03T00:00:00Z", "to": "2025-11-03T12:00:00Z" }
→ 202 Accepted
9) Umowa i wersja
Wersja zdarzenia (pole 'schema _ version') i transport ('X-Webhook-Version').
Dodaj pola tylko jako opcjonalne; w sprawie usuwania - drobna migracja i okres przejściowy (podwójne zapisy).
Typy zdarzeń dokumentów, przykłady, schematy (Schematy JSON), kody błędów.
10) Obserwowalność i SLO
Metryka klucza nadawcy:- 'delivery _ success _ rate' (2xx/wszystkie próby), 'first _ attempt _ success _ rate'
- 'retries _ total', 'max _ retry _ age _ seconds', 'dlq _ count'
- „trwałość _ p50/p95” (occurred_at → ack_received_at)
- 'ack _ latency' (receive → 2xx), 'processing _ latency' (enqueue → done)
- „duplikaty _ total”, „invalid _ signature _ total”, „out _ of _ order _ total”
99. 9% zdarzeń otrzymuje pierwsze ACK ≤ 60 sekund (28d).
- DLQ ≤ 0. 1% całości; DLQ powtórka ≤ 24 godziny.
11) Czas i przerwy w sieci
Użyj UTC w polach czasowych; synchronizacji NTP.
Wyślij 'did _ at' i napraw 'delivered _ at', aby przeczytać lag.
Przy długich przerwach sieć/punkt końcowy → gromadzą się w kolejce, ograniczyć wzrost (backpressure + kwoty).
12) Zalecane limity i higiena
RPS na abonament (np. 50 RPS, rozerwanie 100) + jednoczesność (np. 10).
Max. ciało: 64-256 KB; więcej - "powiadomienie + adres URL' i pobierz podpis.
Nazwy wydarzeń w wężu. przypadek 'lub' kropka. typ „(” zamówienie. stworzony ").
Ścisła idempotencja operacji pisania odbiornika.
13) Przykłady: nadawca i odbiornik
13. 1 Nadawca (pseudokoda)
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 Odbiornik (pseudokod)
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) Praktyki testowania i chaosu
Przypadki negatywne: nieprawidłowy podpis, 429/5xx, timeout, 410, duże ładunki.
Zachowanie: nie w porządku, duplikaty, opóźnienia 1-10 minut, przerwa na 24 godziny.
Obciążenie: pęknięcie 10 ×; sprawdź trwałość backpressure i DLQ.
Kontrakty: JSON Schema, obowiązkowe nagłówki, stabilne typy zdarzeń.
15) Lista kontrolna wdrażania
- 2xx = ACK, i szybki zwrot po zapytaniu
- Wykładnicze backoff + jitter, szacunek „Retry-After”
- Identyfikator odbiornika i X-Webhook-Id (TTL ≥ Retray)
- Podpisy HMAC, tajna rotacja, opcjonalnie mTLS
- DLQ + Replay API, Monitoring i wpisy
- Limity: Timeouts, RPS, Body Size
- Zamówienie: partycja przez klucz lub 'sekwencja' + 'parking'
- Dokumentacja: schematy, przykłady, kody błędów, wersje
- Testy chaosu: opóźnienia, duplikaty, awaria sieci, długa powtórka
16) Mini-FAQ
Czy zawsze muszę odpowiadać na 200?
Każdy 2xx liczy się jako sukces. 202/204 to normalna praktyka dla „przyjętej do kolejki”.
Czy można zatrzymać powtórki?
Tak, odpowiedź 410 i/lub za pośrednictwem konsoli/API nadawcy (zrezygnuj z subskrypcji).
A co z dużymi ładunkami?
Wyślij „powiadomienie + bezpieczny adres URL”, podpisz żądanie pobrania i zainstaluj TTL.
Jak zapewnić porządek?
Partycja przez klucz + 'sekwencja'; w przypadku rozbieżności - „parking” i powtórka.
Razem
Niezawodne webhaki to jasne semantyki ACK (2xx), rozsądne powtórzenia z backoff + jitter, ścisła idempotencja i deduplikacja, kompetentne bezpieczeństwo (HMAC/mTLS), kolejki + DLQ + powtórki i przejrzysta obserwowalność. Napraw kontrakt, wprowadź limity i metryki, regularnie uruchamiaj scenariusze chaosu - a Twoje integracje przestaną „wlewać się” przy pierwszych awariach.