Versandgarantien für Webhooks
Webhooks - Asynchrone Benachrichtigungen „vom System zum Abonnenten“ über HTTP (S). Das Netzwerk ist unzuverlässig: Antworten gehen verloren, Pakete kommen in Duplikaten oder außerhalb der Reihenfolge. Daher werden die Liefergarantien nicht „über TCP“, sondern auf der Ebene des Webhook- und Domain-Idempotence-Protokolls aufgebaut.
Das Hauptziel: eine At-Least-Once-Lieferung mit der Reihenfolge nach Schlüssel (wo nötig) zu gewährleisten, dem Abonnenten Materialien für die idempotente Verarbeitung und ein Reconcile-Tool für die Wiederherstellung zu geben.
1) Garantieniveaus
Best-effort ist ein einmaliger Versuch, ohne Rückblenden. Nur für „unwichtige“ Ereignisse akzeptabel.
At-least-once (empfohlen) - Duplikate und Out-of-Order sind möglich, aber die Veranstaltung wird geliefert, sofern der Abonnent innerhalb eines angemessenen Zeitraums verfügbar ist.
Effectively-exactly-once (auf Effektebene) - erreicht durch eine Kombination aus Idempotenz und Dedup-Speicher auf der Seite des Abonnenten/Absenders. Beim Transport ist HTTP „exactly-once“ nicht möglich.
2) Webhook-Vertrag: minimal notwendig
Überschriften (Beispiel):
X-Webhook-Id: 5d1e6a1b-4f7d-4a3d-8b3a-6c2b2f0f3f21 # глобальный ID события
X-Delivery-Attempt: 3 # номер попытки
X-Event-Type: payment.authorized.v1 # тип/версия
X-Event-Time: 2025-10-31T12:34:56Z # ISO8601
X-Partition-Key: psp_tx_987654 # ключ порядка
X-Seq: 418 # монотонный номер по ключу
X-Signature-Alg: HMAC-SHA256
X-Signature: t=1730378096,v1=hex(hmac(secret, t body))
Content-Type: application/json
Körper (Beispiel):
json
{
"id": "5d1e6a1b-4f7d-4a3d-8b3a-6c2b2f0f3f21",
"type": "payment.authorized.v1",
"occurred_at": "2025-10-31T12:34:56Z",
"partition_key": "psp_tx_987654",
"sequence": 418,
"data": {
"payment_id": "psp_tx_987654",
"amount": "10.00",
"currency": "EUR",
"status": "AUTHORIZED"
},
"schema_version": 1
}
Die Anforderung an den Empfänger: Nach dem Puffern und Validieren der Signatur schnell '2xx' zu antworten und die Geschäftsverarbeitung asynchron durchzuführen.
3) Ordnung und Kausalität
Schlüsselreihenfolge: Die Garantie „geht nicht weg“ nur innerhalb eines' partition _ key'(z.B. 'player _ id', 'wallet _ id', 'psp _ tx _ id').
Eine globale Ordnung ist nicht garantiert.
Auf der Senderseite befindet sich eine Warteschlange mit einer Serialisierung nach Schlüssel (ein Verbraucher/Sharding), auf der Empfängerseite eine Inbox mit'(source, event_id) 'und optional das Warten auf die fehlenden' seq'.
Wenn Lücken kritisch sind - pull-API 'GET/events bereitstellen? after = checkpoint 'für den Status „aufholen und überprüfen“.
4) Idempotenz und Deduplizierung
Jedes Webhook trägt eine stabile' X-Webhook-Id'.
Der Empfänger speichert "inbox (event_id)": PK - "source + event_id'; Wiederholung → No-Op.
Nebenwirkungen (Eintrag in DB/Wallet) werden nur einmal bei der ersten „Vision“ des Ereignisses durchgeführt.
Verwenden Sie bei Effektbefehlen den Idempotency-Key und den Ergebnis-Cache für die Dauer des Retrace-Fensters.
5) Retrays, Backoff und Fenster
Retraypolitik (Referenz):- Retrace auf '5xx/timeout/connection error/409-Conflict (retryable )/429'.
- Nicht retraint auf '4xx' außer '409/423/429' (und nur mit konsistenter Semantik).
- Exponentieller Backoff + voller Jitter: 0. 5s, 1s, 2s, 4s, 8s, … bis zu „max = 10-15 min“; TTL-Fenster-Retrays: z.B. 72 Stunden.
- Respektieren Sie' Retry-After 'beim Empfänger.
- Haben Sie eine gemeinsame Frist: „Erkennen Sie das Ereignis als nicht geliefert“ und übersetzen Sie es in DLQ.
yaml retry:
initial_ms: 500 multiplier: 2.0 jitter: full max_delay_ms: 900000 ttl: 72h retry_on: [TIMEOUT, 5xx, 429]
6) DLQ и redrive
DLQ ist ein „Friedhof“ von giftigen oder TTL-abgelaufenen Ereignissen mit vollständigen Metainformationen (Paiload, Schlagzeilen, Fehler, Versuche, Hashes).
Web-Konsole/API für Redrive (Point Re-Delivery) mit optionaler Endpoint/Secret-Bearbeitung.
Rate-limited redrive und batch-redrive mit Priorisierung.
7) Sicherheit
mTLS (wenn möglich) oder TLS 1. 2+.
Körpersignatur (HMAC mit Geheimnis per tenant/endpoint). Verifizierung:1. Extrahieren Sie't'(timestamp) aus dem Titel, überprüfen Sie das Schiebefenster (z.B. ± 5 min).
8) Quoten, Ratenlimits und Fairness
Fair-Queue per tenant/subscriber: Damit ein Abonnent/Tenant nicht den Gesamtpool erzielt.
Kontingente und Burst-Limits für ausgehenden Datenverkehr und Per-Endpoint.
Reaktion auf '429': Ehrung 'Retry-After', Tretstrom; bei längerer Begrenzung - degrade (Senden nur kritischer Ereignistypen).
9) Abonnementlebenszyklus
Register/Verify: POST Endpoint → Challenge/Response oder Out-of-Band Bestätigung.
Lease (optional): Signatur gültig bis' valid _ to'; Verlängerung ist offensichtlich.
Secret rotation: `current_secret`, `next_secret` с `switch_at`.
Testping: Ein künstliches Ereignis, um die Route zu überprüfen, bevor die Hauptpunkte aktiviert werden.
Health-Tests: periodische HEAD/GET mit Latenzprüfung und TLS-Profil.
10) Entwicklung von Schemata (Versionen von Ereignissen)
Versionierung des Ereignistyps: 'payment. authorized. v1` → `…v2`.
Evolution - additive (neue Felder → MINOR-Version der API), breaking → ein neuer Typ.
Schaltungsregister (JSON-Schema/Avro/Protobuf) + automatische Validierung vor dem Versand.
Der Header 'X-Event-Type' und das Feld 'schema _ version' im Body sind beide obligatorisch.
11) Beobachtbarkeit und SLO
Metriken (nach Typ/Tenant/Abonnent):- `deliveries_total`, `2xx/4xx/5xx_rate`, `timeout_rate`, `signature_fail_rate`.
- 'attempts _ avg', 'p50/p95/p99 _ delivery _ latency _ ms' (von der Veröffentlichung bis 2xx).
- `dedup_rate`, `out_of_order_rate`, `dlq_rate`, `redrive_success_rate`.
- `queue_depth`, `oldest_in_queue_ms`, `throttle_events`.
- Der Anteil der Lieferungen ≤ 60 c (p95) - 99. 5% für kritische Ereignisse.
- DLQ ≤ 0. 1% für 24 Stunden.
- Signature failures ≤ 0. 05%.
Логи/трейсинг: `event_id`, `partition_key`, `seq`, `attempt`, `endpoint`, `tenant_id`, `schema_version`, `trace_id`.
12) Senderreferenzalgorithmus
1. Schreiben Sie das Ereignis in die Transaktions-Outbox.
2. Definieren Sie partition_key und seq; in die Warteschlange stellen.
3. Worker nimmt einen Schlüssel, formt eine Anfrage, signiert, sendet mit Timeouts (connect/read).
4. Bei '2xx' - Lieferung erkennen, Latenz und seq-checkpoint fixieren.
5. Bei '429/5xx/timeout' ist ein Rückzug laut Richtlinie.
6. TTL → DLQ und Alert
13) Referenzverarbeiter (Empfänger)
1. Anfrage annehmen, TLS/proto prüfen.
2. Validierung der Signatur und des Zeitfensters.
3. Schnelles ACK 2xx (nach synchronem Schreiben in die lokale Inbox/Warteschlange).
4. Der asynchrone Worker liest 'inbox', prüft 'event _ id' (dedup), ordnet bei Bedarf 'seq' innerhalb von 'partition _ key' an.
5. Führt Effekte aus, schreibt „offset/seq checkpoint“ für reconcile.
6. Im Fehlerfall lokale Retrays; „giftige“ Aufgaben → lokale DLQ mit Alerts.
14) Reconcile (Pull-Loop)
Für „undurchdringliche“ Vorfälle:- `GET /events? partition_key=...&after_seq=...&limit=...' ist, alles zu geben, was man verpasst.
- Token-Checkpoint: 'after = opaque _ token' statt seq.
- Idempotente Umleitung: gleiche' event _ id', gleiche Signatur durch neues't'.
15) Nützliche Titel und Codes
2xx - akzeptiert (auch wenn die Geschäftsabwicklung später erfolgt).
410 Gone - endpoint ist geschlossen (der Absender stoppt die Lieferung und markiert das Abonnement als „archiviert“).
409/423 - vorübergehende Sperrung der Ressource → Retrace ist vernünftig.
429 - zu oft → Trottle und Backoff.
400/401/403/404 - Konfigurationsfehler; Retrai stoppen, Ticket öffnen.
16) Multi-Tenant und Regionen
Separate Warteschlangen und Limits per tenant/endpoint.
Datenresidenz: Senden von Daten aus der Region; durchgehende Rubriken „X-Tenant“, „X-Region“.
Absturzisolation: Der Fall eines Abonnenten hat keine Auswirkungen auf die anderen (getrennte Pools).
17) Testen
Vertragstests: feste Beispiele für Körper/Unterschriften, Validierungsprüfung.
Chaos: Drop/Duplikate, Shuffle der Ordnung, Netzwerkverzögerungen, 'RST', 'TLS' -Fehler.
Last: Sturmsturm, Messung p95/p99.
Sicherheit: Anti-Replikation, veraltete Zeitstempel, falsche Geheimnisse, Rotation.
DR/Replay: Massive Redrive von DLQ in einem isolierten Stand.
18) Playbooks (Runbooks)
1. Wachstum von 'signature _ fail _ rate'
Überprüfen Sie die Zeitdrift, die abgelaufene' Toleranz', die Rotation der Geheimnisse; vorübergehend „dual secret“ aktivieren.
2. Die Warteschlange altert ('oldest _ in _ queue _ ms' ↑)
Erhöhen Sie die Worker, aktivieren Sie die Priorisierung kritischer Tops, senken Sie vorübergehend die Häufigkeit von „lauten“ Typen.
3. Sturm '429' beim Abonnenten
Aktivieren Sie Trotting und Pausen zwischen Versuchen; weniger kritische Ereignistypen verschieben.
4. Masse' 5xx'
Öffnen Sie den Circuit-Breaker für einen bestimmten Endpunkt und wechseln Sie in den Defer & Batch-Modus; Signal an den Abonnenten.
5. DLQ-Füllung
Stoppen Sie nicht-kritische Veröffentlichung, aktivieren Sie Batch-Redrive mit niedrigem RPS, erhöhen Sie Alerts für Abonnementbesitzer.
19) Typische Fehler
Synchrone schwere Verarbeitung bis zur 2xx-Antwort → Retrays und Duplikate.
Keine Körper-/Zeitfenstersignatur → Anfälligkeit für Substitution/Replikation.
Das Fehlen von 'event _ id' und 'inbox' → unmöglich idempotency zu machen.
Der Versuch einer „globalen Ordnung“ → ewige Warteschlangensperren.
Retrays ohne Jitter/Limits → Incident Enhancement (Thundering-Herd).
Ein einziger gemeinsamer Pool für alle Abonnenten → „laut“ setzt alle.
20) Checkliste vor dem Verkauf
- Vertrag: 'event _ id', 'partition _ key', 'seq', 'event _ type. vN', HMAC-Signatur und Zeitstempel.
- Absender: Outbox, Serialisierung nach Schlüssel, Retrays mit Backoff + Jitter, TTL, DLQ und Redrive.
- Empfänger: schneller Eintrag in inbox + 2xx; idempotente Behandlung; lokales DLQ.
- Sicherheit: TLS, Signaturen, Anti-Replikation, Dual-Secret, Rotation.
- Quoten/Limits: fair-queue per tenant/endpoint, respect „Retry-After“.
- Reconcile API und Checkpoints; Dokumentation für Abonnenten.
- Beobachtbarkeit: p95/Threads/Bugs/DLQ, Trace durch 'event _ id'.
- Versionierung von Ereignissen und Politik der Entwicklung von Schemata.
- Incident Playbooks und „Button“ der globalen Pause/Auftauen.
Schluss
Zuverlässige Webhooks sind ein Protokoll über HTTP und nicht nur „POST mit JSON“. Ein klarer Vertrag (ID, Auftragsschlüssel, Signatur), Idempotenz, Retrays mit Jitter, eine faire Warteschlange und gut geölte Playbooks verwandeln den „Best-Case“ in einen vorhersehbaren und messbaren Liefermechanismus. Erstellen Sie at-least-once + order by key + reconcile und das System wird das Netzwerk, Lastspitzen und menschliche Fehler ruhig überstehen.