Re[13]: Помогите правильно спроектировать микросервисное приложение
От: gandjustas Россия http://blog.gandjustas.ru/
Дата: 01.02.26 11:44
Оценка:
Здравствуйте, Sinclair, Вы писали:

S>Здравствуйте, gandjustas, Вы писали:

S>>>Всё верно. MSA не гарантирует безошибочности. MSA гарантирует, что падение не затронет сценарии, в которых микросервис не задействован.
G>>Смотря что значит "не затронет". Если сервис А зависит от срвиса Б, а сервис Б содержит ошибку, даже неважно приводящую к падению или нет, то и А не будет корректно работать.
S>Зато сервис В, который не зависит ни от А, ни от Б, гарантированно продолжит работать.
Да, я об этом и писал. Если все микро-сервисы могут работать независимо друг от друга, то такая архитектура жизнеспособда.
Но тогда важное условие чтобы транзакционный бизнес-процесс не пересекал границы микросервисов. Как только начинает пересекать — появляются зависимости и одно без другого уже не работает.
Например для ecommerce деление на "заказ" и "склад" в таком случае не актуально, так как при создании заказа надо на складе все забронировать.
А вот "доставку" от "магазина-склада" вполне легко отделить.



S>Воображаемый пример: микросервис аутентификации не зависит вообще ни от кого; кривой апдейт в сервис пользовательских профилей положит почти всю систему КРОМЕ кода, который зависел только от логина. Например — OAUTH с внешними сервисами. Это кажется мелким преимуществом, но в реале это — огромный шаг вперёд по сравнению с хранением профиля и кредов в одной табличке Users под управлением монолита.

Это ровно до тех пор пока БЛ не начинает опираться на роли пользователей.
Например: сотрудник магазина получает скидку 10% на все покупки в онлайн магазине. Чтобы решить эту задачу сервис "заказа" должен пойти в "auth", чтобы получить роль.
Что касается как сделать аутентификацию качественно — есть ASP.NET Identity, который работает и с OAUTH, и с собственными логинами, и федерацией. И он совсем не микросервисный.
И я пока не видел чтобы кто-то мог превзойти ASP.NET identity по возможностям, удобству использования и быстродействии.

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

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

S>А если не воткнул, то и в МСА у нас будет fail-fast, а не генерация мусора.

За счет чего? Если мы делаем синхронный вызов, то у нас надежность сервиса, зависит от надежности другого и в сумме не превышает надежность монолита.
Если мы делаем асинхронную репликацию изменений, то отдаем неверные данные.
Опять приходим к тому, что если граница микросервисов проходит по границе транзакций, то МСА норм работает, а если пересекает, то мы автоматически получаем проблемы.

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

S>Да, это известная проблема. И она-то и является основным аргументом против микросервисов.
Основной агрумент против, это то, что МСА сама по себе ничего не дает и очень многое отнимает.
Все проблемы, которые призвана решать МСА, прекрасно решаются и без МСА. Кроме, наверное, проблемы разработки на нескольких языках программирования.
И то, например, сейчас можно в дотнет процессе запускать Python код. Это не IronPython, который в IL компилирует, а это прямая загрузка питонячего рантайма в дотнет, генераторы, которые делают C# АПИ для питонячего кода и прокидывание ссылок на объекты в из дотнета в пайтон.

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

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


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

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

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

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

G>>3) При падении\ошибке в "нижних" "верхние" тоже ломаются.

S>Главное, что при ошибках в "верхних" не ломаются нижние и соседние. Это всё ещё сильно лучше, чем монолит с "у нас тут новичок добавил компонент, который выжирает всю память, и приходится перезапускать всю машинерию, а у нас время старта 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>>Это в монорепе гораздо сложнее, до уровня "почти невозможно", так как версия всех компонент в монорепе всегда одна. Если вы не балуетесь выносом бизнес-логики во внешние пакеты.
S>Во-первых, может и балуемся, во-вторых, вы же не ведёте разработку в main. Вот ребята в В залили в монорепу изменение, которое сломало сценарий 2. "У нас юнит-тесты зелёные, а если у кого-то сломался сценарий — значит, они нас неправильно используют". Им дали по пальцам, откатили PR. Ребята из Б залили свой PR — сломался сценарий 1. И ребята из А заливают свой PR с тем же результатом.
У нас все просто — кто делает ПР тот и чинит. Тупо защита на ветке — не сольешь пока есть ошибки билда (там еще и тесты запускаются, но у нас их мало).

S>Предотвращение мерджа ломающих изменений — это чисто организационная работа. То есть если у нас есть интеграционные тесты со всеми сценариями И запрет мёрджа PR, при котором ломаются такие тесты — то всё будет работать и в монорепо, и в раздельных репозиториях. А если набора интеграционных тестов нет — то и в монорепо у вас окажется ситуация, когда все юнит-тесты зелёные, покрытие кода 99%, а ни один пример из папочки examples запустить не получается.

Я бы сказал что это чисто настройка ci\cd системы, конечно пока все в одной репе происходит.
Как только у вас требуется коммит в несколько реп для реализации функционала — начинаются танцы с бубнами (реальное описание "организационной работы")
Мы сейчас пытаемся решить такую проблему сабмодулями, но у них свои ограничения.

G>>Я делаю более сильное утверждение: MSA пригодна только там, где есть соответствующая оргструктура: много несвязанных команд, каждая из которых работает на процессами, слабо связанными друг с другом. Такое в банках и маркетплейсах такое очень часто встречается, а они составляют основу русского бигтеха.

G>>А например в корпоративных системах оно не надо, как при кастомной разработке, так и при разработке тиражируемого продукта.
S>Склонен согласитья. Сервисная архитектура всё ещё может быть полезной даже и в этих сценариях, но прямо мелкогранулярное дробление — вряд ли.

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

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

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

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

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

S>Ну, да, code review. Требует, правда, внимательного вычитывания. В TS/JS сплошь и рядом (s as any as { Value: string }).Value.
ИИ? Линтеры? Показательно бить палками тех кто так пишет?
Зачем для решения плохого кода на TS делать МСА?
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.