0Pricing
C# Academy · Урок

Основы устойчивости: повторные попытки и интервалы между ними

Реализуйте простые повторные попытки с экспоненциальным увеличением интервалов и случайным разбросом; отличайте временные ошибки от критических, ограничивайте задержки и прекращайте работу при отмене.

«Основы устойчивости: повторные попытки и интервалы между ними» — бесплатный урок C# Academy на CoddyKit. Это урок 3 из 3. Ты можешь прочитать весь урок бесплатно ниже — а потом практиковать его прямо в браузере с встроенным редактором кода и ИИ-репетитором 24/7. Это часть пути обучения C# Academy, и твой прогресс синхронизируется между веб-версией и приложением CoddyKit. Курс C# Academy содержит 3 уроков всего.

Зачем нужны повторные попытки и Backoff

Цель: сделать вызовы устойчивыми.

  • Повторять попытки при временных сбоях
  • Использовать экспоненциальный Backoff и случайное отклонение
  • Задавать максимальное число попыток и максимальную задержку
  • Останавливаться при критических ошибках или отмене

Классификация сбоев

Временные: тайм-ауты, ответы 429/5xx, временные сбои сети.

Критические: неверный запрос, отсутствие авторизации, ошибки проверки данных — повторять попытку не следует.

Правило: повторяйте попытку только для операций, которые могут успешно завершиться позже.

Вспомогательный код для Backoff

Небольшой вспомогательный метод увеличивает задержку с каждой попыткой и добавляет небольшое случайное отклонение; максимальную задержку также следует ограничить.

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");
    }
  }
}

Обёртка для повторных попыток

Оберните вызов: повторяйте попытку только при временных исключениях, а при остальных останавливайтесь. Перед каждой следующей попыткой ждите дольше, добавляя случайное отклонение.

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);
    }
  }
}

Повторные попытки в заданном бюджете

Добавьте глобальный бюджет с помощью отмены: передавайте токены и операции, и задержкам между повторными попытками.

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); }
  }
}

Рекомендации

Советы:

  • Повторяйте попытки только для идемпотентных вызовов.
  • Ограничивайте число попыток и задержку.
  • Добавляйте небольшое случайное отклонение, чтобы избежать резкого наплыва запросов.
  • Записывайте в журнал итоговый сбой, его причину и число попыток.

Определение Backoff

Быстрая проверка: что лучше всего описывает экспоненциальный Backoff со случайным отклонением?

Итоги

Итоги: повторяйте попытки только при временных ошибках, экспоненциально увеличивайте задержки с установленным ограничением, добавляйте случайное отклонение и останавливайтесь при отмене или критических ошибках.

Часто задаваемые вопросы

Урок «Основы устойчивости: повторные попытки и интервалы между ними» бесплатный?

Да — полный текст урока «Основы устойчивости: повторные попытки и интервалы между ними» бесплатно доступен здесь в веб-версии. Чтобы практиковать его интерактивно (встроенный редактор кода и ИИ-репетитор 24/7) и разблокировать остальной курс C# Academy, подпишись на CoddyKit PRO. Курс C# Academy содержит 3 уроков всего.

Чему я научусь в уроке «Основы устойчивости: повторные попытки и интервалы между ними»?

Реализуйте простые повторные попытки с экспоненциальным увеличением интервалов и случайным разбросом; отличайте временные ошибки от критических, ограничивайте задержки и прекращайте работу при отмене. Ты практикуешь C# Academy с помощью реального кода, который запускаешь прямо в браузере, и ИИ-репетитор 24/7 отвечает на твои вопросы во время урока.

Нужен ли мне опыт, чтобы начать C# Academy?

Предыдущий опыт не требуется. C# Academy на CoddyKit структурирован для всех уровней — от новичков до продвинутых, поэтому ты можешь начать отсюда или с самого начала и учиться в своем темпе. Это урок 3 из 3.

Сколько времени занимает урок «Основы устойчивости: повторные попытки и интервалы между ними»?

Большинство уроков CoddyKit занимают около 5–10 минут. Каждый из них компактный и интерактивный, поэтому ты постоянно делаешь прогресс и продолжаешь с того же места в веб-версии и приложении.

Можно ли писать и запускать код в этом уроке C# Academy?

Да. Каждый урок C# Academy включает встроенный редактор кода, поэтому ты пишешь и запускаешь реальный код прямо в браузере и получаешь моментальную обратную связь от AI — локальная установка не требуется.

Все уроки этого курса

  1. Шаблоны CancellationToken и кооперативная отмена
  2. Тайм-ауты, IProgress и асинхронные освобождаемые ресурсы (эмуляция)
  3. Основы устойчивости: повторные попытки и интервалы между ними
← Назад к C# Academy