31 августа, 2026

Повторные запросы к SMS API: таймауты, backoff и безопасные ретраи

1 мин
Время чтения

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

Эта тема уже не про выбор и подключение SMS API, а про поведение готовой интеграции при неопределённом результате. Главная предпосылка ретраев — идемпотентность SMS API: один логический запрос должен сохранять один ключ во всех попытках.

Почему таймаут не равен отказу

Таймаут сообщает только то, что клиент не получил ответ вовремя. Он не доказывает, что провайдер не принял сообщение. Запрос мог завершиться на стороне шлюза, а ответ потеряться по дороге. Именно поэтому без идемпотентного ключа автоматический повтор POST-запроса неоднозначен.

Нужно различать три результата:

  • точный успех — API вернул идентификатор сообщения;
  • точный отказ — API отклонил запрос из-за формата, авторизации или другого постоянного условия;
  • неопределённость — соединение оборвалось или истёк таймаут, и судьба запроса неизвестна.

Для неопределённости сначала полезно проверить операцию по request_id или client_message_id, если API даёт такой метод. Только затем принимать решение о повторе.

Какие ответы повторять

Повтор обычно оправдан при сетевой ошибке, временной недоступности и части ответов 5xx. Ответ 429 означает ограничение частоты: клиент должен снизить нагрузку и учитывать Retry-After, если сервер его передал.

Большинство 4xx повторять без изменения запроса бессмысленно. Неверный номер, отсутствующий обязательный параметр, неподдерживаемый Sender ID или неправильный API-ключ не исправятся от второй попытки. Такие события нужно отправить в отдельную очередь ошибок и показать ответственному человеку.

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

Backoff и jitter

Если сотни задач повторятся через одинаковую секунду, они создадут новый пик ровно в момент восстановления сервиса. Exponential backoff увеличивает паузу между попытками, а jitter добавляет случайное смещение и разводит запросы по времени.

Пример разумной последовательности — около 1, 2, 4, 8 и 16 секунд с небольшим случайным отклонением, но конкретные значения зависят от назначения сообщения и ограничений API. Важен не набор цифр, а свойства политики:

  • задержка растёт после каждой неудачи;
  • есть верхняя граница паузы;
  • есть максимальное число попыток или общий дедлайн;
  • разные задачи не повторяются синхронной волной;
  • уважена серверная подсказка Retry-After.

AWS рекомендует ограничивать ретраи, применять exponential backoff с jitter и сначала проверять идемпотентность операции — это универсальный принцип распределённых систем, применимый и к SMS-интеграции (документация AWS).

Дедлайн важнее числа попыток

У разных SMS разная ценность во времени. Код входа, который доставится через двадцать минут, уже бесполезен. Напоминание о записи через час после приёма тоже не нужно. Поэтому политика должна учитывать бизнес-дедлайн, а не только число технических попыток.

Для каждой задачи полезно хранить expires_at. Перед очередным вызовом воркер проверяет, остаётся ли сообщение актуальным. Если срок прошёл, задача завершается со статусом «просрочено», а не отправляется любой ценой. Техническая настройка срока доставки рассмотрена отдельно в статье про TTL для SMS.

Один уровень повторов

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

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

Каждая попытка получает новый attempt_id, но сохраняет общий idempotency_key и correlation_id. Так в логах видно историю, а провайдер понимает, что это одна операция.

Circuit breaker и ограничение потока

Если провайдер массово отвечает ошибкой, бессмысленно продолжать поток с прежней скоростью. Circuit breaker временно прекращает новые вызовы после заданного порога сбоев. Через короткий интервал несколько контрольных запросов проверяют восстановление, после чего поток открывается постепенно.

Это защищает и внешнюю сторону, и ваши ресурсы. Без ограничителя воркеры заняты обречёнными запросами, очередь растёт, а важные уведомления оказываются за тысячами повторов. Для разделения потоков пригодятся приоритеты SMS-трафика.

Что делать после исчерпания попыток

Задачу нельзя просто удалить. Её переводят в dead-letter queue или устойчивое состояние ошибки вместе с причиной, числом попыток и идентификаторами. Дальше возможны три действия: ручной повтор после устранения сбоя, резервный маршрут либо окончательная отмена, если сообщение устарело.

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

Наблюдаемость ретраев

Отдельно считайте долю запросов, дошедших с первой попытки, распределение числа повторов, причины ретрая, возраст очереди и количество задач в dead-letter queue. Рост успешных повторов может выглядеть как стабильность, хотя на самом деле первый вызов систематически ломается.

Полезные алерты:

  • доля ретраев выше обычного уровня;
  • серия 429 или 5xx;
  • очередь стареет быстрее, чем обрабатывается;
  • сообщения достигают бизнес-дедлайна;
  • открылась защита circuit breaker;
  • появились конфликты idempotency key.

Тестовый сценарий

Перед продом искусственно смоделируйте таймаут после фактического приёма запроса, два параллельных воркера, 429 с Retry-After, серию 503 и восстановление после паузы. Проверьте не только количество вызовов API, но и количество реально созданных сообщений.

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

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

Итог

Безопасный retry — это не бесконечный цикл вокруг HTTP-запроса. Он объединяет классификацию ошибок, идемпотентный ключ, backoff с jitter, бизнес-дедлайн, ограничение потока и понятное финальное состояние. Такая схема восстанавливает временные сбои, не превращая их в лавину запросов и повторных SMS.

Обновлено 31 августа, 2026
Подходит каждому
SMS-рассылка для любой сферы бизнеса
Узнайте как внедрить SMS-рассылку в ваш бизнес вместе с quicktel
Оставьте номер и вам перезвонит менеджер.