Zum Inhalt springen
use-cases / the-webhook-fan-out-you-didnt-have-to-build / hero
PIPE · HTTP FAN-OUT

Das Webhook-Fan-out, das du nicht selbst bauen musstest

Stripe POSTet den Event-Body an einen Pipe-Pfad mit ?n=12. Zwölf Subscriber GETten denselben Pfad mit ?n=12. Die Pipe hält die Nachricht, bis alle verbunden sind, und streamt sie dann gleichzeitig an alle zwölf. Kein Broker, keine Consumer Group, keine DLQ.

Pipe-Docs lesen
use-cases / the-webhook-fan-out-you-didnt-have-to-build / mechanism

Eine URL. Zwölf Reader. Backpressure pro Verbindung.

Hoody Pipe ist HTTP-Streaming mit einem Multi-Receiver-Modus. Hänge ?n=N an Sender- und Receiver-URLs und die Pipe wartet, bis sich N Reader verbinden — dann spiegelt sie den Body gleichzeitig an alle. Die langsamen drosseln ihre eigene Verbindung — die schnellen fließen weiter.

01STEP 01 · INTAKE

Stripe POSTet in die Pipe

Dein Stripe-Endpoint leitet den Event-Body direkt an /api/v1/pipe/billing?n=12 weiter. Die Verbindung blockiert, während die Pipe darauf wartet, dass sich die zwölf Receiver versammeln.

02STEP 02 · ASSEMBLE

Zwölf Subscriber verbinden sich

Jeder Subscriber ist eine curl-Schleife in einem Container, die denselben Pfad mit ?n=12 GETtet. Die Pipe hält den Body im Speicher, bis sich der zwölfte Reader verbindet — keine On-Disk-Queue, nichts zu flushen.

03STEP 03 · STREAM

Fan-out, dann vergessen

Sobald alle verbunden sind, streamt der Body gleichzeitig an alle zwölf. Ein langsamer Reader übt Backpressure auf seinen eigenen Socket aus; die anderen ziehen weiter. Wenn der letzte Reader trennt, vergisst die Pipe die Nachricht.

stripe-webhook.sh
# Sender side — your Stripe webhook handler.
# Pipe holds the body until 12 readers are attached.
curl -X POST "https://api.hoody.com/api/v1/pipe/billing?n=12" \
  -H "Authorization: Bearer $HOODY_TOKEN" \
  -H "Content-Type: application/json" \
  --data-binary "@stripe-event.json"

# Receiver side — one of twelve subscriber containers.
# Same path, same n. Streams the event body when fan-out is ready.
curl -N "https://api.hoody.com/api/v1/pipe/billing?n=12" \
  -H "Authorization: Bearer $HOODY_TOKEN" | ./billing-handler.sh

# n must match on both ends — mismatch returns 400.
# Default n=1 is point-to-point. n=12 is fan-out for twelve.

Dieselbe URL, derselbe Query-String, beide Enden. Die Verbindung des Senders ist der Broker; die Verbindungen der Reader sind die Consumer Group. Backpressure ist pro Socket, nicht pro Topic, weil es kein Topic gibt — es gibt einen In-Flight-Body und zwölf Sockets, die daran ziehen.

use-cases / the-webhook-fan-out-you-didnt-have-to-build / fleet

Subscriber kommen und gehen wie Prozesse

Einen Subscriber hinzuzufügen ist ein curl mehr in einem Container mehr. Einen zu entfernen heißt, das curl zu killen. Es gibt keine Broker-Config, kein Consumer-Group-Rebalancing, keine DLQ zu drainen — die Cluster-Topologie ist einfach das, was gerade an Prozessen läuft.

Subscriber-Diff · letzte 5 Minutenkein Rebalancing nötig
  • + HINZUGEFÜGT
    WHKAUD
    zwei neue Container hochgefahren
  • − ENTFERNT
    MIX
    Container gekillt, curl beendet
  • = STABIL
    BILANLLOGFRDSUCSLACRMCDNNOT
    weiterhin auf demselben n=12-Pfad

Die Pipe trackt nicht, wer subscribed ist. Sie wartet einfach pro Event auf n Verbindungen. Setz n auf beiden Seiten hoch und das nächste Event wartet auf die neue Anzahl. Es gibt keinen Membership-State, der korrumpieren könnte, weil es keine Membership gibt.

use-cases / the-webhook-fan-out-you-didnt-have-to-build / advantages

Was du nicht mehr betreiben musst

Jede Zeile unten ist eine Kategorie von Arbeit, die verschwindet, wenn das Protokoll der Broker ist. Keine Infrastruktur zu provisionieren, keine Abstraktionen zu lernen — nur curl und ?n.

  • Keine Queue zu provisionieren

    Kein SQS, kein Kafka-Cluster, keine RabbitMQ-Exchange. Die Pipe ist die Queue und sie lebt nur für eine Nachricht.

  • Keine Consumer-Group-Buchhaltung

    Keine Offsets, keine Commits, keine Rebalances. Die Pipe hält den Body, bis n Sockets sich verbinden — das ist das gesamte Coordination-Modell.

  • Keine DLQ zu drainen

    Wenn der Sender trennt, bevor n Reader bereit sind, läuft die Pipe in einen Timeout (5-min TTL) und Stripe versucht es erneut. Kein Poison-Message-Bucket zum Babysitten.

  • Kein SDK pro Sprache

    Subscriber sind curl-Schleifen. Sender sind curl. Das Protokoll ist HTTP. Alles, was eine URL aufrufen kann, kann dem Cluster beitreten.

  • Backpressure pro Socket

    Ein langsamer Reader drosselt seine eigene Verbindung. Die anderen elf streamen weiter mit voller Geschwindigkeit. Es gibt kein Head-of-Line-Blocking über die Flotte hinweg.

  • Vergesslich by default

    Der Body ist weg in dem Moment, in dem der letzte Reader trennt. Keine Retention-Policy zu setzen, kein Log-Compaction zu schedulen, kein GDPR-Delete-on-Request-Job zu schreiben.

use-cases / the-webhook-fan-out-you-didnt-have-to-build / punchline

Zwölf Subscriber, eine URL, kein Broker.

Die Broker unten sind die Dinge, die du früher installiert hast, bevor HTTP-Fan-out ein Query-Parameter war. Rechts steht, was sie ersetzt — ein Pfad, ein n und zwölf curl-Prozesse, die nicht wissen, dass sie ein Cluster sind.

WAS DU NICHT BETREIBST
  • AWS Lambda + SQS/SNS
  • Apache Kafka
  • RabbitMQ-Exchanges
  • Eigene Webhook-Router
WAS DU STATTDESSEN AUFRUFST
POST /api/v1/pipe/billing?n=12

Zwölf Receiver GETten denselben Pfad mit demselben n. Die Pipe ist der Broker — und vergisst die Nachricht in der Sekunde, in der der letzte Reader auflegt.

use-cases / the-webhook-fan-out-you-didnt-have-to-build / replaces

Was das ersetzt

Wenn du zu einem dieser Dinge greifst, um einen Webhook an N Consumer zu broadcasten, macht das Pipe-Modell denselben Job in zwei curl-Aufrufen — Sender auf der einen Seite, Receiver auf der anderen.

  • AWS Lambda + SQS/SNSBroker pro Topic, IAM pro Subscriber
  • Apache KafkaCluster, ZooKeeper/KRaft, Consumer Groups
  • RabbitMQ-ExchangesBindings, Vhosts, Mgmt-UI pro Env
  • Eigene Webhook-RouterService zu pflegen, Retries zu erfinden
  • Pub/Sub-ServicesPer-Message-Billing, GCP/AWS-Lock-in
  • HTTP-Fan-out-PluginsGateway-Plugin pro Stack, keine Backpressure
use-cases / the-webhook-fan-out-you-didnt-have-to-build / cta

Das Fan-out, für das du früher einen Sprint geschrieben hast, ist jetzt ein Query-Parameter. Häng ?n=12 dran und ship.

Pipe-Docs lesen
use-cases / the-webhook-fan-out-you-didnt-have-to-build / related

Lies die anderen