← Назад к списку
Системный дизайнBackendSenior

Спроектируйте сервис уведомлений: email, push и SMS, миллионы отправок в день, ретраи, дедупликация, приоритеты.

Короткий ответ

  • Приём по API — только валидация и постановка в очередь
  • Очереди по каналам и приоритетам, воркеры — по провайдерам
  • Ретраи с экспоненциальной задержкой и dead-letter очередь
  • Идемпотентность по ключу запроса — защита от дублей
  • Шаблоны, предпочтения пользователя и rate limit на получателя
  • Статусы доставки — через вебхуки провайдеров в хранилище

Асинхронный конвейер: API ставит в очередь, воркеры каналов шлют через провайдеров с ретраями, идемпотентностью и DLQ.

Как сказать вслух

пример ответа

Делаю сервис асинхронным: API принимает запрос, валидирует, проверяет ключ идемпотентности и кладёт сообщение в очередь — отправитель сразу получает accepted. Дальше очереди разделены по каналам и приоритетам, чтобы миллионная маркетинговая рассылка не задержала код подтверждения. Воркеры каждого канала ходят к провайдерам — для email и SMS их лучше иметь два с переключением. Неудачи ретраю с нарастающей задержкой, после нескольких попыток сообщение уходит в dead-letter очередь на разбор. Статусы доставки принимаю вебхуками от провайдеров.

Подробный ответ

Основной ответ

Архитектура: API-слой (валидация, аутентификация сервисов-отправителей, идемпотентность по client_request_id, запись в хранилище статусов) → брокер (Kafka или RabbitMQ; отдельные топики/очереди на канал и класс приоритета: transactional против bulk) → воркеры каналов (рендер шаблона, проверка предпочтений и подписок пользователя, выбор провайдера, отправка). Надёжность: at-least-once от брокера плюс идемпотентность на отправке (дедуп по ключу в Redis/БД, чтобы ретрай конвейера не продублировал SMS); ретраи с exponential backoff и джиттером на временные ошибки провайдера, circuit breaker на провайдера с автопереключением на запасного; окончательные неудачи — в DLQ с алертом. Приоритеты изолируют каналы: у транзакционных сообщений свои очереди и свой пул воркеров. Ограничения: rate limit на получателя (анти-спам), квоты провайдеров, тихие часы. Статусная модель: accepted → queued → sent → delivered/failed, обновляется вебхуками провайдеров; хранится для API-запросов и аналитики.

Ключевые моменты

  • Изоляция приоритетов. Общий конвейер — классическая авария: миллион маркетинговых писем забивает очередь, и OTP-коды опаздывают. Отдельные очереди и воркеры обязательны.
  • Дубликаты страшнее задержки. Два одинаковых SMS о списании — инцидент. Идемпотентность нужна на входе (ключ клиента) и на выходе (дедуп перед провайдером).
  • Провайдеры ненадёжны. Таймауты, лимиты, аварии — норма: нужен circuit breaker, второй провайдер и различение временных и постоянных ошибок.
  • DLQ с процессом. Dead-letter очередь без владельца и алертов — кладбище; нужен разбор, метрики и инструмент повторной отправки.

Практический контекст

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

Частые ошибки

  • Отправляют синхронно из API-запроса, завязывая отправителя на задержки провайдера
  • Одна общая очередь без приоритетов — транзакционные сообщения ждут маркетинговые
  • Нет идемпотентности: ретрай конвейера дублирует SMS пользователю

ИП Кочкин Алексей Сергеевич · ИНН 390509026279 · ОГРНИП 325390000030973 · jiniys2005@yandex.ru