Implementierung von Circuit Breakern in Go Microservices mit Hystrix-Go
Lukas Schneider
DevOps Engineer · Leapcell

Einführung
In der komplexen Welt der Microservices-Architektur kann ein einzelner ausfallender Dienst schnell zu weit verbreiteten Ausfällen führen, die das Benutzererlebnis beeinträchtigen und den Geschäftsbetrieb beeinträchtigen. Dieses Phänomen, das oft als „kaskadierender Fehler“ bezeichnet wird, stellt eine erhebliche Herausforderung für die Stabilität und Zuverlässigkeit verteilter Systeme dar. Um dem entgegenzuwirken, haben sich Muster wie der Circuit Breaker zu kritischen Komponenten für den Aufbau robuster Microservices entwickelt. Dieser Artikel befasst sich mit der praktischen Anwendung des Circuit Breaker-Musters in Go Microservices, insbesondere unter Verwendung von Bibliotheken wie hystrix-go
, um kaskadierende Fehler zu mindern und die Systemrobustheit zu gewährleisten. Wir werden seine Konzepte untersuchen, seine Implementierung analysieren und seine Vorteile anhand praktischer Go-Codebeispiele veranschaulichen.
Circuit Breaker und verwandte Konzepte verstehen
Bevor wir uns mit der Implementierung befassen, wollen wir ein klares Verständnis der beteiligten Kernkonzepte schaffen:
-
Microservices: Ein Architekturstil, der eine Anwendung als eine Sammlung lose gekoppelter, unabhängig bereitstellbarer Dienste strukturiert. Obwohl sie Flexibilität bieten, führt diese verteilte Natur auch zu Komplexität in Bezug auf die inter-service-Kommunikation und die Fehlerbehandlung.
-
Kaskadierender Fehler: Eine Kettenreaktion, bei der ein Fehler in einem Dienst zu nachfolgenden Fehlern in abhängigen Diensten führt und potenziell zu einem vollständigen Systemausfall führt. Stellen Sie sich ein Szenario vor, in dem ein Datenbankverbindungspool erschöpft ist, was dazu führt, dass ein Datendienst ausfällt, der dann ein API-Gateway verhungert und letztendlich die gesamte Anwendung zum Absturz bringt.
-
Circuit Breaker-Muster: Inspiriert von elektrischen Leistungsschaltern überwacht dieses Muster Aufrufe an einen potenziell ausfallenden Dienst. Wenn die Fehlerrate einen vordefinierten Schwellenwert überschreitet, löst der Stromkreis aus (öffnet sich) und verhindert weitere Aufrufe an den ausfallenden Dienst, wodurch er Zeit zum Wiederherstellen erhält. Anstatt den nicht funktionierenden Dienst zu erreichen, wird ein sofortiger Fallback-Mechanismus ausgelöst. Dies verhindert, dass Ressourcen für einen fehlerhaften Dienst verbraucht werden, bietet eine schnellere Fehlerantwort und verhindert, dass nachgelagerte Dienste unbegrenzt blockieren.
-
hystrix-go
: Ein Go-Beitrag zum Hystrix-Ökosystem, inspiriert von der Hystrix-Bibliothek von Netflix. Es bietet robuste Circuit Breaker-Funktionen, einschließlich Fehlertoleranz, Latenztoleranz und Gleichzeitigkeitsgrenzen für kritische Dienste.
Implementierung von Circuit Breakern mit Hystrix-Go
Das Circuit Breaker-Muster arbeitet in drei Hauptzuständen:
-
Geschlossen: Der Stromkreis ist zunächst geschlossen und ermöglicht es Anfragen, den Ziel dienst zu durchlaufen. Hystrix-Go überwacht die Erfolgs- und Fehlerraten dieser Anfragen.
-
Offen: Wenn die Anzahl der Fehler (oder die Latenz) innerhalb eines definierten rollierenden Fensters den konfigurierten Schwellenwert überschreitet, löst der Stromkreis aus und öffnet sich. Alle nachfolgenden Anfragen an den Ziel dienst werden sofort abgelehnt, und eine Fallback-Funktion wird aufgerufen, ohne überhaupt zu versuchen, den ausfallenden dienst aufzurufen. Dieser Zustand hat typischerweise ein konfigurierbares „Schlaf-Fenster“ oder „Timeout“.
-
Halboffen: Nach Ablauf des Schlaf-Fensters wechselt der Stromkreis in den halboffenen Zustand. Eine begrenzte Anzahl von Testanfragen darf den Ziel dienst durchlaufen. Wenn diese Anfragen erfolgreich sind, schließt sich der Stromkreis wieder (vorausgesetzt, der Dienst hat sich erholt). Wenn sie fehlschlagen, kehrt der Stromkreis für ein weiteres Schlaf-Fenster in den offenen Zustand zurück.
Hystrix-Go-Konfiguration
hystrix-go
ermöglicht eine feingranulare Steuerung des Circuit Breaker-Verhaltens durch Konfiguration:
package main import ( "fmt" "io/ioutil" "net/http" "time" "github.com/afex/hystrix-go/hystrix" ) func main() { // Hystrix für einen bestimmten Befehlnamen konfigurieren hystrix.ConfigureCommand("my_service_call", hystrix.CommandConfig{ Timeout: 1000, // Timeout für die Befehlsausführung in Millisekunden MaxRequests: 10, // Maximale Anfragen pro rollierendem Fenster, bevor der Stromkreis ausgelöst werden kann ErrorPercentThreshold: 25, // Prozentsatz der Fehler, die den Stromkreis auslösen SleepWindow: 5000, // Zeit in Millisekunden, die der Stromkreis offen bleibt, bevor der Schließversuch unternommen wird RequestVolumeThreshold: 5, // Mindestanzahl von Anfragen in einem rollierenden Fenster, um den Stromkreis auszulösen }) // Beispiel: Simulieren eines ausfallenden Dienstes failingServiceEndpoint := "http://localhost:8081/fail" // Dieser Endpunkt simuliert Fehler // Eine Goroutine starten, um einen ausfallenden Dienst zu simulieren go startFailingService() // Mehrere Aufrufe an den potenziell ausfallenden Dienst tätigen for i := 0; i < 20; i++ { fmt.Printf("Versuch %d: ", i+1) err := hystrix.Do("my_service_call", func() error { // Dies ist die Funktion, die den tatsächlichen Aufruf an den Dienst ausführt resp, err := http.Get(failingServiceEndpoint) if err != nil { return err } defer resp.Body.Close() if resp.StatusCode != http.StatusOK { return fmt.Errorf("Dienst gab einen nicht-OK-Status zurück: %d", resp.StatusCode) } body, _ := ioutil.ReadAll(resp.Body) fmt.Printf("Dienstantwort: %s\n", string(body)) return nil }, func(err error) error { // Dies ist die Fallback-Funktion, die ausgeführt wird, wenn der Stromkreis offen ist oder der ursprüngliche Aufruf fehlschlägt fmt.Printf("Fallback ausgelöst! Fehler: %v\n", err) return nil // nil zurückgeben, wenn Sie den Fehler stillschweigend behandeln möchten }) if err != nil { fmt.Printf("Hystrix `Do` gab einen Fehler zurück: %v\n", err) } time.Sleep(500 * time.Millisecond) // Simulieren Sie eine Verzögerung zwischen den Anfragen } // Der Failing-Service-Goroutine etwas Zeit geben, um zu beenden time.Sleep(2 * time.Second) } // startFailingService simuliert einen Dienst, der manchmal fehlschlägt func startFailingService() { http.HandleFunc("/fail", func(w http.ResponseWriter, r *http.Request) { // Fehler 50 % der Zeit simulieren oder wenn der Dienst zu oft aufgerufen wurde if time.Now().Second()%2 == 0 { w.WriteHeader(http.StatusInternalServerError) w.Write([]byte("Simulierter interner Serverfehler")) } else { w.WriteHeader(http.StatusOK) w.Write([]byte("Dienst hat die Anfrage erfolgreich verarbeitet")) } }) fmt.Println("Ausfallender Dienst hört auf Port :8081") http.ListenAndServe(":8081", nil) }
In diesem Beispiel:
- Wir definieren einen Hystrix-Befehl namens `