Rozdział 1: Wprowadzenie do ponownego próbowania w Go
1.1 Zrozumienie potrzeby mechanizmów ponownej próby
W wielu scenariuszach informatycznych, zwłaszcza przy pracy z systemami rozproszonymi lub komunikacją sieciową, operacje mogą zawieść z powodu błędów tymczasowych. Te błędy są często chwilowymi problemami, takimi jak niestabilność sieci, krótkotrwała niedostępność usługi lub przekroczenie limitu czasu. Zamiast natychmiastowego zakończenia, systemy powinny być zaprojektowane tak, aby ponawiały operacje, które napotykają takie chwilowe błędy. Taki podejście poprawia niezawodność i odporność.
Mechanizmy ponownej próby mogą być kluczowe w aplikacjach, gdzie konieczna jest spójność i kompletność operacji. Mogą również zmniejszyć wskaźnik błędów, z którymi użytkownicy końcowi mają do czynienia. Jednak implementacja mechanizmu ponownej próby wiąże się z wyzwaniami, takimi jak decydowanie o częstotliwości i długości ponownej próby przed poddaniem się. Tutaj dużą rolę odgrywają strategie wycofania się.
1.2 Przegląd biblioteki go-retry
Biblioteka go-retry
w języku Go dostarcza elastyczny sposób dodawania logiki ponownej próby do aplikacji z różnymi strategiami wycofania. Główne funkcje obejmują:
- Rozszerzalność: Tak jak pakiet
http
w Go,go-retry
jest zaprojektowany do rozszerzania za pomocą pośredników. Możesz nawet napisać własne funkcje wycofania się lub skorzystać z dostarczonych przydatnych filtrów. - Niezależność: Biblioteka polega tylko na standardowej bibliotece Go i unika zewnętrznych zależności, co utrzymuje projekt w postaci lekkiej.
- Jednoczesność: Jest bezpieczna do wykorzystania jednocześnie i może działać z gorutynami bez dodatkowych kłopotów.
- Świadomość kontekstu: Obsługuje natywne konteksty Go w przypadku limitu czasu i anulowania, doskonale integrując się z modelem współbieżności Go.
Rozdział 2: Importowanie bibliotek
Zanim będziesz mógł korzystać z biblioteki go-retry
, musi być ona zaimportowana do Twojego projektu. Można to zrobić za pomocą polecenia go get
, które jest poleceniem Go do dodawania zależności do Twojego modułu. Wystarczy otworzyć terminal i wykonać:
go get github.com/sethvargo/go-retry
To polecenie pobierze bibliotekę go-retry
i doda ją do zależności Twojego projektu. Następnie możesz ją zaimportować do swojego kodu jak każdy inny pakiet Go.
Rozdział 3: Implementacja podstawowej logiki ponownej próby
3.1 Prosta ponowna próba z wycofaniem stałym
Najprostsza forma logiki ponownej próby polega na oczekiwaniu przez stały czas między każdą próbą ponowną. Możesz użyć go-retry
do wykonywania ponownych prób z wycofaniem stałym.
Oto przykład użycia stałego wycofania z go-retry
:
package main
import (
"context"
"time"
"github.com/sethvargo/go-retry"
)
func main() {
ctx := context.Background()
// Utwórz nowe stałe wycofanie
backoff := retry.NewConstant(1 * time.Second)
// Obejmij swoją logikę ponownej próby funkcją, która zostanie przekazana do retry.Do
operation := func(ctx context.Context) error {
// Twój kod tutaj. Zwróć retry.RetryableError(err), aby spróbować ponownie, lub nil, aby zatrzymać.
// Przykład:
// err := someOperation()
// if err != nil {
// return retry.RetryableError(err)
// }
// return nil
return nil
}
// Użyj retry.Do z pożądanym kontekstem, strategią wycofania się i operacją
if err := retry.Do(ctx, backoff, operation); err != nil {
// Obsłuż błąd
}
}
W tym przykładzie funkcja retry.Do
będzie próbować wykonać funkcję operation
co sekundę, dopóki się nie powiedzie, albo dopóki nie dojdzie do wygaśnięcia kontekstu lub jego anulowania.
3.2 Implementacja wykładniczego wycofania się
Wykładnicze wycofanie zwiększa czas oczekiwania między ponownymi próbami wykładniczo. Ta strategia pomaga zmniejszyć obciążenie systemu i jest szczególnie przydatna przy pracy z systemami o dużym rozmiarze lub usługami chmurowymi.
Sposób użycia wykładniczego wycofania się z go-retry
jest następujący:
package main
import (
"context"
"time"
"github.com/sethvargo/go-retry"
)
func main() {
ctx := context.Background()
// Utwórz nowe wykładnicze wycofanie się
backoff := retry.NewExponential(1 * time.Second)
// Udostępnij swoją operację ponownej próby
operation := func(ctx context.Context) error {
// Zaimplementuj operację jak wcześniej pokazano
return nil
}
// Użyj retry.Do do wykonania operacji z wykładniczym wycofaniem się
if err := retry.Do(ctx, backoff, operation); err != nil {
// Obsłuż błąd
}
}
W przypadku wykładniczego wycofania, jeśli początkowe wycofanie wynosi 1 sekundę, ponowne próby będą miały miejsce po 1s, 2s, 4s, itd., z wykładniczym zwiększaniem czasu oczekiwania między kolejnymi próbami.
3.3 Strategia wycofywania się ciągu Fibonacciego
Strategia wycofywania się ciągu Fibonacciego wykorzystuje ciąg Fibonacciego do określenia czasu oczekiwania między ponownymi próbami, co może być dobrą strategią w przypadku problemów związanych z siecią, gdzie stopniowo zwiększające się opóźnienie jest korzystne.
Poniżej przedstawiono implementację strategii wycofywania się ciągu Fibonacciego z użyciem go-retry
:
package main
import (
"context"
"time"
"github.com/sethvargo/go-retry"
)
func main() {
ctx := context.Background()
// Utwórz nowe wycofanie ciągu Fibonacciego
backoff := retry.NewFibonacci(1 * time.Second)
// Zdefiniuj operację do ponownego wykonania
operation := func(ctx context.Context) error {
// Tutaj znajdowałaby się logika wykonywania akcji, która może nie powieść się i wymaga ponownego wykonania
return nil
}
// Wykonaj operację z wycofywaniem się ciągu Fibonacciego za pomocą retry.Do
if err := retry.Do(ctx, backoff, operation); err != nil {
// Obsłuż błąd
}
}
Dla wycofywania się ciągu Fibonacciego z początkową wartością 1 sekundy, ponowne próby nastąpią po 1 s, 1 s, 2 s, 3 s, 5 s, itd., zgodnie z ciągiem Fibonacciego.
Rozdział 4: Zaawansowane Techniki Ponownego Próbowania i Middleware
4.1 Wykorzystanie Losowego Szumu w Ponownych Próbach
Podczas implementowania logiki ponownych prób ważne jest uwzględnienie wpływu jednoczesnych ponownych prób na system, co może prowadzić do problemu tłumu słoni. Aby złagodzić ten problem, możemy dodać losowy szum do interwałów wycofywania się. Ta technika pomaga rozłożyć próby ponownego wykonania, zmniejszając prawdopodobieństwo jednoczesnych prób ponownego wykonania przez kilku klientów.
Przykład dodania szumu:
b := retry.NewFibonacci(1 * time.Second)
// Zwróć kolejną wartość, +/- 500ms
b = retry.WithJitter(500 * time.Millisecond, b)
// Zwróć następną wartość, +/- 5% wyniku
b = retry.WithJitterPercent(5, b)
4.2 Ustawienie Maksymalnej Liczby Ponownych Prób
W niektórych przypadkach konieczne jest ograniczenie liczby ponownych prób, aby zapobiec przedłużonym i nieskutecznym próbom. Poprzez określenie maksymalnej liczby ponownych prób możemy kontrolować liczbę prób przed zrezygnowaniem z operacji.
Przykład ustawienia maksymalnych ponownych prób:
b := retry.NewFibonacci(1 * time.Second)
// Zatrzymaj się po 4 próbach, gdy piąta próba zawiedzie
b = retry.WithMaxRetries(4, b)
4.3 Ograniczanie Poszczególnych Czasów Wycofywania
Aby zapewnić, że poszczególne czasy wycofywania się nie przekraczają określonego progu, możemy użyć middleware CappedDuration
. Zapobiega to obliczaniu nadmiernie długich interwałów wycofywania się, dodając przewidywalność do zachowania ponownych prób.
Przykład ograniczenia poszczególnych czasów wycofywania:
b := retry.NewFibonacci(1 * time.Second)
// Upewnij się, że maksymalna wartość wynosi 2s
b = retry.WithCappedDuration(2 * time.Second, b)
4.4 Kontrolowanie Całkowitego Czasu Ponownych Prób
W przypadkach, gdy konieczne jest nałożenie ograniczenia na całkowity czas trwania całego procesu ponownych prób, można użyć middleware WithMaxDuration
, aby określić maksymalny czas całkowitego wykonania. Zapewnia to, że proces ponownych prób nie będzie trwał czas nieokreślony, narzucając limit czasowy dla ponownych prób.
Przykład kontrolowania całkowitego czasu ponownych prób:
b := retry.NewFibonacci(1 * time.Second)
// Upewnij się, że maksymalny całkowity czas ponownych prób wynosi 5s
b = retry.WithMaxDuration(5 * time.Second, b)