Задумывались ли вы, почему мы используем отдельную базу данных для каждого сервиса, но при этом один общий брокер для нескольких сервисов? Ведь вполне возможно использовать базу данных в роли брокера сообщений. Однако если мы попробуем заменить RabbitMQ на Redis, то натолкнемся на проблему общего использования базы данных. Это станет проблемой, потому что общее использование базы данных является антипаттерном.

как AI видит Message broker per service
как AI видит Message broker per service

????️ Database

В микросервисной архитектуре существуют два основных подхода к организации баз данных:

  1. Отдельная база данных для каждого сервиса

  2. Общая база данных для всех сервисов

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

Подход с отдельной базой данных для каждого сервиса подразумевает, что каждый сервис использует свою собственную базу. Сервис может получать данные другого сервиса только через API, без прямого подключения к его базе данных. Это позволяет командам выбирать базы данных, которые им больше подходят. Например, кто-то может предпочесть MongoDB, другие могут предпочесть PostgreSQL, а для кого-то будет достаточно Redis.

???? Database per service - подход, где каждый сервис имеет свою базу данных, обеспечивая изоляцию и независимость.

✉️Shared message broker

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

В работе брокера сообщений участвуют два ключевых элемента: producer (создатель сообщений) и consumer (получатель/подписчик). Один элемент создает сообщения и отправляет их другому элементу-получателю. В процессе отправки брокер обеспечивает промежуточный этап, сохраняя сообщения от продюсера в определенной папке файловой системы.

Для чего нужны брокеры сообщений?

  • Гарантии доставки сообщений. Если получатель не готов обработать полученную информацию в данный момент, она будет сохранена в очереди, и получатель сможет обработать ее позже.

  • Повышение производительности. Использование брокеров может повысить производительность благодаря асинхронной обработке сообщений.

  • Отказоустойчивость и масштабируемость. Способствует созданию отказоустойчивых и масштабируемых систем благодаря асинхронной обработке данных и возможности использовать кластеры брокеров.

  • Разделение слоев. Допускает разделение слоев приложения, уменьшая связность между ними, что предоставляет возможность сделать приложение более модульным, гибким и удобным в разработке. Также будет полезен при организации микросервисной системы.

Посмотрим на рекомендуемую схему с брокером между сервисами. Здесь мы увидим что брокер сообщений общий для всех сервисов:

Минусы

  • Усложнение системы. Использование брокеров повлечет за собой увеличение сложности системы за счет добавления нового элемента в нее. Усложняется отладка, обслуживание и администрирование.

  • Сложность в освоении. Изучение данного ПО может занять длительный период времени из-за сложности архитектуры.

  • Дополнительная точка отказа. Это место возможного критического отказа, при котором большинство систем не смогут функционировать. Однако, многие брокеры обеспечивают высокую доступность и надежность функционирования. Это потенциальное узкое «горлышко» в производительности.

  • Мертвые сообщения. В наших старых и забытых очередях существуют мертвые сообщения, которые, казалось бы, потеряли свою актуальность и важность, но они все равно остаются на своих местах.

  • Дополнительная нагрузка на сеть. Использование брокеров может повлечь за собой дополнительную нагрузку на сеть из-за передачи данных.

????Message broker per service

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

Не явная связанность

Давайте рассмотрим пример, как брокер может быть использован для асинхронного обмена данными между двумя сервисами.

Можем ли мы определить, какой из сервисов работает в асинхронном режиме? Нет, они тесно связаны, как в монолитной структуре, и оба могут быть классифицированы как асинхронные.

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

Теперь давайте приступим к разработке новой схемы.

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

Модель, где каждый сервис использует свой собственный брокер сообщений, предполагает обмен данными сервисов исключительно через API, без прямого доступа к очередям друг друга. Это дает возможность иметь единый контракт для асинхронного и синхронного взаимодействия.

Что бы, когда нам потребуется от сервиса синхронный и асинхронный метод, не получить такую схему:

Это также позволит нам проверить модель сразу при входе, что бы не потерять запрос.

Брокер не заменит HTTP

Рассмотрим возможность использования брокера вместо HTTP-канала для решения проблем с сетью и обеспечения надежной доставки сообщений. Брокер функционирует на основе легковесного протокола AMQP, который может работать лучше, чем HTTP. Ниже представлена схема с общим брокером для сервисов. В случае возникновения сетевых проблем при попытке отправить сообщение, существует риск его потери, поскольку у нас может не получиться подключиться к очереди. Только после того, как сообщение попадает в очередь, мы можем гарантировать его доставку в сервис B, если он сразу не сможет подключиться к очереди.

Если рассмотреть ситуацию с отдельным брокером для каждого сервиса, гарантия доставки остается именно на сервисе. В этом случае брокер просто становится инструментом для обеспечения этой гарантии. Даже в случаи обрыве сети, у нас сервис A должен обеспечить повторную отправку сообщения в сервис B.

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

???? Вам не требуется хранить сообщения в брокере длительный период. Достаточно повторить его всего через несколько секунд

Шина сообщений

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

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

???? Брокер сообщений становится центром проекта, от которого зависит вся система.

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

Таким образом, вы получаете аналогичные возможности, но брокер уже не является центром вашей системы. Ответственность за работу прозрачно определяется командой, которая отвечает за данный сервис. Кроме того, между сервисами устанавливается явная связь через REST-интерфейс.

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

Разница:

Отметим разницу между этими подходами

Shared message broker

Message broker per service

Гарантии доставки сообщений обеспечивает сам брокер

Гарантии доставки сообщений обеспечивает сервис, брокер сообщений используется как инструмент

Зависимость от одного брокера, каждый новый сервис становится частью существующей системы

Гибкость: каждый сервис может выбирать брокер сообщений, который наилучшим образом соответствует его требованиям

Размытые границы доменной области, одна бизнес-модель используется в двух и более сервисах

Четкие границы доменной области, сервисы общаются с помощью DTO

Необходимо дублировать модели между сервисами или публиковать через библиотеки или другие инструменты, задумываясь о версионировании

Контракты распространяются между сервисами через OpenApi, Proto файлы или другие популярные инструменты

Мертвые сообщения присутствуют в наших старых и забытых очередях, которые относятся либо к продюсеру, либо к подписчику.

Мертвые сообщения также могут находиться в очередях, но ответственность за них можно точно определить к сервису и его команде

Ошибочные сообщения будут взяты в работу когда поднимется подписчик.

Не доставленные сообщения сразу берутся в повторную обработку

Сложность из-за неявной связи через объекты, сложно тестировать и вести отладку

Простота: связь между сервисами основана на endpoints, IDE подсвечивает событие в подписчике и продюсере

Уменьшение связности между сервисами, все взаимодействие происходит через брокер

У сервиса есть связь и со своим брокером сообщений, и с конкретным следующим сервисом, или даже несколькими

????Подведем итоги

Отказавшись от одного плюса- "Разделение слоев", который предоставлял нам гибкость взаимодействия между сервисами, мы уверенно получаем следующие преимущества:

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

  • Гибкость: Каждый сервис может выбрать брокера сообщений, который лучше всего соответствует его требованиям. Это может включать различные аспекты, такие как производительность, надежность, сложность и стоимость.

  • Очевидные связи: Изучение данного ПО может занять меньше времени, благодаря более очевидной связи между сервисами.

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

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

Комментарии (5)


  1. miksir
    17.11.2023 14:43
    +3

    Хранение сообщений внутри сервиса нужно, что бы уметь повторять сообщения, которые не получилось отправить (ретрай) или что бы поддерживать pull схему, когда к тебе приходят за данными. Но брокер для этих целей не используют ибо у сервиса уже есть что-то, что выполняет его функции. База или просто хранение в памяти. Дополнительно кролик - не нужная сложность, ибо еще придется решать вопрос ошибок записи в этот локальный кролик, т.е. все-равно потребуется какая-то вторая очередь в памяти.

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

    По-этому, если у вас коммуникация "сервис-сервис", то брокер тут избыточен как "посередине", так и "внутри". Т.е. решалась какая-то несуществующая проблема.

    Брокер же используют, когда нужна гибкость распределения одного сообщения по разным потребителям (кролик) или же когда нужен накопительный буфер для потоковой работы с данными (кафка).

    И вот, для решения гибкого распределения сообщений вы говорите "давайте кролика заменим на сервис, который будет ходить в кролика". В принципе, такое даже делают, но не для того, что бы просто amqp на http заменить (у кролика есть http интерфейс, используйте если так amqp не любите), а решая более комплексные задачи от API композиции до сервис дискавери. А просто написать сервис, который конвертирует amqp кролика под капотом во внешний http - это какое-то усложнение не решающее никаких проблем.

    Ваша сравнительна таблица, это такое натягивание совы на глобус, что прям скучно. То брокер сообщений домен размывает, то proto файлы кто-то запретил использовать для сообщений из кролика.


  1. savostin
    17.11.2023 14:43

    Подождите, если у Вас consumer завис, то таким образом он завешивает и всех publisher'ов, потому, что они будут заниматься повторением отправки сообщений. В итоге всё лежит. Не вижу никаких плюсов в том, чтобы перекладывать доставку сообщений на сервис. Да и зачем каждому сервису очередь? Обычно очередь используется для доставки сообщений между сервисами, а не в пределах одного.


  1. Actor
    17.11.2023 14:43
    +1

    @miksir Возможно не правильно понял вашу мысль, но не согласен с утверждением

    По-этому, если у вас коммуникация "сервис-сервис", то брокер тут избыточен как "посередине", так и "внутри". Т.е. решалась какая-то несуществующая проблема.

    Брокера нужно рассматривать в данном случае как транспорт, такой же как и http запрос.

    Возьмём пример, когда пользователь жме кнопку на сайте, она делает локальные изменения, а затем шлёт информацию в другой сервис. Если мы используем обычный http запрос в обработке нажатия кнопки, то вынуждено поймаем лаг в интерфейсе, пока отправим запрос и пока его отработает другой сервис... (в js это будет диалог со с прогрузкой, а где то в другом месте вплоть до повисания интерфейса (не весь мир радужный, есть старые системы не умеющие многопоток)).

    Поэтому мы сохраняем сообщение в локальном сервисе (для истории или дальнейшей обработки после подтверждения другим сервисом). Отправляем в брокера сообщение возвращаем поток в интерфейс пользователя.

    Ок рассмотрим вариант когда мы не используем брокера, а сохраняем сообщения для дальнейшей отправки позже. Сохранили локально, вернули в интерфейс пользователя результат что принято на обработку. Как их дальше обработать? Нам нужен сервис или служба локальная, которая пройдёт по этим сообщениям и выполнит запросы к другим сервиса. В дополнение, когда начинать обработку? Вечный цикл с таймаутом в пару сек? При запросе к нашему сервису по обмен?

    Что то напоминает? Мы практически сделали свой брокера, кривой, не функциональный, но очень похожий функционал. Вопрос, а зачем мы это сделали, и почему же бы нам не использовать готовые инструменты?


  1. GrigoryPerepechko
    17.11.2023 14:43

    Автор путает базу и субд сервер.

    У микросервисов даже когда базы разные очень часто шарится субд сервер.

    С очередями так же история. Брокер шарится, а очереди разные


    1. kredis31 Автор
      17.11.2023 14:43

      Если проводить аналогию с базами то, в том же кролике есть такое понятие как virtual host. И как раз брокер можно шарить, как и СУБД. А вот очереди и виртуальный хост нежелательно, так же как и бд с таблицами.