Leite einen Webhook an jeden Dienst weiter, der ihn braucht
- 01Wie Quellen, Ziele und Verbindungen zusammenpassen
- 02Filtern, welche Events welches Ziel erreichen
- 03Was Fan-out für Wiederholungen, Durchsatz und deine Rechnung bedeutet
- 04Ein Ziel wiederherstellen, ohne die anderen zu stören
- 05Die Warteschlange lesen, während sie abgearbeitet wird
Drei Objekte, und nur drei
Eine Quelle ist ein eingehender Endpunkt. Sie hat eine maskId und eine ingestUrl, und diese URL ist die einzige Zugangsberechtigung, die dein Anbieter braucht. Ein Ziel ist ein Ort, an den Events gesendet werden sollen, mit eigenen Headern und eigenem Ratenlimit. Eine Verbindung verknüpft eine Quelle mit einem Ziel und entscheidet, welche Events den Weg nehmen.
Fan-out ist das, was du bekommst, wenn eine Quelle mehr als eine Verbindung hat. An der Quelle ändert sich nichts, und der Anbieter erfährt nie davon.
Ein zweites Ziel anbinden
Vorausgesetzt, eine Quelle existiert schon, ist ein weiterer Consumer genau ein Ziel und eine Verbindung. Keins davon berührt den Anbieter.
- 1
Erstelle das Ziel
POST /v1/destinationsmit der URL, die aufgerufen werden soll. Eigene Header werden unterstützt, und die als sensibel markierten werden verschlüsselt gespeichert und nie von der API zurückgegeben. - 2
Verbinde es mit der Quelle
Eine Verbindung benennt das Paar und schränkt optional ein, was hindurchfließt. Lass
filterRules.eventTypesweg, um alles weiterzuleiten.curlcurl -s "$API/v1/connections" \ -H "authorization: Bearer $SDHK_KEY" \ -H 'content-type: application/json' \ -d '{ "name": "Stripe → billing service", "sourceId": "src_...", "destinationId": "dest_...", "filterRules": { "eventTypes": ["invoice.payment_succeeded"] } }'
- 3
Sende ein Test-Event
Sende an die Ingest-URL der Quelle. Eine Quelle mit konfigurierter Signaturprüfung braucht eine gültige Signatur. Ohne sie wird die Anfrage mit 401 beantwortet und mit dem Status
rejectedgespeichert. Sobald ein Event ankommt, solltest du zwei Zustellungen dazu sehen, eine pro Verbindung, jede mit eigenem Status.
Filtern ohne Regelsprache
Eine Verbindung gleicht auf den Event-Typ ab und auf nichts anderes. Der Typ kommt aus dem eventTypeField der Quelle, einem Punkt-Pfad in die Anfrage. Bekannte Anbieter setzen einen sinnvollen Standard, sodass eine GitHub-Quelle headers.x-github-event liest und eine Stripe-Quelle den Typ aus dem Body, ohne dass du etwas konfigurierst.
Diese eine Einstellung deckt mehr ab, als es scheint, weil du den Pfad wählst. Richte eine Quelle auf body.action, und ihre Verbindungen filtern nach der Aktion statt nach dem Header.
# A GitHub source filters on the event header by default.# Point eventTypeField at a body path to filter on something else:curl -s -X PATCH "$API/v1/sources/src_..." \ -H "authorization: Bearer $SDHK_KEY" \ -H 'content-type: application/json' \ -d '{ "data": { "eventTypeField": "body.action" } }' # Connections on that source now match on the action, not the header:# "filterRules": { "eventTypes": ["review_requested"] }
filtered. Du hast es nicht verloren. Füge die fehlende Verbindung hinzu und spiele es erneut ab.Was Fan-out kostet
Die abgerechnete Einheit ist die Zustellung, also ein Event, gesendet an ein verbundenes Ziel. Drei Verbindungen an einem Event sind drei Zustellungen. Der Preis folgt der Arbeit statt der Anzahl der Plätze. Ein vierter Consumer kostet also gleich viel, egal ob eine Person oder vierzig ins Dashboard schauen.
Wenn ein Ziel ausfällt
Zustellungen schlagen unabhängig voneinander fehl. Jede wird auf einer Backoff-Kurve wiederholt, die bei 5 Sekunden beginnt und sich bis auf 10 Stunden zwischen den Versuchen öffnet: drei Versuche innerhalb einer Obergrenze von einer Stunde bei Hobby, acht innerhalb einer Obergrenze von 36 Stunden bei bezahlten Plänen, die die Kurve nach etwa 28 Stunden erreicht. Danach wartet sie auf dich, statt zu verschwinden. Die anderen Verbindungen desselben Events sind nicht betroffen und waren schon erfolgreich.
Nicht jede Antwort außerhalb von 2xx zählt als Fehler, und das ist wichtig, wenn du um 3 Uhr nachts ein Log der Versuche liest.
Retry-After bis zu einer Stunde und kostet keinen Versuch. Höchstens fünfmal pro Zustellung, und schon früher abgelehnt, wenn die Wartezeit über die Wiederholungsobergrenze deines Plans hinausginge. In beiden Fällen geht es in den normalen Fehlerpfad über.Es gibt zwei Wege zur Wiederherstellung, und sie sind nicht austauschbar. POST /v1/deliveries/{id}/retry sendet eine Zustellung erneut an ein Ziel und erzeugt nichts Neues, ist also kostenlos. POST /v1/events/{id}/replay schickt das Event noch einmal durch seine Verbindungen, erzeugt neue Zustellungen und wird abgerechnet. Nimm die Wiederholung, wenn ein einzelner Consumer ausgefallen ist, und das erneute Abspielen, wenn du nachträglich eine Verbindung hinzugefügt hast.
rejected-Event hat die Signaturprüfung nicht bestanden. Es erneut abzuspielen würde also unverifizierte Inhalte unter dem Namen dieser Quelle an deine Ziele schicken. Es liefert 400. Eine als Duplikat verworfene Anfrage wird gar nicht gespeichert, auch da gibt es also nichts erneut abzuspielen.Taktung ist kein Fehler
Auf der ausgehenden Seite gibt es zwei Ratenlimits: die Zustellungen pro Sekunde deines Plans und das Limit, das du an einem einzelnen Ziel setzt. Wird eines davon überschritten, wird die Zustellung eingereiht, statt fehlzuschlagen. Ihr Status lautet throttled mit dem throttledReason rate_limit, es wird kein Wiederholungsversuch verbraucht, und sie wird gesendet, sobald Kapazität frei wird. Derselbe Status mit destination_busy ist die andere Art von Pause: Das Ziel hat sie mit 429, 502 oder 504 verlangt.
Das zählt vor allem, während sich ein Ziel erholt. Ein Dienst, der gerade wieder da ist, bekommt nicht den ganzen Rückstau auf einmal, und du wirst nicht für eine Warteschlange alarmiert, die korrekt abgearbeitet wird. Eine Zustellung, die je angehalten wurde, behält einen throttledAt-Zeitstempel, auch nachdem sie erfolgreich war, sodass die Pause hinterher sichtbar bleibt.
# Everything that was excluded by a filtercurl -s "$API/v1/events?status=filtered" -H "authorization: Bearer $SDHK_KEY" # What is waiting in the outbound queue right nowcurl -s "$API/v1/deliveries/stats" -H "authorization: Bearer $SDHK_KEY"→ { "pending": 4, "throttled": 412 }
Bewährte Praktiken
- Gib jedem Consumer seine eigene Verbindung, statt eine zu teilen und in deinem Handler zu verzweigen. Getrennte Verbindungen bringen dir getrennten Wiederholungsstatus und getrennte Belege.
- Setze ein Ratenlimit an jedem Ziel, von dem du weißt, dass es empfindlich ist. Einreihen ist für beide Seiten günstiger als eine Welle von Fehlern mit anschließendem Backoff.
- Filtere an der Verbindung, nicht in deinem Handler. Ein Event, das dein Dienst ohnehin verwerfen würde, kostet trotzdem eine Zustellung, wenn du es durchlässt.
- Prüfe
?status=filtered, wenn ein Consumer fehlende Events meldet. Die übliche Ursache ist eineventTypeField, das woandershin zeigt, als du dachtest. - Mach Handler idempotent. Erneutes Abspielen gibt es, Leute nutzen es während Incidents, und dein Endpunkt sollte eine zweite Kopie als No-op behandeln.