Trennschalter-MusterCircuit Breaker pattern

Behandeln Sie Fehler, deren Behebung beim Herstellen einer Verbindung mit einem Remotedienst oder einer Remoteressource unterschiedlich lange dauern kann.Handle faults that might take a variable amount of time to recover from, when connecting to a remote service or resource. Dies kann die Stabilität und Resilienz einer Anwendung verbessern.This can improve the stability and resiliency of an application.

Kontext und ProblemContext and problem

In einer verteilten Umgebung können bei Aufrufen von Remoteressourcen und -diensten vorübergehende Störungen auftreten, z. B. langsame Netzwerkverbindungen, Timeouts oder überlastete bzw. vorübergehend nicht verfügbare Ressourcen, die zu Fehlern führen können.In a distributed environment, calls to remote resources and services can fail due to transient faults, such as slow network connections, timeouts, or the resources being overcommitted or temporarily unavailable. Diese Störungen korrigieren sich typischerweise nach kurzer Zeit selbst, und eine robuste Cloudanwendung sollte bereit sein, sie mit einer Strategie wie dem Wiederholungsmuster zu behandeln.These faults typically correct themselves after a short period of time, and a robust cloud application should be prepared to handle them by using a strategy such as the Retry pattern.

Es kann aber auch Situationen geben, in denen Störungen bzw. Fehler auf unvorhergesehene Ereignisse zurückzuführen sind, deren Behebung viel länger dauern kann.However, there can also be situations where faults are due to unanticipated events, and that might take much longer to fix. Der Schweregrad dieser Störungen kann von einem teilweisen Verlust der Konnektivität bis hin zum vollständigen Ausfall eines Diensts reichen.These faults can range in severity from a partial loss of connectivity to the complete failure of a service. In solchen Situationen kann es für eine Anwendung zwecklos sein, einen Vorgang, der unwahrscheinlich ist, ständig zu wiederholen. Stattdessen sollte die Anwendung schnell akzeptieren, dass der Vorgang fehlerhaft ist, und diesen Fehler entsprechend behandeln.In these situations it might be pointless for an application to continually retry an operation that is unlikely to succeed, and instead the application should quickly accept that the operation has failed and handle this failure accordingly.

Wenn ein Dienst zudem sehr ausgelastet ist, kann ein Ausfall in einem Teil des Systems zu überlappenden Fehlern führen.Additionally, if a service is very busy, failure in one part of the system might lead to cascading failures. Ein Vorgang, der einen Dienst aufruft, könnte z. B. so konfiguriert werden, dass er ein Timeout implementiert und mit einer Fehlermeldung antwortet, wenn der Dienst innerhalb dieses Zeitraums nicht antwortet.For example, an operation that invokes a service could be configured to implement a timeout, and reply with a failure message if the service fails to respond within this period. Diese Strategie könnte jedoch dazu führen, dass viele gleichzeitige Anforderungen an denselben Vorgang bis zum Ablauf der Timeoutperiode blockiert werden.However, this strategy could cause many concurrent requests to the same operation to be blocked until the timeout period expires. Diese blockierten Anforderungen können kritische Systemressourcen belegen, z. B. Arbeitsspeicher, Threads, Datenbankverbindungen usw.These blocked requests might hold critical system resources such as memory, threads, database connections, and so on. Infolgedessen könnten diese Ressourcen ausgelastet werden. Dies könnte zum Ausfall anderer, möglicherweise nicht zusammengehöriger Teile des Systems führen, die dieselben Ressourcen benötigen.Consequently, these resources could become exhausted, causing failure of other possibly unrelated parts of the system that need to use the same resources. In solchen Situationen wäre es vorzuziehen, wenn der Vorgang sofort einen Fehler aufweist und nur dann versucht wird, den Dienst aufzurufen, wenn er voraussichtlich erfolgreich sein wird.In these situations, it would be preferable for the operation to fail immediately, and only attempt to invoke the service if it's likely to succeed. Beachten Sie, dass das Festlegen eines kürzeren Timeouts zur Lösung dieses Problems beitragen kann, aber der Timeout sollte nicht so kurz sein, dass beim Vorgang die meiste Zeit Fehler auftreten, selbst wenn die Anforderung an den Dienst letztendlich erfolgreich sein sollte.Note that setting a shorter timeout might help to resolve this problem, but the timeout shouldn't be so short that the operation fails most of the time, even if the request to the service would eventually succeed.

LösungSolution

Das Trennschalter-Muster, das von Michael Nygard in seinem Buch Release It! gemeinverständlich dargestellt wurde, kann verhindern, dass eine Anwendung wiederholt versucht einen Vorgang auszuführen, der voraussichtlich nicht erfolgreich sein wird.The Circuit Breaker pattern, popularized by Michael Nygard in his book, Release It!, can prevent an application from repeatedly trying to execute an operation that's likely to fail. Dabei wird die Fortsetzung des Vorgangs gestattet, ohne auf eine Behebung der Störung zu warten oder CPU-Zyklen zu verschwenden, während ermittelt wird, dass es sich um einen länger anhaltenden Fehler handelt.Allowing it to continue without waiting for the fault to be fixed or wasting CPU cycles while it determines that the fault is long lasting. Mithilfe des Trennschalter-Musters kann eine Anwendung auch ermitteln, ob der Fehler behoben wurde.The Circuit Breaker pattern also enables an application to detect whether the fault has been resolved. Wenn das Problem scheinbar behoben ist, kann die Anwendung versuchen, den Vorgang aufzurufen.If the problem appears to have been fixed, the application can try to invoke the operation.

Der Zweck des Trennschalter-Musters unterscheidet sich von dem des Wiederholungsmusters.The purpose of the Circuit Breaker pattern is different than the Retry pattern. Mithilfe des Wiederholungsmusters kann eine Anwendung einen Vorgang in der Annahme wiederholen, dass er erfolgreich ausgeführt wird.The Retry pattern enables an application to retry an operation in the expectation that it'll succeed. Das Trennschalter-Muster verhindert, dass von einer Anwendung ein Vorgang durchgeführt wird, der voraussichtlich nicht erfolgreich sein wird.The Circuit Breaker pattern prevents an application from performing an operation that is likely to fail. Eine Anwendung kann diese beiden Muster kombinieren, indem sie das Wiederholungsmuster verwendet, um einen Vorgang über einen Trennschalter aufzurufen.An application can combine these two patterns by using the Retry pattern to invoke an operation through a circuit breaker. Die Wiederholungslogik sollte jedoch auf Ausnahmen reagieren, die vom Trennschalter zurückgegeben werden, und Wiederholungsversuche abbrechen, wenn der Trennschalter anzeigt, dass ein Fehler nicht vorübergehend ist.However, the retry logic should be sensitive to any exceptions returned by the circuit breaker and abandon retry attempts if the circuit breaker indicates that a fault is not transient.

Ein Trennschalter fungiert als Proxy für Vorgänge, bei denen möglicherweise Fehler auftreten.A circuit breaker acts as a proxy for operations that might fail. Der Proxy sollte die Anzahl der kürzlich aufgetretenen Fehler überwachen und anhand dieser Informationen entscheiden, ob der Vorgang fortgesetzt oder sofort eine Ausnahme zurückgegeben werden soll.The proxy should monitor the number of recent failures that have occurred, and use this information to decide whether to allow the operation to proceed, or simply return an exception immediately.

Der Proxy kann als Zustandsautomat mit den folgenden Zuständen implementiert werden, die die Funktionalität eines elektrischen Trennschalters simulieren:The proxy can be implemented as a state machine with the following states that mimic the functionality of an electrical circuit breaker:

  • Geschlossen: Die Anforderung der Anwendung wird an den Vorgang weitergeleitet.Closed: The request from the application is routed to the operation. Der Proxy führt eine Zählung der Anzahl der letzten Fehler durch, und wenn der Aufruf des Vorgangs erfolglos ist, inkrementiert der Proxy diesen Zähler.The proxy maintains a count of the number of recent failures, and if the call to the operation is unsuccessful the proxy increments this count. Überschreitet die Anzahl der letzten Fehler innerhalb eines bestimmten Zeitraums einen bestimmten Schwellenwert, wird der Proxy in den Zustand Geöffnet versetzt.If the number of recent failures exceeds a specified threshold within a given time period, the proxy is placed into the Open state. An diesem Punkt startet der Proxy einen Timeout-Timer, und wenn dieser Timer abläuft, wird der Proxy in den Zustand Halb geöffnet versetzt.At this point the proxy starts a timeout timer, and when this timer expires the proxy is placed into the Half-Open state.

    Der Zweck des Timeout-Timers ist es, dem System Zeit zur Behebung des Problems zu geben, das die Störung verursacht hat, bevor es der Anwendung erlaubt wird, den Vorgang erneut auszuführen.The purpose of the timeout timer is to give the system time to fix the problem that caused the failure before allowing the application to try to perform the operation again.

  • Offen: Bei der Anforderung der Anwendung tritt sofort ein Fehler auf, und an die Anwendung wird eine Ausnahme zurückgegeben.Open: The request from the application fails immediately and an exception is returned to the application.

  • Halb geöffnet: Eine begrenzte Anzahl von Anforderungen aus der Anwendung wird zugelassen und darf den Vorgang aufrufen.Half-Open: A limited number of requests from the application are allowed to pass through and invoke the operation. Wenn diese Anforderungen erfolgreich sind, wird davon ausgegangen, dass die Störung, die zuvor den Fehler verursacht hat, behoben wurde und der Trennschalter in den Zustand Geschlossen wechselt (der Fehlerzähler wird zurückgesetzt).If these requests are successful, it's assumed that the fault that was previously causing the failure has been fixed and the circuit breaker switches to the Closed state (the failure counter is reset). Wenn bei einer Anforderung ein Fehler auftritt, geht der Trennschalter davon aus, dass die Störung noch vorhanden ist, sodass er in den Zustand Geöffnet zurückkehrt und den Timeout-Timer neu startet, um dem System eine weitere Frist für die Wiederherstellung nach dem Fehler zu geben.If any request fails, the circuit breaker assumes that the fault is still present so it reverts back to the Open state and restarts the timeout timer to give the system a further period of time to recover from the failure.

    Mit dem Zustand Halb geöffnet kann verhindert werden, dass ein in der Wiederherstellung befindlicher Dienst plötzlich mit Anforderungen überschwemmt wird.The Half-Open state is useful to prevent a recovering service from suddenly being flooded with requests. Wenn ein Dienst wiederhergestellt wird, kann er möglicherweise eine begrenzte Anzahl von Anforderungen unterstützen, bis die Wiederherstellung abgeschlossen ist. Aber während der Wiederherstellung kann eine Vielzahl an Aufgaben dazu führen, dass der Dienst erneut den Timeout überschreitet oder ausfällt.As a service recovers, it might be able to support a limited volume of requests until the recovery is complete, but while recovery is in progress a flood of work can cause the service to time out or fail again.

Trennschalterzustände

In der Abbildung ist der vom Zustand Geschlossen verwendete Fehlerzähler zeitbasiert.In the figure, the failure counter used by the Closed state is time based. Er wird in regelmäßigen Abständen automatisch zurückgesetzt.It's automatically reset at periodic intervals. Dadurch wird verhindert, dass der Trennschalter bei gelegentlichen Ausfällen in den Zustand Geöffnet wechselt.This helps to prevent the circuit breaker from entering the Open state if it experiences occasional failures. Der Fehlerschwellenwert, der den Trennschalter in den Zustand Geöffnet versetzt, wird nur erreicht, wenn innerhalb eines bestimmten Intervalls eine bestimmte Anzahl von Fehlern aufgetreten ist.The failure threshold that trips the circuit breaker into the Open state is only reached when a specified number of failures have occurred during a specified interval. Der Zähler, der vom Zustand Halb geöffnet verwendet wird, zeichnet die Anzahl der erfolgreichen Versuche beim Aufrufen des Vorgangs auf.The counter used by the Half-Open state records the number of successful attempts to invoke the operation. Der Trennschalter kehrt in den Zustand Geschlossen zurück, nachdem eine bestimmte Anzahl aufeinanderfolgender Vorgangsaufrufe erfolgreich durchgeführt wurde.The circuit breaker reverts to the Closed state after a specified number of consecutive operation invocations have been successful. Wenn bei einem Aufruf ein Fehler auftritt, wechselt der Trennschalter sofort in den Zustand Geöffnet und der Erfolgszähler wird zurückgesetzt, wenn er das nächste Mal in den Zustand Halb geöffnet wechselt.If any invocation fails, the circuit breaker enters the Open state immediately and the success counter will be reset the next time it enters the Half-Open state.

Die Wiederherstellung des Systems erfolgt extern, möglicherweise durch Wiederherstellen oder Neustarten einer fehlerhaften Komponente oder durch Reparieren einer Netzwerkverbindung.How the system recovers is handled externally, possibly by restoring or restarting a failed component or repairing a network connection.

Das Trennschalter-Muster sorgt für Stabilität, während sich das System nach einem Fehler erholt und die Auswirkungen auf die Leistung minimiert.The Circuit Breaker pattern provides stability while the system recovers from a failure and minimizes the impact on performance. Es kann dabei helfen, die Antwortzeit des Systems zu wahren, indem es eine Anforderung für einen Vorgang, der voraussichtlich nicht erfolgreich sein wird, schnell ablehnt, anstatt darauf zu warten, dass der Vorgang abgebrochen wird oder niemals zurückkehrt.It can help to maintain the response time of the system by quickly rejecting a request for an operation that's likely to fail, rather than waiting for the operation to time out, or never return. Wenn der Trennschalter bei jeder Zustandsänderung ein Ereignis auslöst, kann diese Information dazu verwendet werden, den Zustand des durch den Trennschalter geschützten Teils des Systems zu überwachen oder einen Administrator zu alarmieren, wenn ein Trennschalter in den Zustand Geöffnet wechselt.If the circuit breaker raises an event each time it changes state, this information can be used to monitor the health of the part of the system protected by the circuit breaker, or to alert an administrator when a circuit breaker trips to the Open state.

Das Muster ist anpassbar und kann je nach Art des möglichen Fehlers geändert werden.The pattern is customizable and can be adapted according to the type of the possible failure. So können Sie z. B. einem Trennschalter einen ansteigenden Timeout-Timer zuweisen.For example, you can apply an increasing timeout timer to a circuit breaker. Sie könnten den Trennschalter zunächst für ein paar Sekunden in den Zustand Geöffnet versetzen. Wenn der Fehler nicht behoben wurde, können Sie den Timeout auf ein paar Minuten erhöhen usw.You could place the circuit breaker in the Open state for a few seconds initially, and then if the failure hasn't been resolved increase the timeout to a few minutes, and so on. In manchen Fällen kann es sinnvoll sein, anstelle des Zustandes Geöffnet, der einen Fehler zurückgibt und eine Ausnahme auslöst, einen Standardwert zurückzugeben, der für die Anwendung relevant ist.In some cases, rather than the Open state returning failure and raising an exception, it could be useful to return a default value that is meaningful to the application.

Probleme und ÜberlegungenIssues and considerations

Bei der Entscheidung, wie dieses Muster implementiert werden soll, sind die folgenden Punkte zu beachten:You should consider the following points when deciding how to implement this pattern:

Fehlerbehandlung:Exception Handling. Eine Anwendung, die einen Vorgang über einen Trennschalter aufruft, muss darauf vorbereitet sein, die aufgetretenen Ausnahmen zu behandeln, wenn der Vorgang nicht verfügbar ist.An application invoking an operation through a circuit breaker must be prepared to handle the exceptions raised if the operation is unavailable. Die Art und Weise, wie Ausnahmen behandelt werden, ist anwendungsspezifisch.The way exceptions are handled will be application specific. Eine Anwendung könnte z. B. vorübergehend ihre Funktionalität herabsetzen, einen alternativen Vorgang aufrufen, um zu versuchen, dieselbe Aufgabe auszuführen oder dieselben Daten zu erhalten, oder die Ausnahme dem Benutzer melden und ihn auffordern, es später erneut zu versuchen.For example, an application could temporarily degrade its functionality, invoke an alternative operation to try to perform the same task or obtain the same data, or report the exception to the user and ask them to try again later.

Arten von Ausnahmen:Types of Exceptions. Eine Anforderung kann aus vielen Gründen fehlerhaft sein, von denen einige auf eine schwerwiegendere Art von Problemen hinweisen können als andere.A request might fail for many reasons, some of which might indicate a more severe type of failure than others. Eine Anforderung kann z. B. fehlerhaft sein, weil ein Remotedienst abgestürzt ist und die Wiederherstellung mehrere Minuten dauert, oder weil der Dienst vorübergehend überlastet ist und einen Timeout verursacht hat.For example, a request might fail because a remote service has crashed and will take several minutes to recover, or because of a timeout due to the service being temporarily overloaded. Ein Trennschalter könnte in der Lage sein, die Art der aufgetretenen Ausnahmen zu untersuchen und seine Strategie entsprechend der Art dieser Ausnahmen anzupassen.A circuit breaker might be able to examine the types of exceptions that occur and adjust its strategy depending on the nature of these exceptions. Es könnte z. B. eine größere Anzahl von Timeoutausnahmen erforderlich sein, um den Trennschalter in den Zustand Geöffnet zu versetzen, verglichen mit der Anzahl der Störungen, die durch nicht verfügbare Dienste hervorgerufen werden.For example, it might require a larger number of timeout exceptions to trip the circuit breaker to the Open state compared to the number of failures due to the service being completely unavailable.

Protokollierung:Logging. Ein Trennschalter sollte alle fehlerhaften Anforderungen (und möglicherweise erfolgreiche Anforderungen) protokollieren, damit ein Administrator die Integrität des Vorgangs überwachen kann.A circuit breaker should log all failed requests (and possibly successful requests) to enable an administrator to monitor the health of the operation.

Wiederherstellbarkeit:Recoverability. Sie sollten den Trennschalter so konfigurieren, dass er dem wahrscheinlichen Wiederherstellungsmuster des zu schützenden Vorgangs entspricht.You should configure the circuit breaker to match the likely recovery pattern of the operation it's protecting. Wenn sich der Trennschalter z. B. über einen längeren Zeitraum im Zustand Geöffnet befindet, kann er Ausnahmen auslösen, auch wenn die Fehlerursache behoben ist.For example, if the circuit breaker remains in the Open state for a long period, it could raise exceptions even if the reason for the failure has been resolved. Ebenso könnte ein Trennschalter schwanken und die Reaktionszeiten von Anwendungen verkürzen, wenn er zu schnell vom Zustand Geöffnet in den Zustand Halb geöffnet wechselt.Similarly, a circuit breaker could fluctuate and reduce the response times of applications if it switches from the Open state to the Half-Open state too quickly.

Testen fehlerhafter Vorgänge:Testing Failed Operations. Im Zustand Geöffnet kann ein Trennschalter für den Remotedienst oder die Ressource regelmäßig den Ping-Befehl ausführen, um zu ermitteln, ob der Dienst oder die Ressource wieder verfügbar ist, anstatt einen Timer zur Ermittlung zu verwenden, wann in den Zustand Halb geöffnet gewechselt werden muss.In the Open state, rather than using a timer to determine when to switch to the Half-Open state, a circuit breaker can instead periodically ping the remote service or resource to determine whether it's become available again. Dieser Ping-Befehl könnte die Form eines Aufrufversuchs für einen Vorgang annehmen, der zuvor fehlerhaft war, oder er könnte einen speziellen Vorgang verwenden, der vom Remotedienst speziell für die Prüfung der Integrität des Dienstes bereitgestellt wird, wie durch das Muster zur Überwachung des Integritätsendpunkts beschrieben.This ping could take the form of an attempt to invoke an operation that had previously failed, or it could use a special operation provided by the remote service specifically for testing the health of the service, as described by the Health Endpoint Monitoring pattern.

Manuelle Außerkraftsetzung:Manual Override. In einem System, in sich dem die Wiederherstellungszeit für einen fehlerhaften Vorgang als extrem variabel erweist, ist es vorteilhaft, eine manuelle Option zum Zurücksetzen bereitzustellen, mit der ein Administrator einen Trennschalter schließen (und den Fehlerzähler zurücksetzen) kann.In a system where the recovery time for a failing operation is extremely variable, it's beneficial to provide a manual reset option that enables an administrator to close a circuit breaker (and reset the failure counter). Ebenso könnte ein Administrator einen Trennschalter in den Zustand Geöffnet zwingen (und den Timeout-Timer neu starten), wenn der durch den Trennschalter geschützte Vorgang vorübergehend nicht verfügbar ist.Similarly, an administrator could force a circuit breaker into the Open state (and restart the timeout timer) if the operation protected by the circuit breaker is temporarily unavailable.

Parallelität:Concurrency. Auf denselben Trennschalter kann von einer großen Anzahl gleichzeitiger Instanzen einer Anwendung zugegriffen werden.The same circuit breaker could be accessed by a large number of concurrent instances of an application. Die Implementierung sollte keine gleichzeitigen Anforderungen blockieren oder übermäßigen Aufwand für die einzelnen Aufrufe eines Vorgangs verursachen.The implementation shouldn't block concurrent requests or add excessive overhead to each call to an operation.

Ressourcendifferenzierung:Resource Differentiation. Seien Sie bei der Verwendung eines einzelnen Trennschalters für einen Ressourcentyp vorsichtig, wenn mehrere unabhängige Anbieter zugrunde liegen.Be careful when using a single circuit breaker for one type of resource if there might be multiple underlying independent providers. In einem Datenspeicher, der mehrere Shards enthält, kann z. B. auf einen Shard uneingeschränkt zugegriffen werden, während bei einem anderen Shard ein temporäres Problem auftritt.For example, in a data store that contains multiple shards, one shard might be fully accessible while another is experiencing a temporary issue. Wenn die Fehlerreaktionen in diesen Szenarien zusammengeführt werden, könnte eine Anwendung versuchen, auf einige Shards zuzugreifen, selbst wenn ein Fehler sehr wahrscheinlich ist, während der Zugriff auf andere Shards blockiert wird, obwohl er voraussichtlich erfolgreich sein wird.If the error responses in these scenarios are merged, an application might try to access some shards even when failure is highly likely, while access to other shards might be blocked even though it's likely to succeed.

Beschleunigter Abschaltvorgang:Accelerated Circuit Breaking. Manchmal kann eine Fehlerreaktion ausreichend Informationen enthalten, damit der Trennschalter sofort und für eine minimale Zeitspanne ausgelöst wird.Sometimes a failure response can contain enough information for the circuit breaker to trip immediately and stay tripped for a minimum amount of time. Die Fehlerreaktion einer freigegebenen Ressource, die überlastet ist, könnte z. B. darauf hinweisen, dass eine sofortige Wiederholung nicht empfohlen wird. Stattdessen sollte es die Anwendung in einigen Minuten erneut versuchen.For example, the error response from a shared resource that's overloaded could indicate that an immediate retry isn't recommended and that the application should instead try again in a few minutes.

Hinweis

Ein Dienst kann HTTP 429 (Zu viele Anforderungen) zurückgeben, wenn er den Client beschränkt, oder HTTP 503 (Dienst nicht verfügbar), wenn der Dienst gerade nicht verfügbar ist.A service can return HTTP 429 (Too Many Requests) if it is throttling the client, or HTTP 503 (Service Unavailable) if the service is not currently available. Die Antwort kann zusätzliche Informationen enthalten, z. B. die erwartete Dauer der Verzögerung.The response can include additional information, such as the anticipated duration of the delay.

Wiedergeben fehlerhafter Anforderungen:Replaying Failed Requests. Im Zustand Geöffnet könnte ein Trennschalter auch die Details jeder Anforderung in einem Journal aufzeichnen und dafür sorgen, dass diese Anforderungen wiedergegeben werden, wenn die Remoteressource oder der Remotedienst verfügbar wird, anstatt einfach nur schnell auszufallen.In the Open state, rather than simply failing quickly, a circuit breaker could also record the details of each request to a journal and arrange for these requests to be replayed when the remote resource or service becomes available.

Ungeeignete Timeouts für externe Dienste:Inappropriate Timeouts on External Services. Ein Trennschalter kann Anwendungen möglicherweise nicht vollständig vor fehlerhaften Vorgängen in externen Diensten schützen, die mit einer langen Timeoutperiode konfiguriert sind.A circuit breaker might not be able to fully protect applications from operations that fail in external services that are configured with a lengthy timeout period. Wenn der Timeout zu lang ist, kann es vorkommen, dass ein Thread, der einen Trennschalter ausführt, für längere Zeit blockiert wird, bevor der Trennschalter anzeigt, dass der Vorgang fehlerhaft ist.If the timeout is too long, a thread running a circuit breaker might be blocked for an extended period before the circuit breaker indicates that the operation has failed. In dieser Zeit könnten viele andere Anwendungsinstanzen ebenfalls versuchen, den Dienst über den Trennschalter aufzurufen und eine beträchtliche Anzahl von Threads zu binden, bevor sie alle ausfallen.In this time, many other application instances might also try to invoke the service through the circuit breaker and tie up a significant number of threads before they all fail.

Verwendung dieses MustersWhen to use this pattern

Verwenden Sie dieses Muster in folgenden Fällen:Use this pattern:

  • Damit verhindert wird, dass eine Anwendung versucht, einen Remotedienst aufzurufen oder auf eine freigegebene Ressource zuzugreifen, wenn sich dieser Vorgang höchstwahrscheinlich als fehlerhaft erweist.To prevent an application from trying to invoke a remote service or access a shared resource if this operation is highly likely to fail.

Verwenden Sie dieses Muster in folgenden Fällen nicht:This pattern isn't recommended:

  • Für die Verwaltung des Zugriffs auf lokale private Ressourcen in einer Anwendung, z. B. die In-Memory-Datenstruktur.For handling access to local private resources in an application, such as in-memory data structure. In dieser Umgebung würde die Verwendung eines Trennschalters zu einem zusätzlichen Aufwand für Ihr System führen.In this environment, using a circuit breaker would add overhead to your system.
  • Als Ersatz für die Behandlung von Ausnahmen in der Geschäftslogik Ihrer Anwendungen.As a substitute for handling exceptions in the business logic of your applications.

BeispielExample

In einer Webanwendung werden mehrere Seiten mit Daten gefüllt, die von einem externen Dienst abgerufen werden.In a web application, several of the pages are populated with data retrieved from an external service. Wenn das System minimales Caching implementiert, führen die meisten Zugriffe auf diese Seiten zu einem Roundtrip zum Dienst.If the system implements minimal caching, most hits to these pages will cause a round trip to the service. Verbindungen von der Webanwendung zum Dienst können mit einer Timeoutperiode konfiguriert werden (normalerweise 60 Sekunden). Wenn der Dienst in dieser Zeit nicht antwortet, wird die Logik der einzelnen Webseiten davon ausgehen, dass der Dienst nicht verfügbar ist und eine Ausnahme auslösen.Connections from the web application to the service could be configured with a timeout period (typically 60 seconds), and if the service doesn't respond in this time the logic in each web page will assume that the service is unavailable and throw an exception.

Wenn der Dienst jedoch fehlerhaft und das System sehr ausgelastet ist, könnten Benutzer gezwungen sein, bis zu 60 Sekunden zu warten, bevor eine Ausnahme auftritt.However, if the service fails and the system is very busy, users could be forced to wait for up to 60 seconds before an exception occurs. Schließlich könnten Ressourcen wie Arbeitsspeicher, Verbindungen und Threads ausgelastet sein. Dadurch könnten andere Benutzer daran gehindert werden, sich mit dem System zu verbinden, selbst wenn sie nicht auf Seiten zugreifen, die Daten aus dem Dienst abrufen.Eventually resources such as memory, connections, and threads could be exhausted, preventing other users from connecting to the system, even if they aren't accessing pages that retrieve data from the service.

Die Skalierung des Systems durch Hinzufügen weiterer Webserver und die Implementierung eines Lastausgleichs könnte sich verzögern, wenn die Ressourcen ausgelastet sind. Es wird das Problem jedoch nicht lösen, da die Benutzeranforderungen immer noch nicht reagieren und allen Webservern eventuell immer noch keine Ressourcen zur Verfügung stehen.Scaling the system by adding further web servers and implementing load balancing might delay when resources become exhausted, but it won't resolve the issue because user requests will still be unresponsive and all web servers could still eventually run out of resources.

Ein Umschließen der Logik, die sich mit dem Dienst verbindet und die Daten in einem Trennschalter abruft, könnte helfen, dieses Problem zu lösen und den Dienstfehler eleganter zu handhaben.Wrapping the logic that connects to the service and retrieves the data in a circuit breaker could help to solve this problem and handle the service failure more elegantly. Benutzeranforderungen werden weiterhin fehlerhaft sein, aber sie werden schneller als fehlerhaft erkannt und Ressourcen werden daher nicht blockiert.User requests will still fail, but they'll fail more quickly and the resources won't be blocked.

Die Klasse CircuitBreaker verwaltet Zustandsinformationen zu einem Trennschalter in einem Objekt, das die im folgenden Code gezeigte ICircuitBreakerStateStore-Schnittstelle implementiert.The CircuitBreaker class maintains state information about a circuit breaker in an object that implements the ICircuitBreakerStateStore interface shown in the following code.

interface ICircuitBreakerStateStore
{
  CircuitBreakerStateEnum State { get; }

  Exception LastException { get; }

  DateTime LastStateChangedDateUtc { get; }

  void Trip(Exception ex);

  void Reset();

  void HalfOpen();

  bool IsClosed { get; }
}

Die State-Eigenschaft gibt den aktuellen Zustand des Trennschalters an, der gemäß der CircuitBreakerStateEnum-Enumeration entweder Geöffnet, Halb geöffnet oder Geschlossen ist.The State property indicates the current state of the circuit breaker, and will be either Open, HalfOpen, or Closed as defined by the CircuitBreakerStateEnum enumeration. Die IsClosed-Eigenschaft sollte „true“ sein, wenn der Trennschalter geschlossen ist, aber „false“, wenn er geöffnet oder halb geöffnet ist.The IsClosed property should be true if the circuit breaker is closed, but false if it's open or half open. Die Methode Trip schaltet den Zustand des Trennschalters in den Zustand „Geöffnet“ und zeichnet die Ausnahme auf, die zur Zustandsänderung geführt hat, sowie das Datum und die Uhrzeit, zu der die Ausnahme aufgetreten ist.The Trip method switches the state of the circuit breaker to the open state and records the exception that caused the change in state, together with the date and time that the exception occurred. Die Eigenschaften LastException und LastStateChangedDateUtc geben diese Informationen zurück.The LastException and the LastStateChangedDateUtc properties return this information. Die Methode Reset schließt den Trennschalter und die Methode HalfOpen legt den Trennschalter auf „Halb geöffnet“ fest.The Reset method closes the circuit breaker, and the HalfOpen method sets the circuit breaker to half open.

Die Klasse InMemoryCircuitBreakerStateStore enthält in diesem Beispiel eine Implementierung der Schnittstelle ICircuitBreakerStateStore.The InMemoryCircuitBreakerStateStore class in the example contains an implementation of the ICircuitBreakerStateStore interface. Die Klasse CircuitBreaker erstellt eine Instanz dieser Klasse, um den Zustand des Trennschalters aufzunehmen.The CircuitBreaker class creates an instance of this class to hold the state of the circuit breaker.

Die ExecuteAction-Methode in der CircuitBreaker-Klasse dient als Wrapper für einen Vorgang, der als Action-Delegat angegeben ist.The ExecuteAction method in the CircuitBreaker class wraps an operation, specified as an Action delegate. Wenn der Trennschalter geschlossen ist, ruft ExecuteAction den Delegat Action auf.If the circuit breaker is closed, ExecuteAction invokes the Action delegate. Wenn der Vorgang fehlerhaft ist, ruft ein Ausnahmehandler TrackException auf, wodurch der Trennschalter in den Zustand „Geöffnet“ versetzt wird.If the operation fails, an exception handler calls TrackException, which sets the circuit breaker state to open. Im folgende Codebeispiel wird dieser Ablauf hervorgehoben.The following code example highlights this flow.

public class CircuitBreaker
{
  private readonly ICircuitBreakerStateStore stateStore =
    CircuitBreakerStateStoreFactory.GetCircuitBreakerStateStore();

  private readonly object halfOpenSyncObject = new object ();
  ...
  public bool IsClosed { get { return stateStore.IsClosed; } }

  public bool IsOpen { get { return !IsClosed; } }

  public void ExecuteAction(Action action)
  {
    ...
    if (IsOpen)
    {
      // The circuit breaker is Open.
      ... (see code sample below for details)
    }

    // The circuit breaker is Closed, execute the action.
    try
    {
      action();
    }
    catch (Exception ex)
    {
      // If an exception still occurs here, simply
      // retrip the breaker immediately.
      this.TrackException(ex);

      // Throw the exception so that the caller can tell
      // the type of exception that was thrown.
      throw;
    }
  }

  private void TrackException(Exception ex)
  {
    // For simplicity in this example, open the circuit breaker on the first exception.
    // In reality this would be more complex. A certain type of exception, such as one
    // that indicates a service is offline, might trip the circuit breaker immediately.
    // Alternatively it might count exceptions locally or across multiple instances and
    // use this value over time, or the exception/success ratio based on the exception
    // types, to open the circuit breaker.
    this.stateStore.Trip(ex);
  }
}

Das folgende Beispiel zeigt den Code (im vorherigen Beispiel ausgelassen), der ausgeführt wird, wenn der Trennschalter nicht geschlossen ist.The following example shows the code (omitted from the previous example) that is executed if the circuit breaker isn't closed. Zuerst wird geprüft, ob der Trennschalter für einen längeren Zeitraum als die durch das lokale Feld OpenToHalfOpenWaitTime in der Klasse CircuitBreaker vorgegebene Zeit geöffnet war.It first checks if the circuit breaker has been open for a period longer than the time specified by the local OpenToHalfOpenWaitTime field in the CircuitBreaker class. In diesem Fall legt die ExecuteAction-Methode den Trennschalter auf „Halb geöffnet“ fest und versucht dann, den vom Delegaten Action angegebenen Vorgang auszuführen.If this is the case, the ExecuteAction method sets the circuit breaker to half open, then tries to perform the operation specified by the Action delegate.

Wenn der Vorgang erfolgreich ist, wird der Trennschalter in den Zustand „Geschlossen“ zurückgesetzt.If the operation is successful, the circuit breaker is reset to the closed state. Wenn der Vorgang fehlerhaft ist, wird er in den Zustand „Geöffnet“ zurückversetzt und der Zeitpunkt, zu dem die Ausnahme aufgetreten ist, wird aktualisiert, sodass der Trennschalter eine weitere Zeitspanne wartet, bevor er erneut versucht, den Vorgang auszuführen.If the operation fails, it is tripped back to the open state and the time the exception occurred is updated so that the circuit breaker will wait for a further period before trying to perform the operation again.

Wenn der Trennschalter nur kurzzeitig geöffnet war (kürzer als durch den Wert OpenToHalfOpenWaitTime angegeben), löst die Methode ExecuteAction einfach eine CircuitBreakerOpenException-Ausnahme aus und gibt den Fehler zurück, der den Übergang des Trennschalters in den Zustand „Geöffnet“ verursacht hat.If the circuit breaker has only been open for a short time, less than the OpenToHalfOpenWaitTime value, the ExecuteAction method simply throws a CircuitBreakerOpenException exception and returns the error that caused the circuit breaker to transition to the open state.

Darüber hinaus verwendet er eine Sperre, um zu verhindern, dass der Trennschalter versucht, gleichzeitige Aufrufe des Vorgangs durchzuführen, während er halb geöffnet ist.Additionally, it uses a lock to prevent the circuit breaker from trying to perform concurrent calls to the operation while it's half open. Ein gleichzeitiger Versuch zum Aufrufen des Vorgangs wird so behandelt, als ob der Trennschalter geöffnet wäre, und er wird mit einer Ausnahme fehlerhaft beendet, wie später beschrieben.A concurrent attempt to invoke the operation will be handled as if the circuit breaker was open, and it'll fail with an exception as described later.

    ...
    if (IsOpen)
    {
      // The circuit breaker is Open. Check if the Open timeout has expired.
      // If it has, set the state to HalfOpen. Another approach might be to
      // check for the HalfOpen state that had be set by some other operation.
      if (stateStore.LastStateChangedDateUtc + OpenToHalfOpenWaitTime < DateTime.UtcNow)
      {
        // The Open timeout has expired. Allow one operation to execute. Note that, in
        // this example, the circuit breaker is set to HalfOpen after being
        // in the Open state for some period of time. An alternative would be to set
        // this using some other approach such as a timer, test method, manually, and
        // so on, and check the state here to determine how to handle execution
        // of the action.
        // Limit the number of threads to be executed when the breaker is HalfOpen.
        // An alternative would be to use a more complex approach to determine which
        // threads or how many are allowed to execute, or to execute a simple test
        // method instead.
        bool lockTaken = false;
        try
        {
          Monitor.TryEnter(halfOpenSyncObject, ref lockTaken);
          if (lockTaken)
          {
            // Set the circuit breaker state to HalfOpen.
            stateStore.HalfOpen();

            // Attempt the operation.
            action();

            // If this action succeeds, reset the state and allow other operations.
            // In reality, instead of immediately returning to the Closed state, a counter
            // here would record the number of successful operations and return the
            // circuit breaker to the Closed state only after a specified number succeed.
            this.stateStore.Reset();
            return;
          }
        }
        catch (Exception ex)
        {
          // If there's still an exception, trip the breaker again immediately.
          this.stateStore.Trip(ex);

          // Throw the exception so that the caller knows which exception occurred.
          throw;
        }
        finally
        {
          if (lockTaken)
          {
            Monitor.Exit(halfOpenSyncObject);
          }
        }
      }
      // The Open timeout hasn't yet expired. Throw a CircuitBreakerOpen exception to
      // inform the caller that the call was not actually attempted,
      // and return the most recent exception received.
      throw new CircuitBreakerOpenException(stateStore.LastException);
    }
    ...

Wenn Sie ein CircuitBreaker-Objekt zum Schutz eines Vorgangs verwenden möchten, erstellt eine Anwendung eine Instanz der Klasse CircuitBreaker und ruft die Methode ExecuteAction auf, wobei der auszuführende Vorgang als Parameter angegeben wird.To use a CircuitBreaker object to protect an operation, an application creates an instance of the CircuitBreaker class and invokes the ExecuteAction method, specifying the operation to be performed as the parameter. Die Anwendung sollte darauf vorbereitet sein, die Ausnahme CircuitBreakerOpenException abzufangen, wenn der Vorgang fehlerhaft beendet wird, da der Trennschalter geöffnet ist.The application should be prepared to catch the CircuitBreakerOpenException exception if the operation fails because the circuit breaker is open. Der folgende Code zeigt ein Beispiel:The following code shows an example:

var breaker = new CircuitBreaker();

try
{
  breaker.ExecuteAction(() =>
  {
    // Operation protected by the circuit breaker.
    ...
  });
}
catch (CircuitBreakerOpenException ex)
{
  // Perform some different action when the breaker is open.
  // Last exception details are in the inner exception.
  ...
}
catch (Exception ex)
{
  ...
}

Die folgenden Muster können für die Implementierung dieses Musters relevant sein:The following patterns might also be useful when implementing this pattern:

  • Wiederholungsmuster:Retry pattern. Beschreibt, wie eine Anwendung beim Herstellen einer Verbindung mit einem Dienst oder einer Netzwerkressource antizipierte, temporäre Fehler behandeln kann, indem ein zuvor nicht erfolgreich durchgeführter Vorgang transparent wiederholt wird.Describes how an application can handle anticipated temporary failures when it tries to connect to a service or network resource by transparently retrying an operation that has previously failed.

  • Muster für Überwachung der Integrität von Endpunkten:Health Endpoint Monitoring pattern. Ein Trennschalter könnte in der Lage sein, den Zustand eines Diensts zu testen, indem er eine Anforderung an einen Endpunkt sendet, der durch den Dienst exponiert wird.A circuit breaker might be able to test the health of a service by sending a request to an endpoint exposed by the service. Der Dienst sollte Informationen über seinen Zustand zurückgeben.The service should return information indicating its status.