Введение
В разработке программного обеспечения надежная логика повторных попыток важна для обработки временных сбоев, таких как проблемы с сетью или временные отключения. Недавно я наткнулся на кодовую базу, где разработчик использовал цикл for
с фиксированным интервалом времени для повторных попыток неудачных операций. Хотя такой подход может показаться простым, ему не хватает устойчивости, необходимой для реальных приложений. Здесь на помощь приходит экспоненциальная задержка — стратегия, разработанная для того, чтобы сделать повторные попытки более умными и эффективными.
В этой статье мы рассмотрим, как работает экспоненциальная задержка, ее преимущества по сравнению с базовым циклом повторных попыток и как вы можете внедрить ее для повышения надежности вашей системы. Я также проведу вас через практический пример, используя модуль отправки электронной почты, показывая, как использовать экспоненциальную задержку для обеспечения более устойчивой обработки ошибок.
Что такое экспоненциальная задержка?
Экспоненциальная задержка — это стратегия повторных попыток, при которой время ожидания между попытками увеличивается экспоненциально после каждого сбоя. Вместо повторных попыток с фиксированными интервалами каждая последующая попытка ждет дольше, чем предыдущая — обычно время задержки удваивается каждый раз. Например, если начальная задержка составляет 1 секунду, следующие попытки будут происходить через 2, 4, 8 секунд и так далее. Такой подход помогает снизить нагрузку на систему и минимизировать риск перегрузки внешних служб в периоды высокого спроса.
Предоставляя больше времени между повторными попытками, экспоненциальная задержка дает временным проблемам шанс на разрешение, что приводит к более эффективной обработке ошибок и улучшению стабильности приложения.
Плюсы и минусы экспоненциальной задержки
Плюсы:
-
Сниженная нагрузка на систему: Разделяя повторные попытки, экспоненциальная задержка минимизирует вероятность перегрузки серверов, что особенно полезно для обработки ограничений по количеству запросов или временных сбоев.
-
Эффективная обработка ошибок: Увеличивающаяся задержка предоставляет временные проблемы больше времени для естественного разрешения, повышая вероятность успешной повторной попытки.
-
Улучшенная стабильность: Особенно для систем с высоким трафиком, это предотвращает поток повторных попыток, позволяя приложениям работать без перебоев и без чрезмерного потребления ресурсов.
Недостатки:
- Увеличенная задержка: Каждая повторная попытка занимает все больше времени, поэтому экспоненциальная задержка может привести к задержкам, особенно если требуется много повторных попыток перед успехом.
Ключевые случаи использования экспоненциальной задержки
Экспоненциальная задержка особенно полезна в сценариях, где системы взаимодействуют с внешними сервисами или обрабатывают большие объемы трафика. Вот некоторые другие распространенные случаи использования:
-
API с ограничением по количеству запросов: Некоторые API имеют ограничения по количеству запросов, ограничивая их в течение определенного времени. Экспоненциальный откат помогает избежать немедленных повторных попыток, которые могут превысить лимит, давая время для его сброса.
-
Нестабильность сети: В случаях временных сбоев сети или тайм-аутов экспоненциальный откат помогает, ожидая дольше между попытками, позволяя сети стабилизироваться.
-
Подключения к базам данных: При подключении к базам данных под высокой нагрузкой экспоненциальный откат помогает предотвратить дальнейшую перегрузку, задерживая повторные попытки и давая базе данных время для восстановления.
-
Системы очередей: В системах очередей сообщений, если сообщение не удается обработать из-за ошибки, использование экспоненциального отката для повторных попыток может предотвратить быстрое переработку и дать время для разрешения временных проблем.
Создание базового сервиса отправки электронной почты с экспоненциальным откатом
Чтобы продемонстрировать экспоненциальный откат, мы создадим базовый сервис отправки электронной почты, который будет повторно пытаться отправить письма в случае возникновения ошибки. Этот пример показывает, как экспоненциальный откат улучшает процесс повторных попыток по сравнению с простым циклом for.
import nodemailer from "nodemailer";
import { config } from "../common/config";
import SMTPTransport from "nodemailer/lib/smtp-transport";
const emailSender = async (
subject: string,
recipient: string,
body: string
): Promise<boolean> => {
const transport = nodemailer.createTransport({
host: config.EMAIL_HOST,
port: config.EMAIL_PORT,
secure: true,
auth: { user: config.EMAIL_SENDER, pass: config.EMAIL_PASSWORD },
} as SMTPTransport.Options);
const mailOptions: any = {
from: config.EMAIL_SENDER,
to: recipient,
subject: subject,
};
const maxRetries = 5; // maximum number of retries before giving up
let retryCount = 0;
let delay = 1000; // initial delay of 1 second
while (retryCount < maxRetries) {
try {
// send email
await transport.sendMail(mailOptions);
return true;
} catch (error) {
// Exponential backoff strategy
retryCount++;
if (retryCount < maxRetries) {
const jitter = Math.random() * 1000; // random jitter(in seconds) to prevent thundering herd problem
const delayMultiplier = 2
const backOffDelay = delay * delayMultiplier ** retryCount + jitter;
await new Promise((resolve) => setTimeout(resolve, backOffDelay));
} else {
// Log error
console.log(error)
return false; // maximum number of retries reached
}
}
}
return false;
};
Настройка параметров экспоненциального отката
Внедрение экспоненциального отката предполагает настройку определенных параметров, чтобы убедиться, что стратегия повторных попыток хорошо соответствует потребностям вашего приложения. Следующие ключевые параметры влияют на поведение и производительность экспоненциального отката в механизме повторных попыток:
- Начальная задержка
-
Цель: Устанавливает время ожидания перед первой повторной попыткой. Оно должно быть достаточно длинным, чтобы предотвратить немедленные повторные попытки, но достаточно коротким, чтобы избежать заметных задержек.
-
Рекомендуемая настройка: Начните с задержки от 500 мс до 1000 мс. Для критических систем используйте более короткую задержку, в то время как менее срочные операции могут иметь более длительную задержку.
- Множитель задержки
-
Назначение: Управляет тем, как быстро увеличивается задержка после каждой попытки повторения. Множитель 2 удваивает задержку (например, 1с, 2с, 4с).
-
Рекомендуемая настройка: Обычно множитель в диапазоне 1.5 и 2 обеспечивает баланс между отзывчивостью и стабильностью. Более высокие множители (например, 3) могут быть подходящими, если система может выдерживать более длительные задержки между попытками повторения.
- Максимальное количество попыток
-
Назначение: Ограничивает количество попыток повторения, чтобы предотвратить чрезмерные попытки, которые могут истощить ресурсы или увеличить нагрузку на систему.
-
Рекомендуемая настройка: Обычно достаточно диапазона 3 до 5 попыток для большинства приложений. За пределами этого операция может быть зарегистрирована как неудачная или управляться иначе, например, уведомлением пользователя или срабатыванием оповещения.
- Джиттер (рандомизация)
- Цель: Добавляет случайность к каждому задержке, чтобы предотвратить сгруппирование повторных попыток и избежать эффекта «громадного стада».
- Рекомендуемая настройка: Добавьте случайную задержку между 0 и 500 мс к каждому интервалу повторной попытки. Этот джиттер помогает более равномерно распределить попытки повторения во времени.
Заключение
Используя экспоненциальный откат, вы добавляете устойчивость вашему приложению, подготавливая его к неожиданным проблемам. Это небольшое изменение с большим эффектом, особенно по мере роста вашего приложения.
На этом пока все, ребята. Не стесняйтесь оставлять комментарии и задавать вопросы, если они у вас есть. Cheers к созданию более надежных и устойчивых приложений.
Счастливого кодирования! 👨💻❤️
Source:
https://timothy.hashnode.dev/implementing-exponential-backoff-for-reliable-systems