Ein typisches Störungsbild im Backend: Ein externer Service (Zahlung, Suche, CRM) wird langsam. Threads stauen sich, Datenbankverbindungen laufen voll, und plötzlich wirken auch eigentlich gesunde Teile der Anwendung „kaputt“. Ursache ist selten ein einzelner Bug, sondern eine Kette aus Timeouts, Retries und Ressourcenbindung. Genau hier setzt das Muster Circuit Breaker an: Es stoppt Aufrufe, die sehr wahrscheinlich scheitern, bevor sie noch mehr Schaden anrichten.
In produktiven Systemen ist das weniger ein Architektur-„Nice-to-have“ als ein Baustein für Stabilität. Entscheidend ist, das Muster sauber mit Timeouts, begrenzten Wiederholungen, sinnvollen Fallbacks und Telemetrie zu kombinieren. Dann entsteht ein Schutzmechanismus, der Fehler isoliert statt sie zu vervielfachen.
Warum sich Fehler in Service-zu-Service-Calls verstärken
Ressourcenbindung ist der eigentliche Ausfall
Viele Systeme fallen nicht aus, weil „nichts mehr geht“, sondern weil wichtige Ressourcen blockieren: Worker-Threads, CPU, Connection-Pools, Queue-Kapazitäten. Ein langsamer HTTP-Call kann pro Request mehrere Sekunden binden. Wenn das pro Sekunde hunderte Requests trifft, entsteht eine Warteschlange, die auch andere Funktionen mitzieht. Das ist besonders kritisch, wenn synchron in Ketten gerufen wird (API → Service A → Service B → Service C).
Ein Circuit Breaker schneidet diese Kette an einer Stelle ab, bevor das System vollständig gesättigt ist. Statt unendlich viele langsame Calls zu erzeugen, schlägt der Call kontrolliert und schnell fehl.
Retries ohne Grenzen verschlimmern die Lage
Retries wirken auf dem Papier sinnvoll: „Beim nächsten Versuch klappt es.“ In der Praxis erhöhen sie aber die Last genau dann, wenn ein System ohnehin überfordert ist. Ohne harte Grenzen (Anzahl, Backoff, Jitter) machen Retries aus einem partiellen Fehler einen Flächenbrand. In Kombination mit einem Circuit Breaker werden Wiederholungen so platziert, dass sie nur im „gesunden“ Zustand stattfinden und im „offenen“ Zustand sofort beendet werden.
Zustände und Schwellen: Wie ein Circuit Breaker korrekt arbeitet
Closed, Open, Half-Open verständlich erklärt
Ein Circuit Breaker hat drei Kernzustände:
- Closed: Requests laufen normal. Fehler werden gezählt und bewertet.
- Open: Requests werden ohne Call sofort abgewiesen (Fail fast). Das schützt Ressourcen.
- Half-Open: Nach einer Wartezeit werden wenige Probe-Requests zugelassen. Wenn sie stabil sind, schließt der Breaker wieder, sonst bleibt er offen.
Wichtig ist: „Open“ bedeutet nicht „das System ist kaputt“, sondern „die Wahrscheinlichkeit für Fehler ist hoch, daher wird aktiv entlastet“.
Fehlerraten, Sliding Windows und minimale Stichprobe
In der Praxis wird nicht „ein Fehler“ als Trigger verwendet, sondern eine Fehlerrate über ein Fenster: etwa 50 Requests im Sliding Window, ab 40–60% Fehlern wird geöffnet. Eine Mindestanzahl an Calls verhindert, dass bei niedriger Last ein einzelner Ausreißer den Breaker aufreißt. Welche Werte passen, hängt von Traffic, SLOs (Service Level Objectives) und Kosten eines Fehlschlags ab.
Als robuste Daumenregel für den Start:
- Fenstergröße: 50–200 Calls (je nach Traffic)
- Fehlerschwelle: 50% (später datenbasiert anpassen)
- Open-Dauer: 5–30 Sekunden (abhängig von Recovery-Zeit des Downstreams)
- Half-Open-Proben: 5–20 Calls
Diese Werte sind kein Standard, sondern Startpunkte, die über Metriken validiert werden müssen.
Timeouts, Retries und Fallbacks richtig kombinieren
Timeout zuerst: schneller scheitern als warten
Ein Circuit Breaker ersetzt kein Timeout. Ohne Timeout hängt ein Call zu lange und blockiert Ressourcen, bevor der Breaker überhaupt reagieren kann. Sinnvoll ist ein gestaffelter Ansatz: Client-Timeout kleiner als das eigene Request-Timeout, und deutlich kleiner als der Timeout auf dem Load Balancer. So entsteht eine klare Priorität, wo abgebrochen wird.
In service-internen Calls sollte der Timeout häufig aggressiver sein als bei user-facing Requests, weil die Kaskade sonst größer wird. Gleichzeitig muss genug Zeit bleiben, damit der Downstream unter Normalbedingungen zuverlässig antworten kann.
Retries nur bei sicheren Fehlern und mit Backoff
Nicht jeder Fehler ist retrybar. Typische Kandidaten sind kurzzeitige Netzprobleme oder 503/429, wenn der Downstream explizit signalisiert, dass später wieder Kapazität da ist. Bei 4xx (außer 408) oder Validierungsfehlern sind Retries meistens reine Lastverschwendung.
Eine praxistaugliche Kombination:
- Maximal 1–2 Retries
- Exponential Backoff plus Jitter
- Retries nur, wenn der Circuit Breaker nicht offen ist
Wenn APIs so gebaut sind, dass doppelte Requests keinen Schaden verursachen, erleichtert das Retry-Handling erheblich. Dazu passt das Prinzip aus idempotenten APIs, das Nebenwirkungen trotz Wiederholungen begrenzt.
Fallbacks: degradieren statt komplett ausfallen
Fallbacks sind nur dann hilfreich, wenn sie fachlich korrekt sind. Ein Fallback „irgendwas aus dem Cache“ kann für Produktlisten sinnvoll sein, für Kontostände aber gefährlich. Beispiele für sinnvolle Degradierungen:
- Read-only Ansicht statt vollständiger Bearbeitung
- „Später erneut versuchen“ mit sauberer Fehlerkennung statt generischem 500
- Vereinfachte Berechnung ohne optionalen Fremdservice
- Asynchronisierung: Request annehmen, im Hintergrund verarbeiten, Status später liefern
Wichtig: Fallbacks müssen selbst schnell und stabil sein. Ein Fallback, der wiederum einen langsamen Service aufruft, ist kein Fallback.
Implementierung: Wo der Breaker sitzen sollte
Client-seitig im Aufrufpfad, nicht „irgendwo im Gateway“
Der Circuit Breaker gehört dorthin, wo die teuren Abhängigkeiten genutzt werden: in den Client-Code, der den Downstream aufruft (HTTP-Client, gRPC-Stub, SDK). Ein zentraler Breaker im API-Gateway kann ergänzen, löst aber nicht das Problem interner Ketten (Service A ruft Service B, unabhängig vom Gateway).
In modularen Backends ist das oft ein dedizierter Adapter: Domain-Code kennt nur ein Interface, der Adapter kapselt Netzwerk, Breaker, Timeouts und Mapping. Das hält die Fachlogik sauber und testbar, ähnlich wie ein sauberer Service-Layer Verantwortlichkeiten trennt.
Granularität: pro Endpunkt statt pro Host
Ein häufiger Fehler ist ein Breaker „pro Host“. Dann reißt ein instabiler Endpunkt den ganzen Service ab, obwohl andere Endpunkte gesund wären. Besser ist Granularität nach Operation: z.B. „/payments/authorize“ getrennt von „/payments/status“. Ebenso kann eine Trennung nach Tenant oder Region sinnvoll sein, wenn Ausfälle lokal begrenzt sind.
Was als Fehler zählen sollte (und was nicht)
Für die Fehlerrate zählen typischerweise:
- Timeouts
- Verbindungsfehler (DNS, TCP Reset)
- 5xx-Antworten
Nicht automatisch als „Breaker-Fehler“ zählen sollten fachliche Ablehnungen (z.B. 409 Konflikt, 422 Validierung). Sonst wird ein Breaker bei korrekten Business-Regeln geöffnet und blockiert legitime Requests.
Messbarkeit und Betrieb: Ohne Signale kein Vertrauen
Welche Metriken im Alltag wirklich helfen
Ein Circuit Breaker ist nur dann ein Stabilitätsgewinn, wenn sein Verhalten beobachtbar ist. Sinnvolle Kennzahlen sind:
- Anzahl der State-Transitions (Closed→Open, Open→Half-Open, …)
- Rate der kurzgeschlossenen Requests (Fail-fast)
- Latenzverteilung der Downstream-Calls (p50/p95/p99)
- Fehlerklassen getrennt (Timeout vs. 5xx vs. Connect)
Für die Ursachenanalyse ist es zudem hilfreich, Requests durch mehrere Services verfolgen zu können. Das lässt sich gut mit Distributed Tracing kombinieren, um zu sehen, wo Zeit verbrannt wird und welche Abhängigkeit den Breaker triggert.
Alarmierung: auf Zustandswechsel, nicht auf einzelne Fehler
Einzelne Timeouts sind oft Rauschen. Ein Breaker, der öffnet, ist dagegen ein klarer Indikator für ein systemisches Problem oder eine falsche Konfiguration. Alarme sollten daher eher auf „Open länger als X Sekunden“ oder „State-Transitions pro Minute über Schwelle“ gehen. Ergänzend kann eine hohe Rate an kurzgeschlossenen Requests ein Signal sein, dass Nutzer:innen gerade stark degradiert werden.
Konkrete Schritte für ein sauberes Setup in einem Service
- Downstream-Calls inventarisieren: pro Call definieren, ob er kritisch ist und welches Verhalten bei Ausfall fachlich akzeptabel ist.
- Timeouts setzen: verbindliche Client-Timeouts pro Operation definieren und im Code zentral konfigurieren.
- Breaker pro Operation konfigurieren: Fenstergröße, Fehlerschwelle, Open-Dauer und Half-Open-Proben festlegen.
- Retries begrenzen: nur bei retrybaren Fehlern, mit Backoff und maximal 1–2 Wiederholungen.
- Fallbacks implementieren und testen: klare Degradierungslogik, die schnell ist und keine neuen Abhängigkeiten einführt.
- Telemetrie integrieren: Zustände, Fehlerraten und Latenzen messen; Dashboards und Alarme auf Zustandswechsel aufbauen.
Typische Stolperfallen aus realen Systemen
„Fail fast“ ohne gute Fehlermeldung
Wenn der Breaker öffnet, darf die Anwendung nicht mit generischen 500 antworten, wenn fachlich ein „temporär nicht verfügbar“ passender ist. Ein sauberes Fehler-Mapping (z.B. 503 mit klarer Message) hilft Clients, korrekt zu reagieren. Für interne Systeme kann zudem ein Retry-After Header sinnvoll sein, wenn er konsistent gepflegt wird.
Ein gemeinsamer Breaker für viele Threads ohne Isolation
Wenn ein einzelner Breaker in einer shared Library übergreifend für viele Aufrufer genutzt wird, kann das Verhalten überraschend werden: Ein hoher Traffic-Bereich beeinflusst einen niedrigen Traffic-Bereich. Besser sind getrennte Instanzen pro Service und Operation, mit klarer Ownership im Code.
Half-Open ohne Schutz vor Lastspitzen
Half-Open ist eine sensible Phase: Zu viele Probe-Requests können den Downstream erneut überfahren. Daher sollten Probe-Requests begrenzt und idealerweise mit Rate-Limits kombiniert werden. Außerdem muss klar sein, welche Fehler im Half-Open sofort wieder öffnen (z.B. Timeout) und welche toleriert werden (z.B. einzelne 5xx bei ansonsten guter Antwortzeit).
Wann andere Patterns besser passen
Asynchron statt synchron, wenn das Geschäft es erlaubt
Wenn eine Operation nicht sofort ein Ergebnis liefern muss, ist Asynchronisierung häufig stabiler: Der Request wird angenommen, in eine Queue geschrieben und später verarbeitet. Das reduziert Kopplung und macht Störungen weniger direkt sichtbar. In Event-getriebenen Flows kann auch ein persistenter Versand helfen, wenn Events zuverlässig raus müssen; dafür passt das Muster aus Outbox Pattern.
Circuit Breaker ist kein Ersatz für Kapazitätsplanung
Der Breaker schützt das eigene System, aber er macht einen Downstream nicht schneller. Wenn ein Service dauerhaft überlastet ist, braucht es Kapazität, bessere Queries, Caching, oder eine Änderung im Produktfluss. Ein offener Breaker ist ein Symptom, kein Heilmittel.
Fehlerbudget im Blick behalten
Technisch ist es leicht, aggressiv zu öffnen und viele Requests zu blockieren. Fachlich kann das falsch sein, wenn kritische Prozesse dadurch zu oft scheitern. Deshalb sollten Schwellenwerte und Fallbacks an Business-Prioritäten gekoppelt sein: Was darf degradiert werden, was muss im Zweifel warten, was muss hart fehlschlagen?
Richtig eingesetzt sorgt Timeouts plus Retry-Strategie plus Circuit Breaker dafür, dass Abhängigkeiten kontrollierbar bleiben. Das Ziel ist nicht „nie Fehler“, sondern ein System, das Fehler lokal hält, transparent macht und unter Störung weiter nutzbar bleibt.
