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

Что такое экспоненциальный откат?

Экспоненциальная отсрочка — это метод, который позволяет приложению повторить неудачную операцию с постепенно увеличивающимся временем ожидания между повторными попытками. С каждым сбоем приложение экспоненциально увеличивает время ожидания, позволяя системе восстановиться после любых временных сбоев, которые могут вызвать проблему. Такой подход гарантирует, что приложение не переполнит систему повторными попытками и потенциально не усугубит проблему.

Пример экспоненциального отката

Предположим, у вас есть сценарий, который выполняет вызов API к службе, которая может время от времени возвращать ошибку из-за проблем с сетевым подключением или регулирования. Вы хотите реализовать стратегию повторных попыток, которая повторяет вызов API до 5 раз с увеличением времени задержки.

Вот как реализовать стратегию экспоненциального отката:

Как видите, время задержки начинается с базового значения 1,0 секунды и удваивается с каждой повторной попыткой. Это дает сервису достаточно времени для восстановления после ошибки и уменьшает количество повторных попыток.

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

Что такое экспоненциальный откат с джиттером?

Экспоненциальная отсрочка с дрожанием — это метод, используемый для повторного выполнения неудачных операций в распределенных системах. Он включает в себя постепенное увеличение задержки между повторными попытками, начиная с малого и экспоненциально увеличиваясь, пока не будет достигнута максимальная задержка. Такой подход снижает нагрузку на систему и предотвращает чрезмерное количество повторных попыток.

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

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

Пример экспоненциального отката с джиттером

С экспоненциальным отставанием с дрожанием вы добавляете некоторую случайность ко времени задержки, вводя случайную задержку или «дрожание» во время задержки следующего повтора. Это гарантирует, что повторные попытки не будут синхронными, и снижает вероятность шторма повторных попыток. Вот как будет выглядеть таблица с экспоненциальным отставанием с Jitter:

Как видите, фактическое время задержки немного отличается из-за привнесенного джиттера. Это снижает вероятность одновременных повторных попыток и предотвращает перегрузку службы.

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

Внедрение экспоненциального отката с дрожанием в сервисе AWS S3

Допустим, у вас есть скрипт Python, который загружает файл в корзину S3 с помощью библиотеки Boto3. Иногда загрузка может завершиться ошибкой из-за проблем с сетью или других временных сбоев. Вы хотите реализовать экспоненциальную отсрочку с дрожанием, чтобы повторить загрузку в случае сбоя.

Вот как можно изменить скрипт для реализации экспоненциальной отсрочки с Jitter:

import boto3
import random
import time

# Create an S3 client
s3 = boto3.client('s3')

# Define the upload function with Exponential Backoff and Jitter
def upload_file_with_retry(bucket, key, filename, max_retries=5, base_delay=1.0, max_delay=60.0, jitter_range=0.5):
    retry_count = 0
    delay = base_delay
    while True:
        try:
            print(f'Uploading {filename} to s3://{bucket}/{key}')
            s3.upload_file(filename, bucket, key)
            print(f'Upload complete!')
            return True
        except Exception as e:
            print(f'Upload failed: {str(e)}')
            if retry_count >= max_retries:
                print(f'Retries exhausted. Aborting upload.')
                return False
            else:
                jitter = random.uniform(-jitter_range, jitter_range)
                delay *= 2
                delay = min(delay + jitter, max_delay)
                print(f'Retrying in {delay} seconds...')
                time.sleep(delay)
                retry_count += 1

# Call the upload function with retry
upload_file_with_retry('my-bucket', 'my-file.txt', '/path/to/my-file.txt')

Вот как работает модифицированный скрипт:

  • Функция upload_file_with_retry принимает имя корзины S3, ключ (путь к файлу), локальное имя файла и дополнительные параметры для повторных попыток.
  • Функция имеет цикл while, который продолжает загрузку файла до тех пор, пока не будет достигнуто максимальное количество повторных попыток.
  • Если попытка загрузки не удалась, функция выводит сообщение об ошибке и вычисляет следующую задержку повторной попытки. Задержка начинается с базовой задержки (1,0 секунды в этом примере) и удваивается с каждой повторной попыткой, пока не достигнет максимальной задержки (60,0 секунд в этом примере).
  • Чтобы ввести дрожание, функция генерирует случайное число с плавающей запятой между отрицательным и положительным диапазоном дрожания (0,5 секунды в этом примере) и добавляет его к следующей задержке повторной попытки. Это гарантирует, что фактическая задержка будет немного отличаться, и уменьшит вероятность одновременных повторных попыток.
  • Функция приостанавливается на рассчитанную задержку перед следующей повторной попыткой.

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

Кроме того, реализация экспоненциальной отсрочки с Jitter также может предотвратить занесение вашего приложения в черный список службой из-за чрезмерных повторных попыток. Многие сервисы имеют механизмы ограничения скорости для предотвращения злоупотреблений, и слишком частые и слишком быстрые повторные попытки могут привести к срабатыванию этих механизмов и временной блокировке вашего IP-адреса или учетной записи.

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

Хотя существует множество способов реализации экспоненциальной отсрочки, существует также множество доступных пакетов, которые могут упростить процесс для разработчиков. Например, библиотека Python retrying предоставляет простой и удобный интерфейс для реализации поведения повторных попыток с настраиваемыми параметрами отсрочки. Точно так же пакет exponential-backoff для Node.js предлагает аналогичный интерфейс для разработчиков JavaScript, а .NET имеет Polly. Используя эти пакеты, разработчики могут легко реализовать экспоненциальную отсрочку в своих приложениях, не беспокоясь о деталях алгоритма.

Таким образом, Exponential Backoff with Jitter — это мощный инструмент для повышения надежности и отказоустойчивости вашего приложения при вызовах API или взаимодействии с внешними службами по сети. Вводя случайность во время задержки между повторными попытками, вы можете избежать синхронных повторных попыток. Это уменьшит вероятность перегрузки службы или срабатывания механизмов ограничения скорости. И, постепенно увеличивая время задержки с каждой повторной попыткой, вы можете дать службе достаточно времени для восстановления после временных ошибок. Это повысит шансы на успех в долгосрочной перспективе.