Как стать автором
Обновить

Комментарии 15

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

Независимость микросервисов - это миф. Просто либо они явные, либо неявные. А внутрення структура микросевиса, это дело самого микросервиса. Он может реализовать как маленькую функциональность, так и большую логику, которую, например, не имеет смысла разделять. Пример на python говорит ровным счетом ни о чем. Ну, есть некий фасад в виде main, который оперирует синхронным взаимодействием с двумя микросервисами, и что? Если вы хотите разделить их ещё на 100, тогда получаем антипаттерн номер 1 в вашем списке.

Антипаттерн 4: "Синхронная связь между микросервисами"

Тут начали за блокирующий I/O, а закончили очередями. Вообще многие ЯП имеют средства асинхронной отправки тех же HTTP запросов, не блокируя поток (async/await, корутины, реактивные библиотеки). Суть асинхронного взаимодействия микросервисов не в неблокирующем I/O, а в том, чтобы сделать инверсию зависимостей от других микросервисов, отправили событие и забыли. Это просто разные виды взаимодействия и не более, нельзя синхронное называть антипаттерном.

Простой пример, есть сервис аутентификации/авторизации, а есть сервис пользователя. По вашей логике объединять их нельзя потому что получается микросервисный монолит, а если оставить два, то как первый у второго возьмет информацию о пользователе, что он хотя бы существует в системе, если синхронно нельзя общаться? Можно, конечно, и там, и там хранить инфу по пользователю, синхронизировать её на основе событий из очереди, плясать с сагами и прочий мазохизм ради замены 1 запроса для субъективной чистоты, а по итогу получить ненужный оверинжинирнг. А когда брокер упадет, такая вечеринка начнётся….

Все эти мантры, штампы и антипаттерны не абсолютны. Создание ПО - это всегда компромисс.

Например ответьте мне пожалуйста на вопрос. Есть у меня десяток сервисов на одном языке, в системе принят общий формат логгирования. Как правильно? Вынести логгер в общую библиотеку и подключать к каждому сервису или реализовывать / копировать в каждом сервисе и почему?

Я всегда выношу в библиотеку (джем) и подключаю, версионирую, развиваю и считаю это верным подходом, но часто в статьях (теоретики?) говорят что это не верно.

Таких библиотек обычно несколько. Клиенты разные и прочее.

часто в статьях (теоретики?) говорят что это не верно

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

Процитирую определение, данное в начале "Антипаттерны – это повторяющиеся решения проблем, которые кажутся интуитивно верными, но на практике приводят к негативным последствиям".

И потом, к примеру "Антипаттерн 6: "Игнорирование отказоустойчивости"". Ну где ж тут "интуитивно верное"?

Про синхронное - не соглашусь. Это в принципе идет от логики. Если вам условно надо заблокировать средства на карте, а потом выдать товар, вы хоть на голову станьте, но эту последовательность нарушить никак нельзя. И первая операция является блокирующей по отношению ко второй. Это просто логика.

НЛО прилетело и опубликовало эту надпись здесь

Половина из этого не имеет отношения ни к патернам, ни к анти патернам. Просто ошибки и недочеты проектирования микросервисов.

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

Кто может пояснить подробнее по 5 антипаттерну?

  1. Если взять даже пример на питоне. Есть у меня например интернет магазин, есть микросервис по работе с корзиной. А есть микросервис по отправке уведомлений. Отправляя уведомления мне в любом случае понадобятся данные представленные в корзине. Если это разные БД то каким образом я тогда должен их связывать? Через АПИ?

  1. Про миграции тоже не понял. Когда БД общая - ты из накатил из любой точки и все. Но если человек разрабатывающий микросервис по работе с корзиной внесет изменения в БД то эти изменения в любом случае коснутся и работы микросервиса по работе с уведомлениями и там придется переписывать код. Так какие де тогда преимущества в итоге чтобы использовать раздельные БД?

  2. Есть у меня интернет магазин. Я решил разработать приложение для телефона. Для приложения для телефона мне нужно разработать отдельное апи. Каким образом тут БД может быть не общая? Да можно было бы вынести апи для интернет магазина и приложения в отдельных сервис, но тогда получается тяжелое апи для работы сразу с двумя сущносями, которые слабо зависимы. Даже если разделить это на два апи, то БД у них все равно одна будет. Что тут можно разделить?

  1. Да, может взять по API информацию у других микросервисов, либо же в самом сообщении/запросе на уведомление получить нужную информацию.

  2. Так в этом и преимущества, что БД корзины никак не влияет на БД уведомлений, нужную информацию получаешь по API или в сообщении/запросе на отправку уведомления и вообще неважно, как там и что хранят, у всех свои миграции. В ином случае смена схемы неявно может сломать другие микросервисы, которые, например, работали с одной таблицей, а её потом вдруг удалили или поменяли колонки нужные им. И вот в этой неявности и есть проблема. Когда микросервисы обмениваются информацией по API у них есть чёткий контракт, который не стоит нарушать или хотя бы поддерживать до момента вывода из эксплуатации старой версии API.

  3. Либо общее API, либо раздельное, в зависимости от потребностей, при чём тут БД вообще не ясно, API Gateway или BFF обращается к микросервисам за нужной инфой или выполнением каких-то действий, а как там хранятся данные ему в общем-то пофиг, это ответственность конкретных микросервисов.

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

Простой пример у нас есть корзина и есть товары как отдельные микросервисы.
У меня в корзине 5000 товаров. Я должен на веб-странице в корзине отобразить 3 страницу и показать только те товары, что есть в наличии.

Каким образом я тут сделаю связь по апи? Ну то есть, если я в запрос отправлю с ИД товаров в корзине на 3 странице (например 20 штук), то я не смогу учесть есть товар в наличии или нет, и соответственно информация будет отображена неверно. А если я запрошу ИД товаров из магазина соответствующие ИД товарам в корзине, то мне придется пересылать по апи 5000 ИД из корзины, чтобы найти соответствующие товары. А если данных еще больше и сложнее? Я вижу тут проблемы с производительностью.

Не совсем понял, как

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

мэтчится с

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

Здесь видится взаимозависимость между этими сервисами их, возможно, стоит объединить в один?

Вообще не нужно забывать про универсальные принципы, которые применимы и к микросервисам тоже. Например single responsibility, который гласит: объединяйте вместе (в один класс, пакет, микросервис) то, что изменяется вместе по одним причинам и разъединяйте в ином случае.

Микросервисы - это уже не модно само по себе и антипаттерн для команд маленького размера и стартапов.

Да - прикольно пилить работу на 1000 человек через микросервисы - чтобы создать магазин косметики. И уже не прикольно делать это втроём в каморке как монолит в котором и фронт и бэк - всё в одном месте через шаблоны.

Но есть более интересная вещь - настоящий кошмар для любителей догм про микросервисы - "Квантовые сервисы". Квантовые сервисы позволяют создавать такие магазины командами от 5-ти человек (3 на Бэк и 2 на фронт, например). Квантовые сервисы берут лучшее от монолитов половину антипаттернов от микросервисов и немного их паттернов - и получается гремучая смесь.

Квантовый сервис - это один проект/репозиторий (моно-репа), который рождает несколько докер-контейнеров (обычно 5-20, команда то маленькая, а ещё тесты писать и нагрузочные тесты и QA тесты - и это всё тем 3-м ребятам с бэка). Кодовая база одна - безусловно. Код поделён на слои и грамотно. Можно масштабировать его хоть до бесконечности. Повторяющийся код-опыт выделяется в OpenSource решение, если это код, реализующий технологию. Бизнес-логика выделена в отдельный кодовый слой-ядро. А к ядру - любой слой интерфейса, gRPS, REST-API, что угодно. БД одна или несколько - зависит от проекта (не только Одна! или каждому контейнеру своя обязательно! А сколько выгодно - столько и баз). Так ещё и СУБД самих может быть несколько и это нормально.

CQRS - полезен и в квантовых сервисах, один контейнер наполняет БД, другой - строит запросы и отдаёт в дашборды.

Сага не нужна, так как между контейнерами связи нет (Вообще нет - вот оно, соблюдение супер-паттерна). Они не общаются. У них в ядре весь код всего проекта (да, эти страшные исходники легаси бизнес-логики на 5-20 мегабайт - лежат в каждом контейнере и "жутко бесят" Devops команду). Потребности через API дёргать функции друг у друга нет, весь код есть у каждого. Нужна очередь - включаем gRPC на RabbitMQ или что-то подобное.

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

Пример деления на слои:

Слой 1. Кодовая база (она же библиотеки, она же слой Технологий) - ничем не отличается от того, что все скачивают из OpenSource решений на гитхабе. Каждая своя библиотека/модуль/GoMod проходит тестирование и может использоваться на любом проекте как обычно (как и любые другие чужие).

Слой 2. Бизнес-легаси. Ядро проекта. Бизнес-логика - она всегда легаси, почти. Она постоянно меняется, хардкода там полно - заказчик так решил, ему надо было быстро - он дал денег. Если хардкод обретает смысл - он отправляется в слой 1. Постоянно идёт процесс осмысления того что есть технология, а что есть - просто логика и где можно паттерн ООП воткнуть, а где и хардкода достаточно.

Слой 3. Интерфейс. Здесь всё просто. Нужен REST - прикрутили REST. Нужен gRPS - прикрутили gRPS. Когда интерфейсов много они могут реализовывать разные вещи, или одинаковые но по-разному. Это всё вопросы интерфейсов.

Сумма трёх слоёв родит контейнер.

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

А теперь шок-контент - если один квантовый сервис родит один контейнер - это будет микросервис =) Да ещё и по всем канонам всех миров.

Внешне, когда получается несколько контейнеров - это ничем не отличается от микросервисов. Всякие хелс чекеры, кёркит брикеры и прочую нечисть можно спокойно вешать сюда. Настраивать метрики. Пилить QA тесты. Узлы системы масштабировать в кубере независимо друг от друга. 100% поведение микросервисов. Не найдёте ни одного отличия просто. Внутри - это 100% единая кодовая база. Слои которой живут своей жизнью. Поведение - правильного монолита.

А какие плюсы?
+Отпадает часть коммуникаций между контейнерами. На это не тратятся ресурсы просто.
+Человек нужно меньше на тоже самое.
+Не нужно 100500 БД в СУБД и 15 СУБД. Нужна одна правильно построенная система хранилища под задачи бизнеса.
+Кода меньше, чем при микросервисах (мы же выкинули шины между ними и их поддержку, как минимум)

А какие минусы?
- Это не монолит.
- Это БУДЕТ распределённый монолит, если не соблюдать правила догм монолита.
- Это не те микросервисы, к которым привыкли большие команды. Более того это не подходит для числа людей когда часть сервисов на Go, например. Другая - на Python. Но не что не мешает объединить их в 2 команды по языку с другой стороны. =)

Пока звучит так, что это просто монолит с чистой архитектурой, в котором разного вида API поделили на разные инстансы с помощью nginx/haproxy/envoy/etc. Есть пример публичного репозитория?

Кажется вы пересказали нам архитектуру микроядра. Где можно почитать про ваши квантовые сервисы?

Спасибо за статью и структурированные мысли! Буду давать ссылку и экономить время на объяснении :)

Только вот с микросервисным монолитом непонятно; мне кажется, объясняется обратный случай — монолитный микросервис.

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

«структура, которая тесно связана друг с другом» - отсюда я перестал понимать. Проблема в том, что каждый микросервис внутри - суть монолит? (А что тут плохого?) Вроде бы антипаттерн здесь - это высокая связность между сервисами, но это уже другой пункт, косметические микросервисы.

Зарегистрируйтесь на Хабре, чтобы оставить комментарий