> Что происходит при передаче данных между сервисами с брокером сообщений если сервис недоступен (Go)
Уровень: senior · Роль: backend · Язык: Go · Категория: Технические вопросы
Компании: Wildberries
Стек: Go
> Пример ответа
Короткий ответ
Если сервис-потребитель недоступен, сообщение остаётся в очереди брокера до истечения TTL или до повторного подключения. Брокер гарантирует at-least-once delivery: сообщение не теряется, но возможны дубликаты. Если недоступен продюсер - сообщения просто не публикуются, очередь деградирует по данным. Ключевой момент: брокер не удаляет сообщения при недоступности консьюмера, если не настроены политики dead letter queue или TTL. В Go это особенно важно из-за конкурентной модели обработки.
Подробное объяснение
При передаче через брокер (RabbitMQ, Kafka, NATS) недоступность сервиса влияет по-разному в зависимости от роли:
Недоступен консьюмер - сообщения накапливаются в очереди. Брокер продолжает принимать от продюсера, но не может доставить. Поведение зависит от настроек:
- Без TTL - сообщения хранятся бесконечно (в RabbitMQ - до перезапуска брокера, в Kafka - до истечения retention)
- С TTL - сообщения удаляются после истечения срока
- С DLQ - после N неудачных попыток сообщение перемещается в dead letter queue
- В Kafka consumer group - партиции продолжают хранить данные, offset не двигается
Недоступен продюсер - очередь пустеет, консьюмеры либо блокируются на чтении, либо получают пустые ответы. В Kafka это не проблема - данные уже в логе.
Критический аспект - подтверждения (ack). Если консьюмер получил сообщение, но упал до ack, брокер вернёт сообщение в очередь (requeue). Это гарантирует доставку, но создаёт риск дубликатов при идемпотентной обработке.
В Go важно учитывать, что стандартные клиенты (например, github.com/rabbitmq/amqp091-go) работают в горутинах. Если консьюмер падает, соединение рвётся, и брокер переустанавливает канал. Нужна обработка ErrClosed и реконнект с экспоненциальным backoff.
На практике
Типичный сценарий для senior - проектирование отказоустойчивости:
- Настроить retry с backoff на стороне консьюмера
- Использовать DLQ для сообщений, которые не удалось обработать после N попыток
- Для критичных данных - не использовать TTL, а полагаться на ручную обработку DLQ
- В Kafka - настроить
enable.auto.commit=falseи коммитить offset только после успешной обработки - Мониторить глубину очереди (queue depth) как ключевой метрику
Пример из практики: сервис оплаты шлёт событие в очередь, сервис уведомлений недоступен. Если не настроен DLQ - через 3 дня сообщение уйдёт в DLQ, и его нужно обработать вручную или повторно запушить. Если очередь без TTL - она растёт, и при восстановлении сервиса он получит лавину старых сообщений.
Пример кода
GO// Консьюмер с реконнектом и retry в Gofunc consume(ctx context.Context, conn *amqp.Connection, queue string) error {for {ch, err := conn.Channel()if err != nil {return err}msgs, err := ch.Consume(queue, "", false, false, false, false, nil)if err != nil {return err}for {select {case <-ctx.Done():return nilcase msg, ok := <-msgs:if !ok {// канал закрыт - реконнектch.Close()time.Sleep(5 * time.Second)goto reconnect}if err := processMessage(msg.Body); err != nil {// retry с backoff, потом в DLQif retries := getRetries(msg); retries < 3 {msg.Nack(false, true) // requeue} else {msg.Nack(false, false) // в DLQ}continue}msg.Ack(false)}}reconnect:}}
Как отвечать на собеседовании
Начни с чёткого разделения ролей: продюсер и консьюмер. Скажи, что брокер - это буфер, и его основная задача - не потерять данные. Затем переходи к деталям: ack, requeue, DLQ, TTL. Для senior важно показать понимание trade-off между гарантиями доставки и производительностью.
Упомяни, что в Kafka модель отличается от RabbitMQ: там нет очереди в классическом смысле, а есть лог с offset. Если консьюмер недоступен - offset не двигается, и при восстановлении он прочитает все сообщения с последнего коммита.
Хорошо добавить про идемпотентность: при at-least-once доставке дубликаты неизбежны, поэтому обработчик должен быть идемпотентным. Это покажет глубину понимания.
Что проверяет интервьюер
- Понимание гарантий доставки (at-most-once, at-least-once, exactly-once)
- Знание механик брокеров: ack, requeue, DLQ, TTL
- Умение проектировать отказоустойчивые системы
- Понимание разницы между RabbitMQ и Kafka
- Навыки работы с конкурентностью в Go (горутины, каналы, контексты)
Типичные ошибки
- Путаница между requeue и DLQ: requeue возвращает в ту же очередь, DLQ - в отдельную
- Утверждение, что сообщение теряется при недоступности консьюмера - это неверно для большинства брокеров
- Игнорирование проблемы дубликатов: при at-least-once они гарантированы
- Отсутствие обработки закрытия канала в Go - без реконнекта консьюмер молча умирает
- Непонимание, что TTL - это потеря данных, а не решение проблемы недоступности
- Слишком долгий retry без backoff - при недоступности сервиса это создаст нагрузку на брокер
> Похожие задачи по Go
В каких случаях лучше использовать брокер сообщений для взаимодействия между сервисами
Как называется операция переноса данных из нескольких таблиц в одну для оптимизации запросов
Как передать данные от одного сервиса к множеству читателей с брокером сообщений
Что такое consumer group в брокерах сообщений
> Похожие задачи по backend
В каких случаях лучше использовать брокер сообщений для взаимодействия между сервисами
Как называется операция переноса данных из нескольких таблиц в одну для оптимизации запросов
Как передать данные от одного сервиса к множеству читателей с брокером сообщений
Что такое consumer group в брокерах сообщений
> ГОТОВЫ К СЛЕДУЮЩЕМУ СОБЕСЕДОВАНИЮ?
Запустите тренировочную сессию с ИИ и получите детальную обратную связь, чтобы увереннее проходить реальные интервью