сеньорчикОткрыть в Telegram
← вся теориятеория к собесу · Интеграции

Очереди и асинхрон

Очереди и асинхрон

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

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

// Формулировки: «зачем очередь?», «что покажешь пользователю?», «как не потерять событие?»

Что даёт очередь и чего она стоит

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

Плата понятна: результат появляется не сразу. Значит, пользовательский сценарий обязан это отражать. «Заказ принят, оплата в обработке, статус обновится в течение минуты» - честно. «Заказ оплачен» до фактического подтверждения - обман, который вернётся письмом об отказе через полчаса.

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

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

Событие против команды, очередь против подписки

Событие сообщает свершившийся факт: «заказ оплачен». Отправителю безразлично, кто на это отреагирует и отреагирует ли вообще. Команда адресована конкретному исполнителю: «списать средства», и отправитель ждёт, что её выполнят.

Разница практическая: события снижают связанность. Новых потребителей факта «заказ оплачен» - аналитику, программу лояльности, уведомления - добавляют, вообще не трогая систему-источник. С командами так не получится, каждый новый адресат меняет отправителя.

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

// Выбор определяется одним вопросом: это работа или уведомление? Перепутав, получаешь либо десятикратно выполненную обработку, либо системы, которые так и не узнали о событии.

Порядок, разбор непрошедших и потерянные события

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

Сообщение, которое раз за разом не обрабатывается, убирают в отдельную очередь - её называют очередью недоставленных, DLQ (dead letter queue), - чтобы оно не блокировало весь поток. Но без регламента разбора она превращается в кладбище потерянных операций: никто не знает, кто туда смотрит, как часто и что делает с найденным.

Самый коварный случай темы - сервис записал заказ в базу и упал, не успев опубликовать событие. Или наоборот: опубликовал событие, а запись не прошла, и потребители получили сообщение о заказе, которого не существует. Атомарно обновить базу и брокер невозможно, это два разных хранилища.

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

очередь недоставленных
отдельная очередь для сообщений, которые не удалось обработать после нескольких попыток. Нужна, чтобы одно битое сообщение не останавливало весь поток

Как отвечать: «Оплата идёт через очередь. Что показать пользователю?»

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

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

На чём валятся

  • − Показывают успех до подтверждения асинхронной операции.
  • − Заводят очередь недоставленных без регламента разбора: она превращается в кладбище.
  • − Не задают срок хранения, число повторов и поведение при переполнении - работают умолчания брокера.
  • − Публикуют событие до записи в базу и получают событие о несуществующем заказе.
  • − Рассчитывают на порядок сообщений при нескольких параллельных потребителях.

Проверьте себя

Пять вопросов из банка по этой подтеме. Всего их 12, остальные разбираются в тренажёре.

  1. #ana_int_async1 / 5
    Чем событие отличается от команды в асинхронном обмене?
    A)Событие передаётся быстрее команды
    B)Событие — факт прошлого, команда — просьба действия
    C)Событие доставляется только одному получателю
    D)Команда не требует подтверждения обработки
    показать ответ и разбор
    +B)Событие — факт прошлого, команда — просьба действия

    // разбор: Событие сообщает, что уже случилось («заказ оплачен»), и отправителю безразлично, кто на него отреагирует. Команда адресована конкретному исполнителю и требует действия («списать средства»). Различие влияет на связанность: события позволяют добавлять потребителей, не трогая источник.

  2. #ana_int_async2 / 5
    Пользователь оформил заказ, а списание идёт через очередь. Что показать ему на экране?
    A)Успешную оплату сразу после отправки
    B)Пустой экран до завершения обработки
    C)Сообщение об ошибке при задержке
    D)Промежуточный статус обработки
    показать ответ и разбор
    +D)Промежуточный статус обработки

    // разбор: Асинхронность обязана быть отражена в интерфейсе: заказ принят, оплата в обработке, статус обновится. Обещать успех до подтверждения нельзя — операция может не пройти, и пользователь получит противоречие. Заодно нужно решить, что делать, если статус не изменился за разумное время.

  3. #ana_int_async3 / 5
    Чем отличается очередь от публикации-подписки?
    A)Подписка не поддерживает подтверждения
    B)Подписка работает только внутри одного сервиса
    C)Очередь не сохраняет сообщения на диск
    D)В очереди сообщение получает один потребитель
    показать ответ и разбор
    +D)В очереди сообщение получает один потребитель

    // разбор: Модель очереди распределяет сообщения между потребителями: каждое достаётся одному, что удобно для балансировки работы. Публикация-подписка доставляет копию каждому подписчику, что удобно для оповещения нескольких независимых систем об одном факте. Выбор определяется тем, работа это или уведомление.

  4. #ana_int_async4 / 5
    Почему в асинхронной интеграции важен порядок сообщений и когда он ломается?
    A)Порядок сохраняется брокером
    B)Порядок теряется при нескольких потребителях
    C)Порядок нарушается только при сбое сети
    D)Порядок не имеет значения для бизнес-логики
    показать ответ и разбор
    +B)Порядок теряется при нескольких потребителях

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

  5. #ana_int_async5 / 5
    Что описать в требованиях, вводя интеграцию через брокер?
    A)Марку брокера и версию сервера
    B)Число разработчиков на задаче
    C)Структуру таблиц получателя
    D)Ретенцию, повторы, переполнение
    показать ответ и разбор
    +D)Ретенцию, повторы, переполнение

    // разбор: Именно эти параметры определяют, что случится в плохой день: сколько хранится неразобранное, сколько раз повторяем, куда девается сообщение после исчерпания попыток, что делаем при заполнении хранилища. Без явных ответов система ведёт себя по умолчанию, а умолчания в брокерах редко совпадают с ожиданиями бизнеса.

дальше

Теорию прочитали. Навык ставится повторением

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