Как организовать гарантированную отправку сообщений в хореографии — паттерн Outbox
При реализации межсервисного взаимодействия с использованием хореографии часто возникает вопрос — как гарантировать доставку сообщений из одной системы (сервиса) в другую?
Предположим такую ситуацию:
Сервис обрабатывает входящий запрос или сообщение, потом сохраняет или обновляет данные в БД и отправляет сообщение. Если возник сбой на каком-то этапе, то могут произойти следующие ситуации:
- Данные сохранены, транзакция закрыта, сообщение отправлено, возник сбой при закрытии соединения или еще что-то — в этом случае мы не теряем ни сообщение, ни данные.
- Возникла ошибка при сохранении данных, сообщение не отправилось — данные в нашей системе остаются согласованными.
- Данные сохранены, транзакция закрылась, сообщение не отправилось, возник сбой, в нашей системе образуются несогласованные данные, и мы не сразу это поймём.
- Данные сохранены, сообщение отправлено, но потом мы закрываем транзакцию и возникает ошибка, сохранение в БД откатывается — опять несогласованные данные.
Чтобы избежать несогласованности данных при обработке и последующей отправке сообщений, есть несколько решений, и одно из них — паттерн Outbox.
Суть паттерна Outbox
Это элегантное, простое в понимании и одновременно громоздкое в реализации решение.
При обработке данных и сохранении/обновлении сущности в БД сервис или компонент одновременно с этим сохраняет запись события в таблицу outbox. Две эти записи происходят в рамках одной транзакции. Следующий шаг — отправка сообщения в Event Bus. Служба по расписанию или по реакции на изменения в БД отправляет сообщение.
Есть несколько моделей реализации служб отправки сообщения:
- Poll-модель — отдельный процесс по расписанию проверяет таблицу outbox и отсылает новые сообщения в Event Bus. Недостаток данного подхода — запаздывание отправки сообщения.
- Change Data Capture — определенный процесс слушает изменения в таблице outbox и при появлении новой записи тут же отправляет ее в Event Bus. Существует множество решений, которые могут реагировать на изменения в данных таблицы, можно реализовать, читая изменения в логе транзакций, либо использовать готовое решение, такое как Debezium. Из очевидных минусов — необходимо либо реализовывать самим, либо подключать еще одно стороннее решение.
Ограничения Outbox
Из самого определения паттерна вытекают и его ограничния:
- Задержка доставки сообщения — даже при использовании CDC модели отправка сообщений происходит со значительной задержкой по сравнению с прямой отправкой.
- Увеличение нагрузки на БД — помимо записи изменений в доменной логике требуется запись в таблицу outbox. Более того, после отправки также требуется запись в таблицу о том, что сообщение отправлено.
- Сложность реализации и поддержки — помимо реализации отправки сообщений, стоит задуматься об отказоустойчивости самой службы отправки, при самостоятельной реализации необходимо учесть следующие нюансы: что делать, если отправка сообщений упадет с ошибкой? Что делать, если сообщение не может быть прочитано и/или отправлено (изменилась схема, битые данные и т. п.).
- Проблема с порядком отправки сообщений — обе модели не гарантируют очередность отправки сообщений, чтобы организовать строгий порядок, требуются дополнительные манипуляции с кодом.
- Возможная доставка дубликатов. Необходимо предусмотреть идемпотентность обрабатываемых сообщений.
Итог
Паттерн Outbox — одно из возможных и распространенных решений в Event Driven архитектурах. Данный подход обеспечивает атомарный подход в сохранении данных и отправке сообщений об изменении данных, что замечательно подходит для систем, в которых взаимодействие реализовано с использованием хореографии. Но техническая реализация требует дополнительных усилий.
Подпишись на мой канал в telegram