Re[12]: Помогите правильно спроектировать микросервисное приложение
От: Sinclair Россия https://github.com/evilguest/
Дата: 01.02.26 07:12
Оценка:
Здравствуйте, gandjustas, Вы писали:
S>>Всё верно. MSA не гарантирует безошибочности. MSA гарантирует, что падение не затронет сценарии, в которых микросервис не задействован.
G>Смотря что значит "не затронет". Если сервис А зависит от срвиса Б, а сервис Б содержит ошибку, даже неважно приводящую к падению или нет, то и А не будет корректно работать.
Зато сервис В, который не зависит ни от А, ни от Б, гарантированно продолжит работать.
Воображаемый пример: микросервис аутентификации не зависит вообще ни от кого; кривой апдейт в сервис пользовательских профилей положит почти всю систему КРОМЕ кода, который зависел только от логина. Например — OAUTH с внешними сервисами. Это кажется мелким преимуществом, но в реале это — огромный шаг вперёд по сравнению с хранением профиля и кредов в одной табличке Users под управлением монолита.

G>Падение — не самая страшная часть, даже в проде. Плохо когда: после падения не может подняться, или вместо падения не работает корректно — висит в ожидании или отдает некорректные данные. Причем второе при несинхронной разработке весьма вероятно.

КМК, это не какие-то "новые" проблемы из-за МСА, а все те же проблемы, которые прекрасно работали и в монолите. Висение в ожидании возможно примерно везде, где есть await (то есть везде). Получение некорректных данных — да запросто, если разработчик воткнул какие-нибудь try {return calculatedTax()} catch { return defaultTax }. А если не воткнул, то и в МСА у нас будет fail-fast, а не генерация мусора.

G>Его же не надо "внутри", надо чтобы разделяемое состояние польностью сохранялось и обрабатывалось ACID хранилищем, желательно внешним по отношению к самому приложению. Чтобы приложение можно было безопасно масштабировать и перезапускать.

Ну вот я о том же и говорю. Это не так просто, как кажется

G>Но у этой медали есть и обратная сторона: если у вас разные базы, то сделать джоин в них крайне сложно. Задачи которые в монолите бы решались одной строкой в SQL в MSA начинают требовать тонны кода.

Да, это известная проблема. И она-то и является основным аргументом против микросервисов.

G>Пример реальный: профили пользователей лежат в одном сервисе, данные о туристических маршрутах в другом. Важная часть логики: для несовреннолетних доступна одна часть маршрутов, для соврешеннолетных — другая. Есть еще другие признаки для фильтров: по регионам, интересам итд.

G>Теперь нам надо сделать рассылку, подобрав подходящие маршруты для клиентов.
G>В монолите — один джоин. В MSA — пожалуйста напишите тонну кода.
G>В реальности ситуация была еще хуже. Данные о бронях групп и совершенных путешествиях лежали в третьем сервисе. И конечно же надо было из предложений исключить тех кто уже ездил.
Тут дело, КМК, не в тонне кода — что там такого-то, цикл по пользователям. Скорее, тут время работы — то, что делалось 1 запросом за 15 минут, теперь — миллион обращений к двум сервисам, каждый из которых тратит по две секунды.

G>А в чем собственно преимущество? Ну если в деньгах посчитать.

В убытках от простоя внедрения изменений в сервис Х. Когда мы пилим монолит, то всегда натыкаемся на то, что "Команда Y не успела стабилизировать свой компонент, поэтому релиз задерживаем на неделю". Какой-нибудь баннер "регистрируйтся на наш конвент 1 октября" уже начинает устаревать, а мы всё никак-никак не можем зарелизиться. А зарелизить отдельно баннер нельзя — монолит-с.

G>Но сразу три замечания:

G>1) это цикл внедрения фичи удлиняет. Так как скорости выгоднее делать "вертикальное" деление проекта.
А обычно так и есть. Суперглубокие цепочки бывают редко. Там скорее два слоя: ядро/инфраструктура, и прикладные штуки. И даже если цепочки длинные, то по мере приближения к "корню" фундаментальность нарастает вместе с падением ожидаемой частоты внесения изменений. Поэтому большинство фич глубже двух уровней ничего не требуют. А те, которые требуют, и в монолите бы потребовали матёрого редизайна и адски длинного цикла согласования и объединения изменений. Ну, там, перейти от "чисто рублей" к "мультивалютности". Придётся переделать всё сверху до низу, без вариантов.
G>2) Это противоречит мысли самодостаточности сервиса. "Верхний" не работает без "нижних", а "нижние" не нужны без "верних".
Нет такой идеи, поэтому и противоречить нечему.
G>3) При падении\ошибке в "нижних" "верхние" тоже ломаются.
Главное, что при ошибках в "верхних" не ломаются нижние и соседние. Это всё ещё сильно лучше, чем монолит с "у нас тут новичок добавил компонент, который выжирает всю память, и приходится перезапускать всю машинерию, а у нас время старта 15 минут".

S>>>>Но с таким подходом мы налетим на те же проблемы и в монолите — если мы ограничимся юнит-тестами компонентов без интеграционного тестирования получится ровно такой же результат.

G>>>В msa весьма вероятна ситуация, что в принципе невозможно собрать работающее сочетание микросервисов. Я такое на практике видел. Было три микросервиса (А,Б,В) и два процесса(1,2), затрагивающие три сервиса. В один момент было так, что: А/HEAD, Б/HEAD, В/HEAD~1 работал сценарий 1, но не работал 2. А/HEAD, Б/HEAD~1, В/HEAD работал 2, но не работал 1 и при этом же А/HEAD~1, Б/HEAD, В/HEAD также работал 2, но не работал 1.

G>>>И каждый разработчик миросервиса доказывал что "work on my microservice" (современный аналог work on my machine)

S>>Это организационная проблема. В монолите все эти команды делали бы ровно то же самое ровно с тем же результатом.
G>Это в монорепе гораздо сложнее, до уровня "почти невозможно", так как версия всех компонент в монорепе всегда одна. Если вы не балуетесь выносом бизнес-логики во внешние пакеты.
Во-первых, может и балуемся, во-вторых, вы же не ведёте разработку в main. Вот ребята в В залили в монорепу изменение, которое сломало сценарий 2. "У нас юнит-тесты зелёные, а если у кого-то сломался сценарий — значит, они нас неправильно используют". Им дали по пальцам, откатили PR. Ребята из Б залили свой PR — сломался сценарий 1. И ребята из А заливают свой PR с тем же результатом.
Предотвращение мерджа ломающих изменений — это чисто организационная работа. То есть если у нас есть интеграционные тесты со всеми сценариями И запрет мёрджа PR, при котором ломаются такие тесты — то всё будет работать и в монорепо, и в раздельных репозиториях. А если набора интеграционных тестов нет — то и в монорепо у вас окажется ситуация, когда все юнит-тесты зелёные, покрытие кода 99%, а ни один пример из папочки examples запустить не получается.
G>Я делаю более сильное утверждение: MSA пригодна только там, где есть соответствующая оргструктура: много несвязанных команд, каждая из которых работает на процессами, слабо связанными друг с другом. Такое в банках и маркетплейсах такое очень часто встречается, а они составляют основу русского бигтеха.
G>А например в корпоративных системах оно не надо, как при кастомной разработке, так и при разработке тиражируемого продукта.
Склонен согласитья. Сервисная архитектура всё ещё может быть полезной даже и в этих сценариях, но прямо мелкогранулярное дробление — вряд ли.

G>Ну я точно также могу сказать про запись в чужую базу. Оно ведь все равно деплоится все в одно приложение докера\кубера и контейнеры видят друг друга. И все равно разраб сталкивается с тестовой средой, где может "в дикой природе" наблюдать какие еще сервисы работают и с какими хранилищами.

Ну нет, не видят.

G>Так и в MSA изоляция зачастую условная. Да, у каждого сервиса база своя, но сервер БД один. Дорого поднимать несколько экзепляров даже постгреса. И учетные данные одни.

Это всё — фикция. Культ карго. Как и совмещение всех микросервисов поверх одной БД. Недостатки МСА мы получаем, а преимущества — нет.
G>Если разработчикам не прививать дисциплину, то никакая изоляция не поможет.
Ну, дисциплина-дисциплиной, а оргмеры — оргмерами. Наша задача — не приучить разработчиков силой воли воздерживаться от простых и неправильных решений, а сделать правильные решения более простыми и прямолинейными, чем неправильные.

G>Один раз премии лишить за рефлекшн без необходимости и сразу желание пропадет так писать.

Ну, да, code review. Требует, правда, внимательного вычитывания. В TS/JS сплошь и рядом (s as any as { Value: string }).Value.

G>Ну давай честно: есть предел размера команды или предел структуры управления для монолита. С этим никто не спорит.

G>Далее возможно развитие или по пути MSA или по пути "ядро\платформа и прикладная часть".
G>Но до этого предела монолит превосходит MSA по любым параметрам.
+1

S>>Так "micro" это же не про размер команды. А про "площадь поверхности".

G>Не очень понятно в чем она измеряется.
В количестве ендпоинтов. Микросервис — это, грубо говоря, CRUD для одной главной entity type. Может, там есть 20 ендпоинтов для разных способов конструирования и поиска этих entity type, но всё ещё не так, что там и пользователи, и логины, и группы, и профили, и счета, и карты, и маршруты, и бронирования.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.