Kapitel 1: Einführung in das Wiederholen in Go
1.1 Verständnis der Notwendigkeit von Wiederholungsmechanismen
In vielen Rechenszenarien, insbesondere bei der Arbeit mit verteilten Systemen oder der Netzwerkkommunikation, können Operationen aufgrund transienter Fehler fehlschlagen. Diese Fehler sind oft vorübergehende Probleme wie Netzwerkinstabilität, kurzzeitige Nichtverfügbarkeit eines Dienstes oder Zeitüberschreitungen. Anstatt sofort zu scheitern, sollten Systeme so konzipiert sein, dass sie Operationen, die auf solche vorübergehenden Fehler stoßen, wiederholen. Dieser Ansatz verbessert die Zuverlässigkeit und Robustheit.
Wiederholungsmechanismen können in Anwendungen, in denen Konsistenz und Vollständigkeit von Operationen erforderlich sind, entscheidend sein. Sie können auch die Fehlerrate reduzieren, die Endbenutzer erleben. Die Implementierung eines Wiederholungsmechanismus bringt jedoch Herausforderungen mit sich, wie z.B. die Entscheidung, wie oft und wie lange vor dem Aufgeben wiederholt werden soll. Genau hier spielen Backoff-Strategien eine bedeutende Rolle.
1.2 Überblick über die go-retry
-Bibliothek
Die go-retry
-Bibliothek in Go bietet eine flexible Möglichkeit, Ihrem Anwendungen mit verschiedenen Backoff-Strategien Wiederholungslogik hinzuzufügen. Die wichtigsten Funktionen sind:
- Erweiterbarkeit: Wie das
http
-Paket von Go ist auchgo-retry
darauf ausgelegt, mit Zwischensoftware erweiterbar zu sein. Sie können sogar Ihre eigenen Backoff-Funktionen schreiben oder die praktischen Filter verwenden. - Unabhängigkeit: Die Bibliothek verlässt sich nur auf die Go-Standardbibliothek und vermeidet externe Abhängigkeiten, wodurch Ihr Projekt schlank bleibt.
- Nebenläufigkeit: Sie ist für den gleichzeitigen Einsatz sicher und kann mit Goroutinen ohne zusätzliche Probleme arbeiten.
- Kontextbewusst: Sie unterstützt native Go-Kontexte für Timeout und Abbruch und integriert sich nahtlos in das Nebenläufigkeitsmodell von Go.
Kapitel 2: Importieren von Bibliotheken
Bevor Sie die go-retry
-Bibliothek verwenden können, muss sie in Ihr Projekt importiert werden. Dies kann mit go get
erfolgen, dem Go-Befehl zum Hinzufügen von Abhängigkeiten zu Ihrem Modul. Öffnen Sie einfach Ihr Terminal und führen Sie folgendes aus:
go get github.com/sethvargo/go-retry
Dieser Befehl wird die go-retry
-Bibliothek abrufen und zu den Abhängigkeiten Ihres Projekts hinzufügen. Danach können Sie sie wie jedes andere Go-Paket in Ihren Code importieren.
Kapitel 3: Implementierung einer einfachen Wiederholungslogik
3.1 Einfache Wiederholung mit konstantem Backoff
Die einfachste Form der Wiederholungslogik beinhaltet das Warten auf eine konstante Dauer zwischen jedem Wiederholungsversuch. Sie können go-retry
verwenden, um Wiederholungen mit konstantem Backoff durchzuführen.
Hier ist ein Beispiel, wie man mit go-retry
einen konstanten Backoff verwendet:
package main
import (
"context"
"time"
"github.com/sethvargo/go-retry"
)
func main() {
ctx := context.Background()
// Erstellen eines neuen konstanten Backoffs
backoff := retry.NewConstant(1 * time.Second)
// Verpacken Sie Ihre Wiederholungslogik in eine Funktion, die an retry.Do übergeben wird
operation := func(ctx context.Context) error {
// Ihr Code hier. Geben Sie retry.RetryableError(err) zurück, um zu wiederholen, oder nil, um anzuhalten.
// Beispiel:
// err := someOperation()
// if err != nil {
// return retry.RetryableError(err)
// }
// return nil
return nil
}
// Verwenden von retry.Do mit dem gewünschten Kontext, der Backoff-Strategie und der Operation
if err := retry.Do(ctx, backoff, operation); err != nil {
// Fehler behandeln
}
}
In diesem Beispiel wird die Funktion retry.Do
den operation
-Funktion alle 1 Sekunde weiterhin versuchen, bis sie erfolgreich ist oder der Kontext abläuft oder abgebrochen wird.
3.2 Implementierung des exponentiellen Backoff
Exponentieller Backoff erhöht die Wartezeit zwischen den Wiederholungen exponentiell. Diese Strategie hilft, die Belastung des Systems zu reduzieren und ist besonders nützlich bei der Arbeit mit großangelegten Systemen oder Cloud-Diensten.
So wird der exponentielle Backoff mit go-retry
verwendet:
package main
import (
"context"
"time"
"github.com/sethvargo/go-retry"
)
func main() {
ctx := context.Background()
// Erstellen eines neuen exponentiellen Backoffs
backoff := retry.NewExponential(1 * time.Second)
// Bereitstellung Ihrer wiederholbaren Operation
operation := func(ctx context.Context) error {
// Implementieren Sie die Operation wie zuvor gezeigt
return nil
}
// Verwendung von retry.Do zum Ausführen der Operation mit exponentiellem Backoff
if err := retry.Do(ctx, backoff, operation); err != nil {
// Fehler behandeln
}
}
Im Falle des exponentiellen Backoffs erfolgen die Wiederholungen bei einer anfänglichen Wartezeit von 1 Sekunde, dann nach 1s, 2s, 4s usw., wobei die Wartezeit zwischen den folgenden Wiederholungen exponentiell zunimmt.
3.3 Fibonacci-Backoff-Strategie
Die Fibonacci-Backoff-Strategie verwendet die Fibonacci-Sequenz, um die Wartezeit zwischen Wiederholungsversuchen zu bestimmen, was eine gute Strategie für netzwerkbezogene Probleme ist, bei denen eine allmählich ansteigende Time-Out-Dauer vorteilhaft ist.
Die Implementierung der Fibonacci-Backoff-Strategie mit go-retry
wird unten gezeigt:
package main
import (
"context"
"time"
"github.com/sethvargo/go-retry"
)
func main() {
ctx := context.Background()
// Erstellen eines neuen Fibonacci-Backoffs
backoff := retry.NewFibonacci(1 * time.Second)
// Definition einer Operation zum Wiederholen
operation := func(ctx context.Context) error {
// Hier würde die Logik stehen, um die Aktion auszuführen, die möglicherweise fehlschlägt und wiederholt werden muss
return nil
}
// Ausführen der Operation mit Fibonacci-Backoff unter Verwendung von retry.Do
if err := retry.Do(ctx, backoff, operation); err != nil {
// Fehlerbehandlung
}
}
Bei einem Fibonacci-Backoff mit einem Anfangswert von 1 Sekunde finden die Wiederholungsversuche nach 1s, 1s, 2s, 3s, 5s usw. gemäß der Fibonacci-Sequenz statt.
Kapitel 4: Fortgeschrittene Wiederholungstechniken und Middleware
4.1 Verwendung von Jitter bei Wiederholungen
Bei der Implementierung von Wiederholungslogik ist es wichtig, die Auswirkungen gleichzeitiger Wiederholungen auf ein System zu berücksichtigen, die zu einem "Rudelschlag"-Problem führen können. Zur Minderung dieses Problems können wir zufälligen Jitter zu den Backoff-Intervallen hinzufügen. Diese Technik hilft dabei, die Wiederholungsversuche zu staffeln und die Wahrscheinlichkeit von gleichzeitigen Wiederholungen mehrerer Clients zu reduzieren.
Beispiel für das Hinzufügen von Jitter:
b := retry.NewFibonacci(1 * time.Second)
// Den nächsten Wert zurückgeben, +/- 500ms
b = retry.WithJitter(500 * time.Millisecond, b)
// Den nächsten Wert zurückgeben, +/- 5% des Ergebnisses
b = retry.WithJitterPercent(5, b)
4.2 Festlegen von maximalen Wiederholungen
In einigen Szenarien ist es notwendig, die Anzahl der Wiederholungsversuche zu begrenzen, um anhaltende und ineffektive Wiederholungen zu vermeiden. Durch Festlegung der maximalen Anzahl von Wiederholungen können wir die Anzahl der Versuche steuern, bevor die Operation aufgegeben wird.
Beispiel für die Festlegung maximaler Wiederholungen:
b := retry.NewFibonacci(1 * time.Second)
// Nach 4 Wiederholungen stoppen, wenn der 5. Versuch fehlgeschlagen ist
b = retry.WithMaxRetries(4, b)
4.3 Begrenzung einzelner Backoff-Dauern
Um sicherzustellen, dass einzelne Backoff-Dauern einen bestimmten Schwellenwert nicht überschreiten, können wir das CappedDuration
-Middleware verwenden. Dies verhindert, dass übermäßig lange Backoff-Intervalle berechnet werden und verleiht dem Wiederholungsverhalten Vorhersehbarkeit.
Beispiel für das Begrenzen einzelner Backoff-Dauern:
b := retry.NewFibonacci(1 * time.Second)
// Sicherstellen, dass der maximale Wert 2s beträgt
b = retry.WithCappedDuration(2 * time.Second, b)
4.4 Steuern der gesamten Wiederholungsdauer
In Szenarien, in denen eine Begrenzung der Gesamtdauer für den gesamten Wiederholungsprozess erforderlich ist, kann das WithMaxDuration
-Middleware verwendet werden, um eine maximale Gesamtausführungszeit anzugeben. Dies stellt sicher, dass der Wiederholungsprozess nicht endlos fortgesetzt wird und setzt ein Zeitbudget für die Wiederholungen.
Beispiel für die Steuerung der gesamten Wiederholungsdauer:
b := retry.NewFibonacci(1 * time.Second)
// Sicherstellen, dass die maximale Gesamtwiederholungszeit 5s beträgt
b = retry.WithMaxDuration(5 * time.Second, b)