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

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

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

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

Что представляют себе разработчики в среднем по больнице

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

Сразу вспоминают про монолиты, что микросервисы это шаг вперёд и вообще гораздо более лучше. На вопросы, а что есть ещё кроме микросервисов и монолитов, отвечают обычно что не знают.

Прошу обозначить плюсы и минусы каждого подхода - тут всё обычно сложно, но со скрипом называют такие плюсы микросервисов:

  • Можно фигачить каждый сервис на своём фреймворке и языке

  • Проще поддерживать - сервис же маленький, кода мало

  • Проще тестировать - то же самое, сервис маленький же

  • Лучше скейлятся - почему так, и почему эти самые “монолиты” скейлятся хуже, обычно непонятно

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

Как выглядят эти ответы для меня:

Ну или наоборот. Или всё-таки нет.
Ну или наоборот. Или всё-таки нет.

Мало того, что они упускают всю суть, и следовательно начинают лепить “микросервисы” где надо и где не надо, так ещё и делают это плоско, не видя, что никаких “микросервисы vs монолиты” в принципе то и нет, а есть многомерная ситуация в нескольких плоскостях сразу, с кучей выбора.

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

Что важно на самом деле

  • Во-первых, “микросервисы” - это сложно

  • Во-вторых, “монолиты” - это не всегда что-то плохое.

  • В-третьих, индустрия давно уже перешагнула через эту возню “микросервисы vs монолиты”, и воюют уже совсем про другое, но вы ещё не в курсе

С кем воюем

У нас сейчас такой обывательский консенсус, что монолит = плохое зло. 

– Чем микросервисы лучше?

– Чем монолиты конечно

(цитаты королей разработки)

Пошла такая охота на ведьм, где всё, что вышло плохо, называется монолитным. Монолит стал таким ругательным словом, которое говоришь - и ничего дальше объяснять не нужно.

“Переписываем монолит на микросервисы”, - говорят и пишут из каждого утюга. Объяснять зачем, уже не модно, и так же понятно - за всё хорошее, против всего плохого, за себя и за Сашку.

Если всё-таки покопаться в сортах монолитов, то на поверхности будет такое:

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

И есть монолиты (про это чуть дальше).

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

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

Индустрия уже давно впереди

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

  • Модули. Думают, как код разложить на относительно самостоятельные и осмысленные модули / компоненты. Смотрят, где у нас в кодовой базе разные зоны ответственности, и где между этими зонами провести границы.

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

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

Про модули

Как и когда разбивать на модули?

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

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

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

Получается так. Когда бить на модули:

  • Когда без разбиения на части становится сложно разбираться в коде, большая когнитивная сложность

  • Когда сложно тестировать - например тестов слишком много и они долго выполняются. А гонять надо все тесты при любом изменении. Разбиваем на модули, и если модуль не менялся, то и тесты для него не надо запускать.

  • Когда несколько команд будут работать над разными частями кода. Лучше каждой команде раздать по модулю, потому что очень фигово получается работать, когда много людей толкаются в одних и те же файлах. Постоянно потом конфликты, которые приходится мержить, это долго и больно. И менеджерам сложно трекать, из-за какой команды постоянно баги и откладываются релизы (сарказм, ну или не совсем сарказм ;)). Гораздо проще провести границу между модулями и через эту границу взаимодействовать командам.

  • Когда в коде явно делаются какие-то совсем разные вещи - в одном модуле солим огурцы, в другом управляем грузовиком.

Когда бить ещё рано:

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

  • Когда границы ваших модулей постоянно и довольно сильно меняются. Иначе придётся постоянно эти модули рефачить, двигая границы туда-сюда. Ну и нужны эти границы тогда?

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

Важно: модули - не равно микросервисы. Они не обязаны общаться между собой по HTTP/Кафке, хоть это и можно. Они не обязаны деплоиться отдельно, хоть и можно.

Про репы

У нас 2 крайности - можно или совсем всё держать в одной репе, и будет у нас монорепа, или каждый модуль раскидать в свой репозиторий.

Монорепы

Говорят, сейчас волна хайпа по монорепам прошла, и их уже не форсят так как раньше. Плюс чтобы сделать хорошую монорепу, недостаточно всё сложить в один репозиторий. Нужно ещё классно обмазать всё это пайплайнами, так чтобы у вас был не 1 мега-билд всего, а чтобы артефакты билдились независимо друг от друга и чтобы тесты гонялись отдельно и независимо. И ещё чтобы всё это не толкалось между собой и билды не занимали несколько часов из-за простоя в очереди.

Чего хочет разработчик

Разработчик хочет, чтобы у него всё было в одном окошке IDE, а не в 10, чтобы ему не нужен был для разработки ноут на 64 гига оперативки, и чтоб не надо было переключаться между 5 проектами, чтобы поправить одну багу, а потом создавать и протаскивать 5 ПРов. И ревьюер тоже скажет спасибо, что его не мучают и не заставляют в голове склеивать этот паззл. А сверху на это всё можно написать красивый интеграционный тест и положить его в проект, чтобы можно было его гонять локально, а не ставить задачу автоматизаторам, чтобы подождать несколько дней и потом получать только ночные прогоны этого теста раз в сутки.

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

Когда можно разносить модули по разным репам?

  • Если эти модули редко будут меняться вместе в рамках одной задачи

  • Если эти модули будут пилить разные команды, которые не хотят толкаться и путаться в ПРах и страдать от поломанных другой командой билдов, красных тестов и прочего

  • Когда репа начинает превращаться в монорепу и требовать нетривиальных усилий на поддержку инфраструктуры билдов

Когда не нужно разносить?

  • Когда мы стартуем новый проект и просто решили, что каждый модуль будет в своей репе

  • Когда мы думаем, что нужно всё сделать “масштабируемо” без понимания сути

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

Про артефакты

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

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

Так вот, если общаться модулям всё-таки надо, а мы их задеплоили по-отдельности, то теперь нельзя просто из одного модуля вызвать метод другого, между ними теперь сеть. И хоба - наша система превращается в распределённую! Со всеми её минусами:

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

  • Очень долго - это возможно и никогда. У нас может потеряться сетевая связность между нашими сервисами, или какие-то сервисы могут быть доступны, а какие-то - нет. Часть приложения работает, а часть - нет :) и как нам с этим жить, придётся теперь думать.

  • Всё, что мы хотим передать по сети, надо сначало превратить в текст, типа в JSON например, а потом обратно. Это вообще-то жжёт проц, придётся платить.

  • За траффик тоже придётся платить, так что готовьтесь к разным хакам по его сокращению: 

    • сжатие: о нет, оно тоже жрёт проц! 

    • кэширование, которое как известно приносит нам самую сложную проблему программирования, плюс вообще-то жрёт память и даёт сервису стейт, с которым потом придётся прыгать - делать его распределённым и прочее (привет ещё траффик!)

  • А напоследок самое весёлое - теперь ваша система всегда неконсистентна! Вот этот вот eventual consistency - теперь ваш лучший друг. А ещё:

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

    • Здравствуйте, компенсирующие транзакции!

    • Здравствуй, Saga!

    • Здравствуй, retry!

    • Здравствуй, circuit breaker!

    • Привет, асинхронное общение через очереди!

    • Хай, Dead Letter Queue!

    • Привет, идемпотентность и дедупликация!

    • Здравствуй, exactly once!

    • Здравствуй, transactional outbox!

    • Идемпотентность - наш хлеб и соль!

    • CDC и дебезиум - наше всё!

    • Распределённый сбор логов!

    • Распределенный трейсинг!

    • Распределённый дебаг! (ой, такого у нас нет)

И всё это лишь бы не вызывать один метод из другого.

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

Наговнокодить то, что будет тормозить, как жопа тигра, и при этом жрать кучу проца, памяти и гнать кучу траффика, а потом ещё и не мочь разобраться, почему оно тормозит и как это починить - вот классика современной разработки.

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

У вас между микросервисами может течь дурацкий паразитный траффик, который никто специально не порождал, и никто и не знает, что он там есть. Просто в результате бага шлётся не 1, а 2 запроса. И ещё вон там лишний запрос, который можно было и не делать. Это в добавок к тому траффику, который все знают, что он там есть, но тоже нифига не понятно, зачем это было гнать по сети.

Нет, конечно для интеграции между системами это всё тоже нужно, но зачем усложнять себе жизнь внутри нашей же системы, которую пилят 2 с половиной землекопа?

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

Когда может иметь смысл деплоить вещи в разные артефакты?

  • Когда один модуль может грохнуть процесс и утащить за собой все остальные, а нам так не хочется. Например, есть система приёма заказов, и система обработки заказов. Если наша система обработки заказов нестабильна и часто падает по OOM, то это не значит что с ней должна падать и система приёма заказов, это может стоить много деняк. А может и нет. Кто знает. Надо узнавать.

  • Когда мы понимаем, что какой-то модуль нужно будет скейлить в совершенно другом темпе, чем все остальные. Тогда можем подеплоить и скейлить его отдельно. Например, какой-то наш API очень любят другие системы и он очень нагружается, а другие нет.

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

  • Когда нам нужно использовать другой стек. Внимание: это бывает не так часто, чаще всего компания старается использовать одинаковый стек везде где можно. Например, есть у нас нейросетка, она что-то считает на видяхе, у неё там вообще свой стек, свои либы, своя жизнь. Мы её можем захотеть запускать отдельно, в том месте, где есть мощные видяхи.

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

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

Дак вот, монолиты и микросервисы

Давайте теперь применим наши новые многомерные знания обратно к изначальной теме.

Получается, наш “монолит” - это система, которая разбита на N модулей, которые разрабатываются в 1 репозитории и деплоятся в 1 артефакт - например один docker-образ или war-файл.

А наши “микросервисы” - это по сути те же N модулей, которые разрабатываются в N репозиториях и деплоятся в N артефактов.

И есть же ещё куча вариантов где-то между, этими двумя, где N модулей могут лежать в M репах и деплоиться в P образов. Если вы видите только монолиты и микросервисы, вы не видите кучу вариантов между ними.

Едем дальше. Все эти модули, сервисы - это не статичная херня. Никогда не бывает, что сели архитекторы, придумали как всё будет, потом пришли прогеры, и всё так и написали. А потом ещё 10 лет система жила и всё так и осталось, как было придумано.

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

Например, начинать разработку часто профитно в монолите - 1 репа, N модулей (часто канает по началу даже 1), 1 артефакт. Так быстрее разрабатываем, легче деплоим, нас всё равно в начале мало. 

А дальше никто не запрещает эти N модулей начать деплоить в M артефактов вместо 1. Если вы не нарушали границы между модулями, то просто пошли, обмазали это всё контроллерами или кафкой, поменяли конфиг сборки - и готово.

Если хочется, можно ещё и на P реп это всё разнести. Например когда у нас 3 команды и каждая пилит свои модули, не пересекаясь с другой, и им не хочется видеть ПРы других команд и хочется всё своё и отдельно.

Итого

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

Нам пора уходить от устаревшей и абсолютно непродуктивной дихотомии “монолит – микросервисы” и смотреть на наши конкретные потребности, при этом не стоя сложные распределённые системы начиная с первого дня, особенно там где они изначально не нужны.

И не смотрите на своё решение как на финальное, всё течёт и всё меняется. У вас всё равно не получится сделать хорошо сразу и навсегда. Будьте готовы завтра усложнить там, где будет надо, а не сразу делать сложно, ведь “через год нам это понадобится, когда нагрузка вырастет”. Вы за этот год можете и не закончить ваш мега-микросервисный проект.

Дисклеймеры

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

Дисклеймер 2: естественно, много всего было упрощено, иначе получилась бы книга. А в чём-то я мог ошибиться. Если это что-то важное, черканите в комменты плз, буду рад узнать что-то новое с вашей помощью.

Дисклеймер 3: как всё на самом деле, я конечно же не знаю, и вы не знаете, и никто не знает. Но все вместе можем предпринять хорошую попытку это выяснить.

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


  1. Avangardio
    01.07.2024 18:37
    +9

    Отличная статья, но не могу себе не позволить шутку: «Не могу читать монолитные статьи, когда переход на микросервисные».

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

    Лично я нашел имбалансный юзкейс для жс бэкенда: мега io и cpu задачи выкинуть куда-нибудь через Кафку или грпс на гошные инстансы, чтоб не словить случаем блок по загрузке на монолите.


  1. SergeyPo
    01.07.2024 18:37
    +5

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


  1. 19Zb84
    01.07.2024 18:37

     Думают, как код разложить на относительно самостоятельные и осмысленные модули / компоненты

    Хорошая статья. Спасибо.
    А в двух словах можно, к чему пришли ? Я модули тестирую на js и есть четкое понимание уже, что я хочу и что делаю. Интересно, что на других языках.


    1. Captain_Jack Автор
      01.07.2024 18:37

      Да вот это и было в двух словах к чему пришёл :)

      Если серьезно, то я из мира java-энтерпрайза, тут если сразу не начали пилить мега-проект на 5 лет, а просто сделали MVP за полгода, это уже считай повезло. Сразу делают "солидно", с замашкой на успешный проект под рост на годы вперёд.

      У нас тут обычно нет практики писать сначала на js, потому что переписывать на другом языке бизнес не готов, когда оно уже работает. Плюс команды не хотят копить техдолг, поэтому сразу делают более менее прилично. Получается правда обычно как всегда :)

      И пожалуй среди джавистов мало тех, кто знает js, а ещё меньше тех, кто любит. Хотя мне в целом заходит.

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


      1. 19Zb84
        01.07.2024 18:37

        Ну это я понимаю )) Не совсем выразился правильно.
        В js сейчас активно модули развиваются.
        Они действительно дают очень большую свободу в написании фронта.
        Начиная с html.

        если на java понимание модулей развивается в таком же ключе, это было бы очень круто


        1. Captain_Jack Автор
          01.07.2024 18:37

          М, вот сейчас понял.

          В java завезли модульность почти 10 лет назад, она даёт хорошую инкапсуляцию. Но она довольно хардкорная и в обычных проектах она не особо используется, больше в каких-то инфраструктурных, где реально важно инкапсулировать часть классов модуля.

          В kotlin тоже есть некоторое подобие - уровень доступа internal. Но тоже пока не встречал, где бы это юзали.

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


          1. 19Zb84
            01.07.2024 18:37

            Понял. Спасибо.
            У js есть особенность. Он асинхронный и модули, что бы могли взаимодействовать между собой их надо синхронизировать между собой и сделать передачу данных в модули, которые может быть ещё не загрузились.
            Плюс надо вокреры(потоки) инициализировать и уничтожить в которые так же надо как то данные из других модулей передавать.

            По этому модули в js имеют, по крайней мере я так вижу, уникальные свойства, которые на языках таких как java сложно увидеть.


      1. brownfox
        01.07.2024 18:37
        +2

        Сразу делают "солидно", с замашкой на успешный проект под рост на годы вперёд

        Это потому, что в мире энтерпрайза никто не оплатит полугодовой MVP или R&D с непонятным выходом. Там правит бал планирование и KPI :)

        Я сам в прошлом из энтерпрайзной интеграции, Oracle / Tibco / Websphere и прочие "платформенные" истории.


  1. Drucocu
    01.07.2024 18:37
    +1

    Спасибо за статью.

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

    Артефакты. Артефакты бывают разные, но в целом это те штуки, которые мы релизим.

    Это точно устоявшийся термин? Почему-то мозг об него буксует.


    1. Captain_Jack Автор
      01.07.2024 18:37
      +3

      Дааа, про связность и связанность это оно, но это какие-то магические слова. Мне они не помогают понять, а часто наоборот - как будто сказал "слабая связанность" - и всем всё понятно, выгода очевидна.

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

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

      Это точно устоявшийся термин?

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


    1. AntonLarinLive
      01.07.2024 18:37
      +5

      Артефакты. Артефакты бывают разные, но в целом это те штуки, которые мы релизим.

      Это точно устоявшийся термин? Почему-то мозг об него буксует.

      Такая же фигня. Когда я вижу слово "артефакт", то неизменно вспоминаются какие-нибудь ботинки лесных эльфов, дающие по +2 к скорости и удаче.


      1. panzerfaust
        01.07.2024 18:37
        +3

        Не знаю как в дотнете, но в джаве Apache Maven использует понятие "артефакт" испокон веку. Да и в нексусе оно так называется. Или вшито в название как в JFrog Artifactory.


    1. RH215
      01.07.2024 18:37
      +2

      Это точно устоявшийся термин? Почему-то мозг об него буксует.

      Да, термин уже достаточно давно существует в CI. Например в Gitlab:
      https://docs.gitlab.com/ee/ci/jobs/job_artifacts.html


  1. kenomimi
    01.07.2024 18:37
    +9

    Монолит - это когда всё приложение для бухгалтерии ТНК - один полуторагиговый жарник (без ресурсов, да - ресурсы отдельно). Запуск на вебсфере около часа. Да, внутри все разложено на модули, но это монолитная дура, запускающаяся последовательно. Если оно наворачивается - а сделать ООМ элементарно легко загрузкой любой разновидности зип-бомбы, или просто файла с кинцом на полтерабайта... Там под капотом решение третьей стороны, крупного американского вендора, которое класть файлы в свое пропиетарное хранилище стримом не умеет, только подняв файл в память полностью. Баги были заведены, но отклонены с каментом "не баг, а фича". Плюс тысяча конвертеров документов, анализаторов, преобразовалок... Навернулась нода - жди час перезапуска. Нод 50, но пользователь упрям, и продолжает грузить этот злополучный файл - половину кластера ложит легко. Сборка - два часа в мемдиске, на хардах полдня, локально не запустишь - дай от терабайта оперативки. Вот это монолит.

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

    Еще есть наносервисы - приложение с одной единственной функцией и девизом "пох на падения" :) чаще всего это приложения, конвертирующие доки либо работающие с архивами. Пошла утеча памяти или распаковка сожрала всю оперативку? Контейнер убивается, тут же запускается новый. Заддосить почти нереально. Минусом будет тот факт, что когда наносервисов станет много - начнется ад коммуникации, запомнить какой топик в кафке чей и кто использует - не очень реально. Хорошо, если есть культура идеального документирования, но почти всегда ее нет, увы.

    И самое главное. Инструмент надо выбирать по задаче, а не забивать кувалдой винтики в очках. Где-то вполне заходит монолит, где-то разные типы микросервисов, а где-то без наносервисов никак...


    1. gravyzzap
      01.07.2024 18:37
      +9

      пользователь упрям, и продолжает грузить этот злополучный файл - половину кластера ложит легко

      Это про говнокод, а не про монолит. Решается оборачиванием решения под капотом собственным кодом, который проверяет размер файла.

      stateless

      Как микросервисность делает ваше приложение стейтлес? Или вы имеете в виду, что некоторые микросервисы будут стейтлес?

      и так же быстро откатить

      Попробуйте откатить баг в микросервисе А появившийся три версии назад, с которой не совместимы последние 5 версий сервиса Б, с которыми несовместимы последние 10 версий сервиса В. Депенденси хел цветёт и пахнет в микросервисах.


      1. maxzh83
        01.07.2024 18:37

        Попробуйте откатить баг в микросервисе А появившийся три версии назад, с которой не совместимы последние 5 версий сервиса Б

        А как это связано, если баг не задействует контракт между сервисами А и Б?


        1. Virviil
          01.07.2024 18:37

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


    1. Xexa
      01.07.2024 18:37
      +2

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

      Ага. Щас.

      С чего такое мнение? Через задницу написанный код хоть замикросервисируйся - стартанёт когда стартанёт и то если повезёт и не будет взаимной зависимости кольцевой.

      Не помогут микросервисы. Не помогут. Если хватит квалификации написать их, то и на монолите у этого же человека(команды) не будет проблем.


  1. brownfox
    01.07.2024 18:37
    +4

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

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

    Ноги у популярности микросервисов, разумеется, растут из веб-программирования, как и многие другие не совсем адекватные вещи. За последние годы я не раз сталкивался с последствиями такого подхода, разгребая чужие legacy-решения. В одном случае автор для решения достаточно простой задачи нагородил 6 микросервисов с отдельными БД (да-да, есть ещё секта противников "монолитных" баз). В другом программа просто не работала в контуре заказчика, изолированном от интернета, потому что "ну всегда же можно указать ссылку на фреймворк".

    Так и живём.


    1. Captain_Jack Автор
      01.07.2024 18:37
      +4

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

      Сам делал так же, пока не разобрался в теме. Помню как один раз вкорячил 4 сервиса там, где прекрасно работал бы и 1, потом пришлось переделывать.

      Поколение новичков, выбирающих по наитию, никогда не иссякнет, но надеюсь, что хотя бы пройдет этот консенсус в индустрии, что делать микросервисы - это стильно-модно-молодёжно, и вообще уйдет эта ложная дихотомия "монолиты против микросервисов". Может тогда будет меньше ориентира на хайп. Хотя кого я обманываю :)


    1. persona
      01.07.2024 18:37

      6 микросервисов с отдельными БД (да-да, есть ещё секта противников "монолитных" баз)

      Но микросервис же обычно подразумевает использование своей БД, куда только этот микросервис имеет доступ. Иначе зачем тогда этот микросервис нужен?


      1. brownfox
        01.07.2024 18:37
        +1

        Иначе зачем тогда этот микросервис нужен?

        Нужен он для выполнения некоторой относительно простой функции, как обычно и заявляется в концепции микросервисов :)

        Микросервис не требует наличия собственной БД, он может вообще не обращаться к базе или работать с отдельной схемой централизованной БД. Или даже без отдельной схемы.

        Разделение данных по владельцам с их передачей через API - совершенно отдельная идея, родившаяся, IMHO, в головах людей, которые не понимают, как готовить Оракл озабоченных в большей степени производительностью сервисов на выдачу данных. Распределение информации по кэшам в виде микросервисных БД именно эту проблему решает.

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


        1. persona
          01.07.2024 18:37

          Конечно микросервис может не использовать никакую БД, я подрузамевал что если она нужна.

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

          В целом тогда нужно проводить разделение между сервисами и микросервисами в составе сервиса. Сервис должен владеть своими данными. Микросервисы могут соответственно могут шарить общие данные. Но другие (микро)сервисы могут иметь доступ к данным этого сервиса только через его API, без прямого доступа к данным.


  1. sse
    01.07.2024 18:37

    "Можно писать на любом языке" и "2-pizza team" это все selling points для недоверчивых технарей, а я расскажу, почему на самом деле бизнес выбирает микросервисы. Причин всего две:

    1. Быстрая проверка гипотез, которые в 9 из 10 случаев проваливаются - да, продакт-менеджеры тоже не боги. В случае монолита вы стоите перед непростым выбором: или вкладываться по полной в эксперимент (дизайн, код, тесты), который, скорее всего, будет выброшен, или на коленке слепить то, что рискует выстрелить, и тогда с этим поделием придется жить вечно в монолите. Какой вариант хуже? Оба хуже. В случае с микросервисами эксперименты не сильно трогают ядро системы, которая кормит всю компанию и их семьи.

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

    Эти две причины пересиливают (как будто бы) все сложности, связанные с МСА. А сложностей немало, от оркестрации до смены парадигмы владения системами.

    Все, кроме этих двух причин на самом деле ни зачем не надо. А, еще маленькое замечание: проще нанимать людей, потому что "у нас такие же крутые технологии, как в Гугле (так что мы будем платить поменьше)".


    1. Drucocu
      01.07.2024 18:37

      Быстрая проверка гипотез

      Решается не микросервисами, а специальными инструментами: будь то отдельная инфраструктура для AB-тестирования или feature toggle. То же самое может быть организовано и в монолите. По вашему что, под каждую гипотезу пишется свой микросервис, а потом дропается?

      в случае микросервисной архитектуры можно нанимать вообще любых программистов

      у нас такие же крутые технологии, как в Гугле (так что мы будем платить поменьше)

      Какие-то влажные фантазии. Наверное, поэтому зарплаты Golang (основной язык для этих ваших микросервисов) в ТОП-5 на бекенде?

      Кажется, ваш обидел какой-то адепт микросервисов и вы теперь вымещаете зло на всех. Не стоит: человек может быть просто не очень, независимо от того, на чём пишет.


  1. tuxi
    01.07.2024 18:37
    +4

    Спасибо что не побоялись сказать то, что у многих уже накипело.


  1. sergey_prokofiev
    01.07.2024 18:37

    Самый важный аспект упустили: микросервисы deploy fast. Это и хорошо и плохо.

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

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

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

    Аналогично, облегчается задача 0-downtime deployment для асинхронных воркеров: в ряде случаев их можно передеплоивать с даунтаймом, при условии гарантированной доставки им сообщений.

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

    В случае с монолитом все внутренние контракты входят в одну единицу деплоймента, соотвественно поменяли, задеплоили.

    В случае с микросервисами, это не так. У каждого микросеврсиа свой релизный цикл. И если команда А поменяла контракт, то у команды Б(которая вызывает микросервис команды А), свои планы, свои задачи и тд. У команды В, которая тоже дергает микросервис команды А - своя история. И на практике команде А надо поддерживать 2 версии контракта, старую и новую, пока клиенты не перейдут на новую. Это может занять время, и новое изменение контракта уже в планах... 3 версии....

    There is no silver bullet к сожаление.


    1. janvarev
      01.07.2024 18:37

      В случае монолита очень сложно сделать быстрый релиз: ведь в той же единице деплоймента(монолит) находятся текущие изменения остальных 4х команд.

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

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

      А если у вас контракты постоянно меняются, то проблемы будут что на монолите, что на микросервисах.

      В любом случае упор в качественную архитектуру с хорошей изоляцией и контрактами.


      1. sergey_prokofiev
        01.07.2024 18:37

        потому что дотестировать изменение контрактов не нужно

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


        1. janvarev
          01.07.2024 18:37

          Текущая версия монолита пересобирается с обновленными библиотеками (которые пилятся разными командами как раз) и деплоится.


          1. sergey_prokofiev
            01.07.2024 18:37

            Тогда надо делать полную регрессию, это вам любой QA скажет.


            1. janvarev
              01.07.2024 18:37

              Что мне скажет QA, меня мало интересует. Меня интересует, будет работать или нет.

              Если вы фиксите багу без изменения контрактов - можно перезапустить что сервис, что передеплоить монолит - почти без последствий, и в большинстве случаев будет работать (и на практике так и делают для хотфиксов)

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


  1. mivlad
    01.07.2024 18:37

    А дальше никто не запрещает эти N модулей начать деплоить в M артефактов вместо 1. Если вы не нарушали границы между модулями, то просто пошли, обмазали это всё контроллерами или кафкой, поменяли конфиг сборки - и готово.

    Звучит так, словно с вызовов на кафки перейти раз плюнуть :) Хотя при проектировании изначально под асинхронные обмены ряд решений и даже базовых понятий могут оказаться совсем другими.


  1. panzerfaust
    01.07.2024 18:37
    +5

    По какой-то совершенно неизвестной мне причине первое часто приравнивается ко второму, хотя это как сладкое и мокрое, вообще разное

    Это называется плохая репутация.

    У нас тут на хабре олды с 20+ лет опыта любят рассказывать, как божественно они писали 10-15 лет назад. А мой личный опыт таков, что самый всратый код - это как раз с культурного слоя "конец нулевых, начало десятых" и написан он нынешними архитекторами и си-грейдами. Причина проста: не хватало еще коллективного опыта плюс над ними не было старших товарищей с этими же 20+ годами. Если говорим про джаву, то в нулевых годах это был еще молодой язык с растущей экосистемой. Писали кто во что горазд.

    Следующее поколение пришло, взглянуло на эту хтонь, ужаснулось и выработало ассоциацию "монолит == говнокод, только микросервисы, только хардкор". Говнокода в микросервисах тоже хватает, но опять же по моему опыту, работать с ним не настолько больно. Потому что у этого поколения уже есть опытные старшие товарищи с живительным подзатыльником.

    Новое поколение приходит и видит данную дискуссию монолит vs микросервисы. Надеюсь, они будут допускать еще меньше идиотских ошибок.


    1. tuxi
      01.07.2024 18:37

      Не бывает абсолютно идеальной архитектуры. И близкой к идеальной не бывает. Идеальность всегда завязана на текущие доступные вычислительные ресурсы / сетевые возможности. Java середины-начала нулевых (да, я оттуда) это весьма некислые цены на выделенные сервера и толщину канала. И если стоит задача обеспечить 300...500 rps при достаточно сложных требованиях от бизнеса, то будет использована именно та архитектура, которая это обеспечит.


  1. SellerOfSmiles
    01.07.2024 18:37
    +2

    Мало нам было разработчиков которые спорят как нужно правильно писать код вместо того чтобы хоть что-нибудь написать. Так теперь ещё об'явились не_разработчики (судя по посылу) которые указывают разработчикам как им правильно думать на тему (и без), и о чем теперь модно спорить...


  1. amazingname
    01.07.2024 18:37
    +1

    Вот эти два пункта ИМХО и есть основная причина по которой нужны микросервисы:

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

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

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


  1. kai
    01.07.2024 18:37

    Когда у тебя несколько монолитов. Они становятся микросервисами. Автор, конечно, смешал в кучу всё на свете.


  1. ggo
    01.07.2024 18:37
    +2

    В целом с большинством тезисов согласен.

    Только один комментарий. Микросервисы - это не про технологии, микросервисы - это про бизнес, про способ организации работы большого количества разработчиков. И основной значимый критерий сервис микро или нет - одна ИТ команда (5-7 человеков) полностью отвечает за сервис, от разработки до сопровождения. Работает плохо этот сервис? Только эта команда его чинит.

    Является ли микросервис - подой в кубере или OSGI-модулем в большом java-приложении - в общем случае неважно. Хотя в экстремуме микросервис должен обладать своими собственными ресурсами, которые никакой другой сервис забрать не может.


  1. dezahrise
    01.07.2024 18:37

    Вот тут 1 момент упущен. 90% предприятий, 60-70% занятости и 50% ВВП во всем мире приходятся на долю мелких и средних предприятий. А там никакой дискуссии нет - практически 1 монолит.


  1. SnowBearRu
    01.07.2024 18:37

    Я бы еще в статье упомянул https://ru.wikipedia.org/wiki/Сервис-ориентированная_архитектура вот это. На мой взгляд это завязано темой.

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


  1. Mr_Cheater
    01.07.2024 18:37

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

    Ну, например, те же зависимости - в js, хотя бы.