0Pricing
C# Academy · Lekcja

Podstawy odporności: ponawianie i backoff

Proszę implementować proste ponawianie z wykładniczym backoffem i jitterem, rozpoznawać błędy przejściowe i krytyczne, ograniczać opóźnienia oraz zatrzymywać operacje po anulowaniu.

Podstawy odporności: ponawianie i backoff to bezpłatna lekcja C# Academy na CoddyKit. To lekcja 3 z 3. Możesz przeczytać całą lekcję poniżej za darmo — a potem ćwiczyć ją interaktywnie w przeglądarce z wbudowanym edytorem kodu i tutorem AI dostępnym 24/7. To część ścieżki edukacyjnej C# Academy, a Twój postęp synchronizuje się między webem a aplikacją CoddyKit. Kurs C# Academy zawiera 3 lekcji w sumie.

Dlaczego ponawianie prób i backoff

Cel: Zapewnienie odporności wywołań.

  • Ponawianie prób po wystąpieniu przejściowych błędów
  • Stosowanie exponential backoff i mechanizmu jitter
  • Ustawienie maksymalnej liczby prób i maksymalnego opóźnienia
  • Zatrzymywanie działania po wystąpieniu błędów krytycznych lub anulowaniu

Klasyfikowanie błędów

Przejściowe: przekroczenia limitu czasu, odpowiedzi 429/5xx, tymczasowe zakłócenia sieci.

Krytyczne: nieprawidłowe żądania, brak autoryzacji, błędy walidacji — nie należy ponawiać takich operacji.

Reguła: należy ponawiać tylko te operacje, które mogą zakończyć się powodzeniem później.

Pomocnicza funkcja backoff

Niewielka funkcja pomocnicza zwiększa opóźnienie przy każdej próbie i dodaje niewielki jitter; ogranicza również maksymalne opóźnienie.

using System;
using System.Threading;
using System.Threading.Tasks;

public static class Backoff
{
  // Compute delay = min(base * 2^attempt, cap) + jitter
  public static int ComputeDelayMs(int attempt, int baseMs, int capMs, int jitterMs, Random rng)
  {
    long exp = (long)baseMs << attempt; // base * 2^attempt
    if (exp > capMs) exp = capMs;
    int jitter = rng.Next(0, jitterMs + 1); // [0..jitterMs]
    return (int)exp + jitter;
  }
}

public class Program
{
  public static void Main(string[] args)
  {
    Random rng = new Random(123);
    for (int i = 0; i < 5; i++)
    {
      int d = Backoff.ComputeDelayMs(i, 100, 2000, 50, rng);
      Console.WriteLine("Attempt " + i + " -> " + d + " ms");
    }
  }
}

Wrapper ponawiania prób

Wywołanie należy opakować tak, aby ponawiać próbę tylko po wystąpieniu wyjątków przejściowych, a po pozostałych wyjątkach zakończyć działanie. Każda kolejna próba jest poprzedzona coraz dłuższym opóźnieniem z jitterem.

using System;
using System.Threading;
using System.Threading.Tasks;

public class Program
{
  static readonly Random Rng = new Random();

  // Simulate an operation that sometimes fails transiently.
  static async Task<string> FlakyAsync()
  {
    await Task.Delay(60);
    // 50% chance of transient failure
    if (DateTime.UtcNow.Ticks % 2 == 0) throw new TimeoutException("Transient");
    return "OK";
  }

  static async Task<T> RetryAsync<T>(Func<Task<T>> action, int maxAttempts)
  {
    int baseMs = 100, capMs = 2000, jitterMs = 50;
    for (int attempt = 0; attempt < maxAttempts; attempt++)
    {
      try
      {
        return await action();
      }
      catch (TimeoutException)
      {
        if (attempt == maxAttempts - 1) throw; // out of retries
        int delay = Backoff.ComputeDelayMs(attempt, baseMs, capMs, jitterMs, Rng);
        await Task.Delay(delay);
      }
      catch (Exception)
      {
        // Non-transient (unknown) -> do not retry
        throw;
      }
    }
    throw new InvalidOperationException("Unreachable");
  }

  public static void Main(string[] args)
  {
    try
    {
      string s = RetryAsync(FlakyAsync, 5).GetAwaiter().GetResult();
      Console.WriteLine("Result: " + s);
    }
    catch (Exception ex)
    {
      Console.WriteLine("Failed: " + ex.GetType().Name);
    }
  }
}

Ponawianie prób w ramach budżetu

Należy dodać globalny budżet za pomocą anulowania: przekazać tokeny zarówno do operacji, jak i do opóźnień backoff.

using System;
using System.Threading;
using System.Threading.Tasks;

public static class RetryUtil
{
  static readonly Random Rng = new Random();

  public static async Task<T> RetryAsync<T>(Func<CancellationToken, Task<T>> action, int maxAttempts, CancellationToken ct)
  {
    int baseMs = 100, capMs = 1500, jitterMs = 50;
    for (int attempt = 0; attempt < maxAttempts; attempt++)
    {
      ct.ThrowIfCancellationRequested();
      try
      {
        return await action(ct);
      }
      catch (TimeoutException)
      {
        if (attempt == maxAttempts - 1) throw;
        int delay = Backoff.ComputeDelayMs(attempt, baseMs, capMs, jitterMs, Rng);
        await Task.Delay(delay, ct); // pass token so we can abort waiting
      }
    }
    throw new InvalidOperationException("Unreachable");
  }
}

public class Program
{
  static async Task<string> SometimesSlowAsync(CancellationToken ct)
  {
    await Task.Delay(200, ct);
    if (DateTime.UtcNow.Millisecond % 3 != 0) throw new TimeoutException("Transient");
    return "OK";
  }

  public static void Main(string[] args)
  {
    var cts = new CancellationTokenSource();
    cts.CancelAfter(800); // overall budget

    try
    {
      string s = RetryUtil.RetryAsync(SometimesSlowAsync, 5, cts.Token).GetAwaiter().GetResult();
      Console.WriteLine("Done: " + s);
    }
    catch (OperationCanceledException) { Console.WriteLine("Canceled"); }
    catch (Exception ex) { Console.WriteLine("Failed: " + ex.Message); }
  }
}

Wytyczne

Wskazówki:

  • Należy ponawiać tylko idempotentne wywołania.
  • Należy ustawiać limity liczby prób i opóźnienia.
  • Należy dodawać niewielki jitter, aby uniknąć lawiny równoczesnych żądań.
  • Należy rejestrować końcową porażkę wraz z jej przyczyną i liczbą prób.

Definicja backoff

Szybkie sprawdzenie: Które stwierdzenie najlepiej opisuje exponential backoff z jitterem?

Podsumowanie

Podsumowanie: Należy ponawiać tylko operacje zakończone przejściowymi błędami, wykładniczo zwiększać opóźnienia z zastosowaniem limitu, dodawać jitter i zatrzymywać działanie po anulowaniu lub wystąpieniu błędów krytycznych.

Często zadawane pytania

Czy lekcja „Podstawy odporności: ponawianie i backoff” jest bezpłatna?

Tak — pełny tekst „Podstawy odporności: ponawianie i backoff” jest dostępny za darmo tutaj w sieci. Aby ćwiczyć ją interaktywnie (wbudowany edytor kodu i tutor AI dostępny 24/7) i odblokować resztę kursu C# Academy, przejdź na CoddyKit PRO. Kurs C# Academy zawiera 3 lekcji w sumie.

Co nauczysz się w „Podstawy odporności: ponawianie i backoff”?

Proszę implementować proste ponawianie z wykładniczym backoffem i jitterem, rozpoznawać błędy przejściowe i krytyczne, ograniczać opóźnienia oraz zatrzymywać operacje po anulowaniu. Ćwiczysz C# Academy z praktycznym kodem, który uruchamiasz bezpośrednio w przeglądarce, a tutor AI dostępny 24/7 odpowiada na Twoje pytania podczas pracy nad lekcją.

Czy potrzebuję doświadczenia, aby zacząć C# Academy?

Nie wymagamy żadnego doświadczenia. C# Academy w CoddyKit jest strukturyzowany dla początkujących i zaawansowanych użytkowników, więc możesz zacząć tutaj lub od początku i uczyć się w swoim tempie. To lekcja 3 z 3.

Ile czasu zajmuje lekcja „Podstawy odporności: ponawianie i backoff”?

Większość lekcji CoddyKit trwa około 5–10 minut. Każda lekcja to mały, interaktywny krok, dzięki czemu robisz systematyczne postępy i zawsze wracasz dokładnie do tego samego miejsca — na webie i w aplikacji.

Czy mogę pisać i uruchamiać kod w tej lekcji C# Academy?

Tak. Każda lekcja C# Academy zawiera wbudowany edytor kodu, więc piszesz i uruchamiasz prawdziwy kod bezpośrednio w przeglądarce i od razu otrzymujesz sprzężenie zwrotne od AI — bez konfiguracji na komputerze.

Wszystkie lekcje w tym kursie

  1. Wzorce CancellationToken i kooperacyjne anulowanie
  2. Limity czasu, IProgress i zasoby async (emulacja)
  3. Podstawy odporności: ponawianie i backoff
← Powrót do C# Academy