Очереди и асинхрон
Тема мидлового уровня: её спрашивают, когда хотят понять, проектировал ли ты что-то сложнее синхронного вызова. И ключевой вопрос почти всегда не про брокер, а про то, что в этот момент видит пользователь на экране.
Стержень: очередь развязывает стороны во времени, но платой становится отложенная согласованность, которую обязан отразить интерфейс. Техническое решение здесь напрямую превращается в требование к экрану.
// Формулировки: «зачем очередь?», «что покажешь пользователю?», «как не потерять событие?»
Что даёт очередь и чего она стоит
Брокер - это посредник, который принимает сообщение и хранит его, пока получатель не заберёт. Отправитель отдал сообщение и пошёл дальше, даже если приёмник в этот момент лежит на профилактике. В этом и состоит развязка: две системы больше не обязаны быть живыми одновременно.
Плата понятна: результат появляется не сразу. Значит, пользовательский сценарий обязан это отражать. «Заказ принят, оплата в обработке, статус обновится в течение минуты» - честно. «Заказ оплачен» до фактического подтверждения - обман, который вернётся письмом об отказе через полчаса.
И сразу решается второй вопрос, о котором забывают: что показать, если статус не изменился за разумное время. Без ответа заявка зависает навсегда, пользователь звонит в поддержку, поддержка идёт к разработчикам, разработчики руками правят статус в базе.
// Полезно проговорить это с бизнесом до реализации, а не после: сколько ждём, что показываем по истечении срока, повторяем автоматически или отменяем резерв. Это продуктовое решение, а не техническое.
Событие против команды, очередь против подписки
Событие сообщает свершившийся факт: «заказ оплачен». Отправителю безразлично, кто на это отреагирует и отреагирует ли вообще. Команда адресована конкретному исполнителю: «списать средства», и отправитель ждёт, что её выполнят.
Разница практическая: события снижают связанность. Новых потребителей факта «заказ оплачен» - аналитику, программу лояльности, уведомления - добавляют, вообще не трогая систему-источник. С командами так не получится, каждый новый адресат меняет отправителя.
Второе разделение - про способ доставки. Модель очереди отдаёт сообщение одному из конкурирующих потребителей: это распределение работы, десять обработчиков разбирают поток быстрее одного. Публикация-подписка доставляет копию каждому подписчику: это оповещение нескольких систем об одном факте.
// Выбор определяется одним вопросом: это работа или уведомление? Перепутав, получаешь либо десятикратно выполненную обработку, либо системы, которые так и не узнали о событии.
Порядок, разбор непрошедших и потерянные события
При нескольких параллельных потребителях порядок сообщений не гарантирован: отмена заказа может обогнать его создание, и обработчик получит отмену того, чего ещё нет. Лечится двумя способами - ключом партиционирования, который гонит все сообщения по одной сущности в один поток, либо обработчиком, устойчивым к любому порядку.
Сообщение, которое раз за разом не обрабатывается, убирают в отдельную очередь - её называют очередью недоставленных, DLQ (dead letter queue), - чтобы оно не блокировало весь поток. Но без регламента разбора она превращается в кладбище потерянных операций: никто не знает, кто туда смотрит, как часто и что делает с найденным.
Самый коварный случай темы - сервис записал заказ в базу и упал, не успев опубликовать событие. Или наоборот: опубликовал событие, а запись не прошла, и потребители получили сообщение о заказе, которого не существует. Атомарно обновить базу и брокер невозможно, это два разных хранилища.
// Решение называется исходящей таблицей: событие записывают в обычную таблицу той же транзакцией, что и сам заказ, - либо оба записались, либо ни одного. А отдельный фоновый процесс потом читает эту таблицу и публикует события в брокер, повторяя попытки до успеха.
- очередь недоставленных
- отдельная очередь для сообщений, которые не удалось обработать после нескольких попыток. Нужна, чтобы одно битое сообщение не останавливало весь поток
Как отвечать: «Оплата идёт через очередь. Что показать пользователю?»
Промежуточный статус и явное обещание, когда он обновится: заказ принят, оплата в обработке. Показывать успех до фактического подтверждения нельзя - операция может не пройти, и человек получит противоречие: сначала «оплачено», потом письмо об отказе, и дальше он идёт в поддержку с справедливым вопросом. Пустой экран тоже не годится, он читается как зависание и заставляет обновлять страницу. Отдельно проговариваю с бизнесом, что делать, если статус не изменился за оговорённое время: показать пояснение с контактом поддержки, повторить автоматически или отменить резерв. Без этого решения заявки зависают навсегда и разбирать их приходится руками через базу.
Кандидат связывает техническую асинхронность с пользовательским сценарием и закрывает случай зависания, который в реальных проектах и создаёт основную нагрузку на поддержку.
На чём валятся
- −− Показывают успех до подтверждения асинхронной операции.
- −− Заводят очередь недоставленных без регламента разбора: она превращается в кладбище.
- −− Не задают срок хранения, число повторов и поведение при переполнении - работают умолчания брокера.
- −− Публикуют событие до записи в базу и получают событие о несуществующем заказе.
- −− Рассчитывают на порядок сообщений при нескольких параллельных потребителях.
Проверьте себя
Пять вопросов из банка по этой подтеме. Всего их 12, остальные разбираются в тренажёре.
- Чем событие отличается от команды в асинхронном обмене?A)Событие передаётся быстрее командыB)Событие — факт прошлого, команда — просьба действияC)Событие доставляется только одному получателюD)Команда не требует подтверждения обработки
показать ответ и разбор
+B)Событие — факт прошлого, команда — просьба действия// разбор: Событие сообщает, что уже случилось («заказ оплачен»), и отправителю безразлично, кто на него отреагирует. Команда адресована конкретному исполнителю и требует действия («списать средства»). Различие влияет на связанность: события позволяют добавлять потребителей, не трогая источник.
- Пользователь оформил заказ, а списание идёт через очередь. Что показать ему на экране?A)Успешную оплату сразу после отправкиB)Пустой экран до завершения обработкиC)Сообщение об ошибке при задержкеD)Промежуточный статус обработки
показать ответ и разбор
+D)Промежуточный статус обработки// разбор: Асинхронность обязана быть отражена в интерфейсе: заказ принят, оплата в обработке, статус обновится. Обещать успех до подтверждения нельзя — операция может не пройти, и пользователь получит противоречие. Заодно нужно решить, что делать, если статус не изменился за разумное время.
- Чем отличается очередь от публикации-подписки?A)Подписка не поддерживает подтвержденияB)Подписка работает только внутри одного сервисаC)Очередь не сохраняет сообщения на дискD)В очереди сообщение получает один потребитель
показать ответ и разбор
+D)В очереди сообщение получает один потребитель// разбор: Модель очереди распределяет сообщения между потребителями: каждое достаётся одному, что удобно для балансировки работы. Публикация-подписка доставляет копию каждому подписчику, что удобно для оповещения нескольких независимых систем об одном факте. Выбор определяется тем, работа это или уведомление.
- Почему в асинхронной интеграции важен порядок сообщений и когда он ломается?A)Порядок сохраняется брокеромB)Порядок теряется при нескольких потребителяхC)Порядок нарушается только при сбое сетиD)Порядок не имеет значения для бизнес-логики
показать ответ и разбор
+B)Порядок теряется при нескольких потребителях// разбор: При параллельной обработке несколькими экземплярами сообщения по одному объекту могут разъехаться: отмена обгонит создание. Обычное решение — ключ партиционирования, гарантирующий, что события одной сущности идут в один поток. Второе — писать обработчики так, чтобы порядок не имел значения.
- Что описать в требованиях, вводя интеграцию через брокер?A)Марку брокера и версию сервераB)Число разработчиков на задачеC)Структуру таблиц получателяD)Ретенцию, повторы, переполнение
показать ответ и разбор
+D)Ретенцию, повторы, переполнение// разбор: Именно эти параметры определяют, что случится в плохой день: сколько хранится неразобранное, сколько раз повторяем, куда девается сообщение после исчерпания попыток, что делаем при заполнении хранилища. Без явных ответов система ведёт себя по умолчанию, а умолчания в брокерах редко совпадают с ожиданиями бизнеса.
дальше
Теорию прочитали. Навык ставится повторением
В Сеньорчике эта подтема идёт в ежедневных сессиях: движок возвращает её, пока ответы не станут уверенными, и ведёт прогресс отдельно по каждой подтеме. Теория внутри тоже бесплатна, лимит только на количество вопросов в день.