Комментарии 237
Почему нет?
И что? Если это не главная задача то наверное нагрузка на рендер будет меньше чем на все остальное. Особенно когда посетителей не более тысячи в час.
Вместо этого логично отсадить рендер на отдельный сервер и рендеринг ставить в очередь. И даже если он встанет раком, это никак не повлеяет на работу всего сайта.
Очевидно что тяжёлые задачи надо ставить в очередь и выполнять асинхронно в другом потоке. То что для любой тяжёлой задачи нужен отдельный сервер — вовсе не очевидно. Если сервер станет захлёбываться логичней звучит добавить идентичных серверов.
Пользователь загрузил 20-и минутный видео файл в качестве 1024p. Его нужно предобразовать в видео меньшего разрешения: 720p, 480p и 360p. Какой алгоритм будет в монолитном приложении?
Для определенности предположим, что вы делаете клон YouTube, но с посещаемостью на порядки меньше. Вам интересно как бы это выглядело на монолите?
Как бы сделал я:
0) Балансировщик на входе — fabio/Traefik/nginx.
Так как для надежности и горизонтального масштабирования — все это и в случае монолитной архитектуры должно уметь работать на нескольких серверах в нескольких инстансах.
Это что для монолита, что для микросервисов нужно.
1) БД для метаданных видеофайлов, профилей пользователей, сессий, комментариев.
Возможно, одна СУБД, если проект невысокой нагрузки.
Или несколько СУБД различной специализации, например, более часто запрашиваемые сущности такие как сессии — в Tarantol/Redis, комментарии в Mongo (тут пригодится ее умение масштабироваться сравнительно легко, ну а то, что платой за это является у Монги консистентность не строгая, а типа «когда-нибудь потом» — это не важно для комментариев).
Необходимость реляционной СУБД для такой простой задачи под вопросом. Ну, разве что, для строгой консистентности профилей пользователей, если, например, у вас платный доступ. Допустим, возьмем PostgreSQL.
Все экземпляры всех видов СУБД — разумеется реплицируются.
Выделение Tarantool/Mongo/PostgreSQL позволит сделать удобную репликацию master-master для Tarantool/Mongo (Тарантул точно умеет, Монго, надеюсь, не помню точно). Это снимет нагрузку с PostgreSQL, так как он заметно хуже работает на большом количестве экземпляров (а тем более по сети) и позволит обойтись для PostgreSQL более мягким master-slave.
Что для микросервисов, что для монолита — эти СУБД будут.
2) Бэкенд. Вполне себе монолит. Большой и единый.
3) Фронтенд.
Это не монолит и не микросервис. Он запускается все равно не на наших серверах, а у пользователей. С точки зрения сервера — это небольшие статические файлы, раздать их не проблема.
4) Модуль конвертации. Маленькая узкоспециализированная утилита. Даже какой нибудь ffmpeg годится.
С небольшой специально написанной обвязкой, например, подписанной на очередь и запускающий ffmpeg.
Или вообще без обвязки специальной — тот же Nomad из Hashicorp вполне годится, имхо. Его же можно для deploy новых версий бекенда использовать. Его же можно использовать для запуска ffmpeg
Что для монолита, что для микросервисов — все тот же ffmpeg и все та же обвязка.
5) Сервер очередей. Для более гибкого управления множеством инстансов.
Чтобы не лазать в БД на каждый чих, чтобы все экземпляры бэкенда и все экземпляры конвертера были просто подписаны на события.
Это нужно и для монолита и для микросервисной архитектуры.
6) cloud storage работающее по протоколу S3 или OpenStack Swift
Это нужно и для монолита и для микросервисной архитектуры.
7) Кэш. Поскольку у нас есть очередь с подписками и мы сможем легко инвалидировать кэш — то годится и локальный для каждого бэкенда кэш или даже кэш, встроенный в бэкенд (это будет очень быстро).
Инвалидировать локальный внешний кэш можно из бэкенда-монолита, внешняя обвязка для этого не нужна. Все равно кроме бэкенда-монолита в него никто лазать не будет.
Для микросервисов такой вариант тоже имеет смысл — в терминах Kubernetes кэш будет размещен в одном поде с использующими его микросервисами.
Впрочем, не локальный, а общий для всех экземпляров монолита/микросервиса кэш тоже имеет право на жизнь. Но это будет медленнее, чем локальный. Нужно по профилю нагрузки и по имеющейся оперативной памяти на серверах смотреть что выбрать — локальный или общий.
— Имеем множество экземпляров бэкенда-монолита и множество экземпляров конвертера.
Опционально добавляем:
8) Если бюджет позволяет более серьезно относится к диагностике — централизация сбора логов и метрик. Логи нужны всем. Метрики микросервисам важнее.
9) Если нагрузка по закачке на наш сервис новых файлов более серьезна то выделить из монолита один компонент, занимающийся загрузкой файлов. Разумеется, тоже несколько экземпляров.
Для многопоточных бэкендов Erlang или Go (golang) нет необходимости выделять этот компонент сразу. Для NodeJS с ее однопоточностью — принципиально выделить сразу, даже при небольшой нагрузке на upload новых файлов.
Итого имеем балансировщик, СУБД и MQ-сервер и cloud storage и кэш — обязательные как для монолитной так и для микросервисной архитектуры.
Варианты с монолитом:
1) Монолит бэкенда + конвертер.
2) Монолит бэкенда + конвертер + модуль upload
С сохранением все того же масштабирования при нагрузке, что и у микросервисов, причем можно масштабировать отдельно нагрузку и на инстансы бэкенда-монолита и на инстансы конвертера и на инстансы загрузчика.
Конечно модуля upload и конвертер — легковесны и обладают частью признаков микросервисности, но все решение — однако все решение в целом все же не является микросервисной архитектурой, имхо. Максимум — просто сервисной. Без «микро-».
В микросервисной архитектуре, имхо, компонент десятки и сотни и обязательно нужен дополнительный инструментарий для поддержки этого роя микросервисов.
Впрочем, учитывая особо выделяющийся по объемности функционал компонента бэкенда-монолита — все же, полагаю, что это монолитная архитектура. С использованием отдельных помощников бэкенду — MQ, cache, конвертер.
Тут товарищ в соседней ветке предлагает делать правильный монолит — 1 приложение и много потоков.
Тут товарищ в соседней ветке предлагает делать правильный монолит — 1 приложение и много потоков.
Не обязательно.
Для отказоустойчивости и для горизонтального масштабирования монолит все равно нужно запускать в нескольких инстансах на разных серверах.
Ничем тут от микросервисов не отличается.
Если позволяет выбранная технология — будет у вас один инстанс обрабатывать и 10 000 потоков. Не позволяет — просто будете запуском дополнительных инстансов этот недостаток компенсировать.
И причина, по которой вы запускаете дополнительные инстансы — не важна.
Неважно что это будет необходимость утилизировать все ядра процессора, если выбранный инструмент этого не умеет (если использует только 1 ядро, а на сервере их 16 — в этом случае вы запустите 16 инстансов на одном сервере). Или неважно, что это будет необходимость обеспечить обработку достаточного числа потоков (если один инстанс обрабатывает 1 000, а вам нужно 100 000, то вы просто запустите 100 инстансов)
Важно другое — вам нужно обеспечить корректную совместную работу большого количества инстансов (например, используя: репликации, общие сервера очередей и пр.).
Это важно и для монолита и для микросервисной архитектуры.
Давайте рассмотрим чистый монолит (сам конвертирующий видео файлы, сам же обрабатывающий запросы от пользователей) на примере ситуации, описанной ppl2scripts в habr.com/post/427215/#comment_19272331
Потому что завтра нужно будет отрендерить миллион превьюшек. Хорошо 50 тысяч. Тысячу.
Ситуация с монолитом не отличается от ситуации с микросервисами.
Определимся с вашей политикой использования вычислительных ресурсов. Что для монолита, что для микросервисов это будет одно и то же:
- Или по мере освобождения вычислительных ресурсов, выделенных только под длительные операции. Таким образом, под трудоемкие операции выполняются только на определенном выделенном пуле вычислительных ресурсов.
- Или по мере освобождения вычислительных ресурсов от других операций. В этом случае, фактически размер доступного пула динамически меняется в зависимости от нагруженности по прочим операциям
- Или автомасштабированием — добавлением вычислительных мощностей, выполнением операции, освобождением вычислительных мощностей.
- Или комбинацией этим методов.
Пусть, для простоты, автомасштабированием без лимитов — простой способ получить производительность, но дорогой метод (сервера денег стоят):
- В схеме бэкендом+конвертер мы просто запустим конвертер 50 000 раз.
- Для бэкендов ничего не изменится.
- В схеме с единым бэкендом — мы начинаем конвертацию, тем самым количество бэкендов, доступных для обычных запросов пользователей, сокращается. При обработке очередного запроса от пользователя обнаруживается, что обрабатывать запрос некому, так как свободных бэкендов нет. Просто запустим новый бэкенд. Очевидно, что при сохранении нагрузки придется запустить под быстрые запросы 50 000 дополнительных бэкендов, взамен тех, что длительное время будут заняты конвертацией. Если ваши бэкенды долго инициализируются это имеет смысл сделать заранее, одновременно с запуском конвертации. Или предусмотреть пул «горячих» свободных бэкендов
- Рассмотрим ситуацию, когда конвертировать вообще ничего не надо. Но обычные быстрые запросы не успеваем обрабатывать. Просто запустим новый бэкенд. Ничем не отличается от предыдущего пункта в плане автомасштабирования. Только то было «автомасштабирование по причине того, что бэкенды заняты конвертацией». А это «автомасштабирование, так как бэкенды не успевают обработать обычные быстрые запросы». Но не отличается ровно ничем — ответ на недостаток ресурсов для обработки это запуск нового бэкенда
Но почему это произошло?
Если у меня год подряд было 1 000 в день, то вероятность того, что завтра будет 50 000 в день — крайне незначительна. А вероятность того, что будет миллион = 0.
Если такое случится, видимо, теперь мы мега-популярны и мега-посещаемы?
Так что следующий этап:
Инвесторы/владельцы будут только рады доплатить разработчикам за сверхсрочную работу, чтобы они вынесли операцию конвертации в отдельно запускаемый и отдельно масштабируемый модуль.
А пока разработчики оптимизируют систему инвесторы/владельцы будут рады заплатить дополнительные деньги за сервера, которые держат нагрузку по приведенной выше схеме с бэкендом-монолитом.
Платить за это заранее готов далеко не каждый.
А вдруг не будет не то что миллиона, а и 50 000 не будет никогда?
Сделать сразу «как у Фейсбука и Гугл» — это ж разница совсем не в 20% по стоимости работ. И по срокам тоже дольше.
А скорее всего для начала сгодится компромиссный вариант, описанный тут habr.com/post/427215/#comment_19276749
Монолиты гораздо эффективнее по производительности, чем микросервисы на огромном числе задач. При меньшей стоимости и большей скорости разработки.
Вот пример примитивнейшей ситуации:
habr.com/post/282299
«Кэш внутри приложения» vs «Кэш рядом, в соседнем отдельном процессе».
А ведь в микросервисах, все еще хуже. Там и сетевые задержки. И переходов между процессами/модулями/сервисами куда как больше. И дополнительных сериализаций/десериализаций полным-полно. И пр.
Надо смотреть по конкретной задаче.
Ну вот пример очень даже посещаемого проекта:
habr.com/company/oleg-bunin/blog/418235
Как было раньше (со старта Авито до 2015 – 2016 годов): мы жили в условиях монолита, с монолитными базами и монолитными приложениями. В определенный момент эти условия стали мешать нам расти. С одной стороны, мы уперлись в производительность сервера с главной базой, но это не основная причина, так как вопрос производительности можно решить, например с помощью шардирования.
Как они пишут производительность можно было решить и с монолитом. А ведь это один из самых посещаемых сайтов в РФ, входит в первую десятку rg.ru/2017/09/28/nazvany-samye-populiarnye-sajty-v-rossii.html
Если вы не Гугль, то, вполне возможно, гораздо оптимальнее для вас будет не микросервисы, а всего лишь 2-3-4 крупных сервиса. Главное, чтобы они были способны работать на множестве запущенных инстансов для горизонтального масштабирования.
А возможно оптимальнее будут именно микросервисы.
А возможно у вас такое финансирование, что вы можете на него и продукт свой сделать 5-ю способами, чтобы выбрать лучший, и два Kubernetes'а в придачу. Всяко бывает.
Надо смотреть по конкретной задаче.
Я один не считаю такое приложение монолитом? Для меня это SOA
Зачем замыкаться в формальных определениях. Ведь чистых монолитов и не бывает.
Кроме самого монолитного приложения — всегда есть СУБД, часто есть кэши, и в отказоустойчивых системах с несколькими инстансами как правило есть MQ.
Приведенный пример точно не микросервисы, а скорее ближе именно к монолиту.
Откуда по вашему взялись «очевидные места»?
Из опыта
Не хотеть говнокодить и не говнокодить — две большие разницы.
Также можно сказать про ООП, ФП и другие подходы
Конечно можно
А может просто кто-то не хочет говнокодить?
Как говнокод связан с микросервисами? Микросервисы — это про архитектуру. А как она реализована — это уже другой вопрос. Говнокод с равной вероятностью может быть как в монолите, так и в микросервисах.
ООП и ФП это попытки справится со сложностью декомпозицией на меньшие куски.
Если микросервисы это единственный метод для декомпозиции, то получится как на
У меня есть небольшая гипотеза, возможно объясняющая часть таких людей.
Микросервисы крайне хорошо заходят на чрезвычайно крупных проектах. На таких проектах большое количество разработчиков. Причём за пару лет ещё и происходит ротация разработчиков.
Часть этих разработчиков уходит в проекты помельче. И начинают применять практики, которые, как они видели, работают.
По моему опыту, микросервисы хороши там, где задачи хорошо параллелятся и скоростью коммуникации между сервисами можно принебречь: один сервис что-то сделал, положил задачу очередь, второй подхватил, что-то сделал, передал дальше и так далее…
Если же коммуникация между «сервисами» должна быть исключительно быстрой, лучше сделать монолит… У меня был опыт, когда в хорошем продукте всё равно пришлось несколько сервисов слить в монолит, потому как так работало лучше…
Микросервисная архитектура строится по-другому, там монолиту просто нет места.
Как мне кажется, описанный пример имеет мало общего с микросервисами. Конечно, очень трудно тут проводить чёткую границу.
Но это больше похоже на балансировку нагрузки, нежели на способ обеспечения независимости разработки.
Вот у меня есть монолитное API-приложение с in-memory database (это просто пример, ок :-)).
Я заменяю in-memory database на чужую внешнюю бд, которая имеет свой протокол обмена данных и может хоститься на отдельном сервере. Приложение перестало быть монолитом?
Я добавляю к приложению чужой внешний кэш, который имеет свой протокол обмена данных и может хоститься на отдельном сервере. Приложение перестало быть монолитом?
Я добавляю к приложению чужой фулл-текст (эластик, к примеру), который имеет свой протокол обмена данных и может хоститься на отдельном сервере. Приложение перестало быть монолитом?
Я добавляю к приложению балансировку нагрузки на одинаковые инстансы, которые не имеют возможности обмениваться данными, но могут хоститься на отдельном сервере. Приложение перестало быть монолитом?
Я добавляю к приложению сервис авторизации, который выношу на отдельный сервер и который может обмениваться с основным приложением. Приложение перестало быть монолитом?
Да, на мой взгляд, это уже будет сложно назвать монолитным приложением.
А что, если у меня всего этого нет, потому что не нужно? Ладно, не суть
Вы не ответили ни на один мой вопрос. Но в вашем ответе прослеживается явное нежелание называть монолитом приложение использующее стороние сервисы. Продолжая ваши рассуждения, использование чужой внешней бд тоже не дает возможности называть приложение монолитом.
Я так не играю. Извините, но имхо, то, что вы описываете далеко от общепринятого понимания микро/нано-сервисов.
Ответ «Да, монолит» со стороны пользователей вашего API-приложения — для них ваше API-приложение условно «чёрный ящик» и внешне он самостоятелен и независим.
Ответ «Нет, не монолит» должен быть с вашей стороны т.к. вы знаете строение этого «Чёрного ящика», а следовательно для вас он уже либо «Белый ящик», либо «Серый ящик».
Посмотрите на следующую цепочку: Монолит > SOA и Микросервисы > Библиотеки и Модули > Классы и функции.
Все элементы цепочки объединяет одно — концепция деления большего на меньшие части (для широкого спектра задач от облегчения поддержки крупного приложения, оптимизации узких мест до переиспользования каких-то состовляющих в других приложениях). Замечу, что Микросервисы являются вариацией SOA.
Рассмотрим ваше API-приложение:
На внешнем уровне действительно может выглядеть монолитом, а по началу и быть монолитом.
Но вот пришло время и вы начали использовать внешнюю бд (не обязательно чужую) или внешний кеш или внешний фулл-текст (эластик, к примеру) или внешний сервис авторизации.
Как вы могли заметить у всех вариантов ключевое слово «внешний» и это ключевое слово не обязательно означает, что решение является «внешним» только по тому, что расположено на другой машине, нет «внешний» именно по тому, что является отдельным самостоятельным решением (сервис/приложение) с собственной кодовой базой, жёстко не связанной с вашим приложением (то что для работы вашего приложения они необходимы не означает, что ваше приложение необходимо для работы этих решений).
Так или иначе вы уже обращаетесь к другим сервисам даже если они физически на одной машине. И вот теперь ваше приложение относится к SOA (не обязательно микросервисная архитектура, но сервисная точно), и не является монолитом т.к. уже не выполняет полностью от и до весь заявленный функционал: хранение/поиск/авторизация уже выполняются внешними сервисами/приложениями. А ваше приложение представляется фасадом с набором собственной связывающей бизнес логики.
К слову вариант с вынесением авторизации в отдельный сервис из монолита будет происходить быстрее и проще, если «монолит» будет не совсем монолитом, а скорее модульным приложением. Всё-таки довольно легко независимый и унифицированный модуль вынести в отдельный сервис. Чем пытаться распутать жёстко связанный спегетти код особенно, если приложение уже стало огромным.
Вообщем всё это делается в целях простоты поддержки будущего развития приложения. В любом случае для конечных пользователей любая такая архитектура видна скорее не будет, а соответственно для них приложение будет являться монолитом.
Если рассматривать ваше приложение, не обращая внимание на внешние свервисы, а заостряя внимание чисто на бизнес логику/код вашего приложения и только вашего приложение, и если оно не построено по модульной системе, то конечно же оно монолитное. Однако учитывая внешние сервисы, ваше монолитное приложение уже крутится в SOA.
Просто всё зависит от того как близко рассматривать всё решение. Невооруженным глазом одно, под микроскопом другое, под микроскопом рассматривая примитивную единицу третье.
Вкратце, хочется просчитать все моменты и заложить немного наперед. Но это очень сложно (невозможно). Хотя это, конечно, не значить, что не нужно думать вообще.
Замечательная статья.
Для меня удивительно, что в статье не упомянут важный момент: микросервисы вносят дополнительную сетевую задержку. Для мелких проектов этот недостаток реально трудно перекрыть.
Еще хуже, что при неправильной декомпозиции (а на старте она будет скорее неправильной, чем правильной) приложение будет напоминать пинг-понг: множественные пересылки данных между микросервисами в рамках исполнения одного api-запроса.
Вы верно вспоминаете о том, что бд/кэш/elastic тоже вводят дополнительные сетевые заддержки.
Но есть некоторая разница. Написание собственной бд (и всего остального) в качественном виде — практически неподъемная задача для большинства. Не говоря уже о том, что использование общепринятого решения во многих случаях куда лучше велосипеда. Это приемлемая пеня. Тем не менее, для мелких (с точки зрения количества пользователей и объема данных) приложений идея держать все на одном сервере вполне имеет смысл.
Но даже, если ваши микросервисы (или приложение, и бд) крутятся на одном сервере, то вы все равно имеете довольно впечатляющую задержку.
Для того, чтобы сравнивать микросервисы и бд в этом плане, нужно показать, что микросервисы имеют такие же преимущества.
Но есть еще один важный момент. Вводя микросервисы вы увеличиваете длину цепочки пересылки данных. Причём в худших случаях, если перестать следить, то цепочка увеличится не на один микросервис, а на кучу.
В статье говорится про то что нужно сначала делать монолит. Но это слишком дорого для начинающего проекта, нужно держать большой сервер, с запасом. Вместо этого было бы лучше разделить приложение на несколько частей, чтобы иметь возможность масштабировать их по-отдельности.
Я уверен что любое приложение можно на начальном этапе разделить как минимум на 2 части:
— с состоянием
— без состояния
На часть без состояния обычно приходится основная нагрузка. И эту же часть легко масштабировать. Для этого, например, в AWS/Azure существуют готовые решения: auto scaling groups / scale sets. Это позволяет держать 1 меленький сервер, а в пиковые нагрузки запускать дополнительные.
Если вы испытываете проблемы с объемами бд, то у вас есть возможность сделать шардирование. На крайняк, вы можете разбить бд руками на независимые бд.
Если вы испытываете проблемы с поступающей нагрузкой, то у вас есть возможность поднять кучу инстансов и сделать балансировщик нагрузки. Многие приличные бд имеют возможность создавать read replica. Вы также можете вынести отдельные тяжелые операции в фон на отдельный сервер с помощью очередей, если это возможно (если что, это вовсе не микросервисы).
Почему описанные стратегии не подходят?
Вы также можете вынести отдельные тяжелые операции в фон на отдельный сервер с помощью очередей, если это возможно
Но это же уже будет не монолит?
если что, это вовсе не микросервисы
еще раз, я не говорил про микросервисы, я имел в виду минисервисы.
Но это же уже будет не монолит?А у меня, к примеру, общая кодовая база, но разные точки входа. Одна точка входа слушает API-запросы, другая точка входа слушает очередь. Вполне себе монолитище.
еще раз, я не говорил про микросервисы, я имел в виду минисервисы.Разница только в объеме кода или есть еще какая-то разница?
Что в таком случаи делать?
Либо делать оверхед одиночному монолиту, чтобы не переписывать когда их будет два? Или всё же переписывать когда надо будет?
Мне кажется логичней сделать микросервис(пусть они и не очень микро, но и не монолит) который принимает запросы, а второй их разгребает в правильном порядке. Тогда запись в базу нельзя назвать оверхедом.
Монолит — скорее всего не изолированный код, который скорее всего не может деплоиться независимо.
Если вам кажется глупым складировать сообщения в очередь в рамках монолита, то это скорее ваша проблема, чем проблема концепции монолита. Если есть требования, которые требуют очереди, то почему бы и не юзать очереди? Если вам для оправдания очереди приходится подключать еще что-то кроме требований, то это ваша проблема.
Кроме того, вы рассматриваете понятие «оверхед» только в контексте «мне почему-то кажется эта операция ненужной». Но если операция реально нужна, то это не оверхед, а просто неприятность.
И по сути БД или MQ это микросервис.
Оверхед тут в том, что приходится использовать лишнее хранилище, хотя если это было одно приложение то можно было бы эти данные хранить и сортировать в памяти, без использования очередей.С очередью в памяти очень трудно добиться либо гарантий persistance, либо гарантий exactly-once. Все еще оверхед?
И по сути БД или MQ это микросервисВы уверены, что БД можно считать микросервисом? Просто одно из свойств правильно приготовленных микросервисов заключается в том, что можно делать независимое обновление и деплой микросервисов.
Вообще мне этот комментарий доставляет. Такое впечатление, будто вы думаете, что микросервис — это модуль, общающийся по сети.
Сделать несколько копий монолита возможно только для очень простого монолита. Я даже таких приложений и не встречал.
их сильно задевают.
Но вы мне скажите один момент. Микросервисы в общем-то недавняя тема (если смотреть по взрыву популярности). Как до этого момента люди делали сложные приложения?
И продолжают так делать. Может вы скажете что Facebook, Google, VK — это монолитные приложения?
Пожалуйста, забудьте учебник демагогии. Это реально раздражает.
То, что, внутри этих компаний может быть применяются микросервисы еще не значит, что нет сложных монолитов.
Хорошо, мы уже пришли к сервисам. Вы уверены, что все сложные приложения делались на основе сервисов?
Хорошо, давайте зайдем с другой стороны. Как разрабатываются сложные приложения вне веба? Тоже сервисы/микросервисы?
Ага. Например, ядро линукса, движок v8, или компилятор достаточно сложного языка.
И от сложности кода тут ничего не зависит. Разве что у вас такие сложные запросы к БД когда две копии одного приложения будут драться за блокировку таблиц например.
Почему? Если с размахом подойти к делу, то можно сколько угодно серверов занять. На первых трех — кластер бд, мастер и два слейва на чтение. Еще на шестерых запустить по одному экземпляру монолита. Ну и последний отдать под nginx — балансировать нагрузку. Вот и десяток серверов заняли. И это еще не упомянули ни кеширование (+ 1 сервер под редис?), ни разбивку таблиц на части…
upd: Опоздал с сообщением, выше уже всё написали.
Личный опыт наблюдения за неправильными монолитами не значит, что большинство монолитов проектируются рукожопами.
Т.е. вы утверждаете что любую задачу можно решить монолитным приложениемНе-а, мы вообще не об этом говорим. Однако то, что монолит не всем приложениям подходит, вовсе не значит, что монолит нужно списывать в утиль.
А если этого нельзя, то приложение сделано рукожопами?Строго говоря, у разработчиков монолита могли быть весомые причины не добавлять возможность запуска на произвольном количестве серверов. Однако в таком случае и о микросервисах говорить бессмысленно.
Так что в рамках выбора монолит/микросервисы мы имеем, что такое монолитное приложение спроектировано рукожопами.
Если в общем — бывают различные ситуации, но в большинстве случаев можно спроектировать монолит так, чтобы он горизонтально масштабировался.
Секрет прост — он должен быть стейтлесс. Всё, что имеет статус — вынести отдельно, не хранить в памяти приложения. О данных позаботится постгрес. О пользовательских сессиях и кешировании — редис. О доставке сообщений о фоновых задачах — один из десятков существующих систем очередей, хоть тот же редис использовать.
Единственное, с чем могут быть реальные сложности — пользовательские файлы. Хранить их в бд (постгресе или монге) — нерационально. S3 может быть слишком дорого. И вот лежат они на диске, рядом с монолитом, и никак второй сервер сюда не присобачишь. Но даже тут есть варианты, начиная от запуска облачной файловой системы — от клона S3 до какого-нибудь seaweedfs, написанного на go.
Понятное дело, что можно начать придумывать, типа, запускать таймер только на 1 инстансе, запускать в 10 раз реже и т.п.
Но не проще ли сделать отдельное приложение для этого?
В таком случае я могу свободно нарекать себя разработчиком в проектах на микросервисной архитектуре. Но боюсь, на собеседовании меня не поймут
Хочу отметить, что при разделении приложения таким образом мы вовсе не обязаны разделять кодовую базу. Особенно если хотим шарить общую бизнес-логику. В таком случае код куда ближе к монолитам.
Основные трудности которые есть в монолитах, есть и в микросервисах. Более того, микросервисы привносят чисто свои проблемы — падение производительности, большая сложность отладки, большая сложность инфраструктуры и развертывания.
Отторгнуть такие квазимикросервисы из монолита вовне не составит проблемы, но есть ли в этом смысл — это зависит от контекста задачи. При отторжении мы естественно получаем необходимость выстраивания взаимодействия с «потребителями», ранее решаемое платформой, на которой крутится монолит, а как бонус — получаем возможность вынести сервис на другие серверные мощности, возможность использования сервиса другими приложениями и определенную свободу по изменению внутренних механизмов сервиса, не оказывающих влияния на интерфейс взаимодействия, а так же возможности более легкого масштабирования и резервирования сервиса.
Зря ведёте дискуссию.
Ваш оппонент плавает в темах, что монолита, что микросервисов.
Причём ужасно плавает.
Понятное дело, что блог, онлайн магазин, форум, нет смысла делить на части. Если сложнее этого вы ничего не делали, то спор действительно бесполезен.
К примеру, я учавствовал в разработке серьезных проектов. Монолит нисколько не мешал. Но откуда вам знать, что я не соврал?
Зы: вы же не думаете, что люди защищающие монолит защищают отсутствие модульности?
Да, я не эксперт в микросервисах, я никогда с ними не работал и по-этому стараюсь их не упомянать. Но что такое монолит я представляю.
1) вы не представляете как масштабировать монолит;
2) сами не имеете практики участия в проектах с архитектурой похожей на микросервисную.
Вы не находите в этом ничего смешного?
А вы имели дело с микросервисами? Несмешо, если нет.
Пока мой потолок по нагрузке в карьере это 7млн уникальных посетителей в сутки (у хабра меньше, существенно меньше).
И да, это был обычный монолит на десятках инстансов со временем эволюционировавший в SOA.
P.S. Даже онлайн-магазин может состоять более чем из одной частей — склад, бухгалтерия, логистика, crm…
Можно в бд хранить инфу, кто когда последний раз начинал очистку и смог ли завершить её. Кто первый из дубликатов монолита успеет пообщаться с бд, тот и выполнит очистку.
>Но не проще ли сделать отдельное приложение для этого?
Черт его знает, может и проще, но это не относится уже к обсуждаемой теме. Мы не обсуждаем, что лучше — монолит или микросервисы. Мы обсуждаем возможность горизонтального масштабирования монолита.
Но не проще ли сделать отдельное приложение для этого?
Это не решает проблему 10 очисток, когда нужно заложить отказоустойчивость.
У монолитов точно так же должен быть такой механизм. Один из воркеров поднял атомарно какой-то флаг и пошел чистить. Другие воркеры уже буду знать что процесс очистки идет.
Никакой разницы в этом смысле между микросервисом и монолитом.
Всегда есть больше 1го решения, и у всех свои плюсы/минусы
Я запустил 10 инстансов и получил 10 очисток, вместо 1.
Потому что ваша система не умеет работать в нескольких инстансах.
Это не имеет отношения ни к микросервисам ни к монолитам.
В том смысле что любой из двух этих вариантов может иметь или не иметь подобный косяк.
Но не проще ли сделать отдельное приложение для этого?
Ну я бы решил сие с помощью MQ-сервера.
Это, имхо, довольно универсальный ответ на то, чтобы несколько инсансов приложения корректно совместно работали.
Нормальный подход — сделать для этого отдельный сервис.
В общем примеров море. Ни одно приложение, в разработке которого я участвовал, не получилось бы сделать монолитным, без каких-то извращений.
Update: да, тоже можно через общую фс. Пойду спать)
А в чем ключевые различия этой задачи при монолите и при микросервисной архитектуре?
![](https://habrastorage.org/webt/8b/6k/z9/8b6kz9qssq4ns2tufpvw_n9umgw.png)
Монолит (resumable upload не прокатит):
![](https://habrastorage.org/webt/jl/dp/ku/jldpkujdxlztgaw4wa0zr9uwtpm.png)
Простите, я ничерта не понял из вашей картинки
Все инстансы (реплики) могут ведь иметь общее файловое хранилище и работать с одними и теми же файлами.
монолитное приложение можно разместить только на 1 сервере
— не правда, кластеризация и балансировка запросов возможны и на монолите
Странно что никто до сих пор это не кинул
gist.github.com/jboner/2841832
Send packet CA->Netherlands->CA 150,000,000 ns 150,000 us 150 ms
Между Калифорнией и Нидерландами 150ms. А точто такое же внутри одного датацентра?
Задержка для сетевого обмена данных складывается из сериализации и десериализации данных, скорости передачи данных, пинга. В лучшем случае это числа порядка миллисекунд. В худшем случае число может достигать порядка десятков миллисекунд (а то и сотни).
Для сложного приложения эти числа нужно умножать в пару раз, потому что происходит больше одного общения.
Это вовсе не пренебрежимо малая задержка.
А если учитывать tcp handshake, кучу промежуточных роутеров, фаерволов, несколько уровней балансировщиков, можно получить скорость, которую совсем не ожидаешь, особенно если микросервис однопоточный, не имеет очередь. Тут же всплывают вещи о которых надо думать, потери пакетов в сети, лимиты на процесс (которых можно не достичь при локальных сервисах) и прочее-прочее. И в итоге может оказаться, что сравнивая производительность проекта построенного на микросервисной архетикруте и монолита, разница получается не в пользу микросервисов, при большей суммарной производительности серверов где работают микросервисы.
кучу промежуточных роутеров, фаерволов: например в AWS/Azure есть опция (Accelerated Networking) которая отрубает фаервол.
Я привел сравнительную таблицу только для одной цели, оценить задержки разных подсистем. Разбивая монолит на микросервисы вы добавляете значительную сетевую работу, которая медленнее всего, даже если это 10G. Приведение форматов обмена между микросервисами тоже необходим, а протокол общения добавляет кол-во возможных исключений. Что делать при отсутствии сети, потере пакетов, недоступности какого-либо сервиса, ошибок ответа, перегрузке вм-ки где работает сервис. И это только сетевая часть. А если немного поофтопить, и коснуться деплоя, то возникают вопросы версионности, внедрение фич касающийся сразу нескольких сервисов, обратной совместимости (при rollback и смене версий), сложности CI/CD, балансировке, сервис-дискавери. Повторюсь, я не спорю, просто разбивая на микросевисы, вы выносите изнутри наружу всю сложность связности. Когда то это оправданно, но чаще нет.
Например что если данные лежат в двух отдельных БД — невозможно гарантировать их консистентность. Ну типа такую операцию нельзя выполнить транзакционно:
personsSvc.fire(id = 123);
workplaceSvc.free(id = 123);
Или что не получится по-человечески сделать распределенный join на данных из двух сервисов, типа
select *
from svc1.persons p
join svc2.tasks t
where p.name like 'ivan%' and t.name like 'do %'
Такого плана мелочи ведь меркнут перед всеми неоспоримыми плюсами микросервисов.
Распределенные транзакции невозможны даже теоретически по CAP, а условный 2-phase commit — дает какую-никакую соломку, но средней руки разработчик никогда его по-человечески не сделает, а средней руки архитектор и аналитик — никогда этого «маленького нюанса» не учтут. В результате постоянная история когда что-то там «не прососалось», и руками что-то там в БД лазают и правят.
Распределённый join теоретически возможен — таки никакой разницы с БД вроде как нет — алгоритм будет такой же: hash join или inner loop там. Но когда это делается внутри БД — с локальным диском, кешем, и умными опытными разработчиками БД сделанное — это одно. А когда поверх REST-сервисов джунами — это совсем уже другое. Там же приходится городить кеши или сервисы-аггрегаторы какие — и хана перформансу и консистентности.
Ну и при этом всём, я регулярно вижу всяких архитекторов, которые реально топят за «давайте выделим Person Service и будем туда ходить из всех аппов в стиле GET svc/people/123». А иногда этим идиотам даже дают реально делать так систему, потому что модно. Весь этот проект, есстесственно, жостко факапится. И по жопе за это этим идиотам даже не выдать. Да, можно уволить их с позором. Но они же потом пойдут и устроятся «с опытом построения микросервисных архитектур» в другое место. Потому что модно. И даже может и не поймут что проект факапнулся от их глупости.
Поэтому я аггресивно против популяризации микросервисов. Еще раз — не против самого паттерна, а против его пополяризации. Людей, которые могут обоснованно принять решение юзать микросервисы — очень мало, там должен быть опыт архитекторства и разработки лет в 10 хотя бы. Эти люди сами как-нибудь решат. А для остальных всех — гораздо лучше будет если по-умолчанию в голове установка «микросервисы — плохое решение в 99% случаев».
Распределенные транзакции невозможны даже теоретически по CAP,
Нет, CAP-теорема вообще о другом. Она сообщает в общем-то самоочевидную мысль, что в условиях неработоспособности сети невозможно одновременно поддерживать целостность данных и доступность сервиса. Распределённые транзакции никуда не делись, и по-прежнему возможны, в том числе и теоретически.
Обычно CAP и Two Generals из одной области, находятся одновременно человеком, когда проблема до него доходит, и весьма возможно что одна из них выводиться из второй.
Вообще jacobz написал хорошо.
Транзакционное поведение в микросервисах делается через общую шину и ключи идемпотентности. Если облом на одном из сервисов, другие участники получают сигнал "сделай обратную операцию". Более того, действия могут быть очень разветвленными, на много экранов, если бы это было внутри хранимой процедуры. Ну а джойны с like%, это само по себе узкое место производительности и маркер костыля. А ведь можно вместо джойна кинуть запрос на десяток сервисов(например поисковый) и асинхронно склеить. Яндекс вон склеивает целую пирамиду из результатов метапоисков.
Поэтому я бы не скидывал и ядерный взрыв как фактор в таком уж случае.
Что такое транзакция, простыми словами? Это объединение нескольких запросов в базу — либо выполнятся все, либо не одна. Если во время исполнения транзации в приложении возникает ошибка — транзакция откатывается, изменений в базе нет.
А в распределенном приложении все по-другому. Изменения в базе уже есть еще на этапе выполнения похожей-на-транзакцию-операции. В случае, если произойдет какая-то ошибка, то приложение попытается создать новые изменения, которые вернут базу в «первоначальный» вид. А вот получится ли у него — бабка на двое сказала.
Проблем может быть целая куча. Ошибка в шине. Ошибка в микросервисе, который не сможет исполнить команду шины «откати». Падение микросервиса, который должен сделать откат. Недоступность базы в тот момент, когда микросервис пытается сделать откат.
Да, эти проблемы не будут встречаться в каждом втором запросе. Но транзакция гарантирует, что все будет ок, а псевдо-транзакция надеется, что все будет ок.
Если нужно "гарантий" — прятать транзакционный движок внутри конкретного сервиса. В остальных случаях достаточно носить каску и не стоять под стрелой. Надеяться, подстраховавшись в разумных пределах. Основная проблема — удачно поделить. Если получилось, то транзакции не понадобятся, а если все же вылезли наверх составные операции, есть варианты как выйти из положения. Но все же, надо сначала поделить на кирпичи без лишних связей.
В плюсах изоляция сложности, осязаемые простые ТЗ, легкость тестирования, меньше холиваров какой движок выбрать. Можно вообще заказывать часть микросервисов на стороне.
В простонародье есть "золотое правило микросервиса" — любой микросервис можно выкинуть и переписать с нуля за 2 недели не ухудшив.
>каковыми могут быть и монолиты — как несколько инстансов на разных узлах
А тут не соглашусь. Транзакции, в традиционном понимании этого слова — операция в бд, обычно реляционной. Сколько инстансов монолита общаются с этой бд — совершенно не важно.
Вообще же — thecoder тут в нескольких постах пояснил, что работа с транзакциями — это последнее, что надо реализовывать как микросервисы, только в случае, когда уж другого выхода нет. Признак потенциального микросервиса — это насколько все плохо будет для системы в целом, если сервис откажет. Если система переживет временную потерю сервиса приемлемо — его можно вынести, а если это чревато большими проблемами — сто раз подумайте, прежде чем этот модуль отторгать.
И мне сдается, что весь негатив к микросервисной архитектуре как раз оттого, что люди не думают что творят, когда начинают выносить в микросервисы тесно связанные модули — но здесь злой Буратино не микросервисная архитектура, а сам архитектор.
Простой пример — транзакция загрузки файла пользователем: или файл должен лежать на диске, а в базе его метаданные, или ни файла, ни метаданных быть не должно.
Пример посложнее: транзакция оплаты товара пользователем через внешнюю платежную систему. Статус заказа должен изменяться на «оплачен» только если оплата прошла, причём ответ платежной системы может быть «прошла» или «не прошла», синхронным или асинхронным, а может вообще ответ не дойти и надо самим постучаться на сервер и проверить.
Да даже если так сделать — все равно ж это не ACID.
И про like% я и не говорил. Я говорил что обычный джоин нельзя сделать на JSON/REST-сервисах без слёз. Ну типа есть такие API:
users.svc/get?age=23&city_id=2,3,1
locations.svc/get?i=1,2,3&country='RU'
Как вытащить всех пользователей с age=23 из городов с country='RU'? Вынуть все города России, и передать их ID в users? Или вынуть всех пользователей с age=23, вынуть их city_id и передать в locations?
Как вытащить всех пользователей с age=23 из городов с country='RU'?
Никак. Это пример архитектуры курильщика. Еще и на мобиле часто это все пытаются через REST дергать, чтобы собрать интерфейс, по 20 запросов на экран. Сквозные данные (единый набор id, общие структуры) не должны летать между сервисами.
Микросервис — это одна конкретная функция. Нарезать картинку, упаковать файл, отправить нотификацию, вычислить маршрут, геофенсинг, посчитать рейтинг, выписать токен доступа. Легко тестировать, почти нет зависимостей, конкретные изолированные кирпичи. Функция для которой делается джойн должна быть одним сервисом — каталогом юзеров с локациями. Сложно абстрактно рассуждать. В контексте поиска — разные вертикали обсчитываются разными сервисами. Учет денег тоже должен быть внутри одного сервиса.
… где-то видел схему с «делай обратную операцию» работающую реально?
Гарантий 100% никто дать не может и бывают необратимые действия. Это надо индивидуально рассматривать. В редакторах undo реально работает и сложность там не запредельная.
Если сделать микросервисы из одной функции примитивными, вся транзакционность сильно упрощается, практически до уровня промисов js в точке склейки логики. Ключ идемпотентности больше применяется, чтобы дважды одно и то же не сделать, но и откатить при случае можно. Типа сложный трансфер прав на объект, который сопровождается дополнительным пересчетом, но это прям экзотика.
Нотификация может быть финальным шагом в логике, а может быть сразу в пачке действий, часть из которых обламывается. Не отправлять сразу, ставить в очередь с задержкой отправки, по сигналу отмены удалять. Работает вполне. По ключу потом и логи удобно вытаскивать.
Например что если данные лежат в двух отдельных БД — невозможно гарантировать их консистентность.Почитайте, удивитесь: habr.com/company/oleg-bunin/blog/418235
А еще в статье явно написано что они уперлись в производительность монолита. Т.е. до этого они монолите построили крупнейший в россии интернет-магазин.
А еще в статье явно написано что они уперлись в производительность монолита. Т.е. до этого они монолите построили крупнейший в россии интернет-магазин.
Там не так написано.
С одной стороны, мы уперлись в производительность сервера с главной базой, но это не основная причина, так как вопрос производительности можно решить, например с помощью шардирования. С другой стороны, у монолита очень сложная логика, и на определенном этапе роста доставка изменений (релизов) становится очень длительной и непредсказуемой: много неочевидных и сложных зависимостей (все тесно связано), тестировать тоже трудоемко, в общем масса проблем. Решение — перейти на микросервисную архитектуру.
Они уперлись в производительность монолита.
Но видели пути решения.
Однако, заодно им захотелось все переписать, ибо им было неудобно.
И многие из этих статей делают вывод что надо сразу пилить микросервисы. Это неправильный вывод. Правильный вывод — сначала надо сделать попроще, а вот если взлетит — можно уже подумать как переделать, и на это уже будут деньги, и будет опыт и понимание как правильно делать.
К сожалению в Microsoft Visual Studio кнопки «приготовить пакет для инсталляции на Microsoft Windows Server 2016» не оказалось, а без кнопки выполнять поиск по соответствию пакетов библиотек платформе разрабочик отказалась, на основании того, что это потребует от неё работать сверхурочные.
Поскольку разработчик имеет неплохие отношения с отделом, начальник решил, что это не главное, работника не уволили, сервис запустили на платформе Microsoft Internet Information Service, и никаких нареканий по этому поводу не было.
Средства на освоение Microservice были освоены и выплачены, работа была не сделана, а недостаток компетенции инженера свалили на отсутствие кнопки в Microsoft Visual Studio.
И здесь возникает резонный вопрос, если можно отказаться от микросервиса, то наверное можно отказаться от многих других вещей, которые вообще — то не очень нужны предприятию, чей бизнес построен вокруг кассы в магазине. Можно сказать больше, в тот момент, когда на серьёзного эмитента, будь то США или Россия, свалится депрессия, мы будем уволены первыми. Все до одного в тот же день. Потому, что без нас продавец прекрасно может посмотреть на ярлык, подсчитать необходимый налог на продажу на калькуляторе, и выдать покупателю чек, насадив на колышек копию, которую внесет в реестр бухгалтер фирмы.
Да, огромное количество продавцов в бывшем СССР, с подачи многих бухгалтеров, пользовались недостатками этой системы оплаты товаров народного хозяйства, и их ловили на кражах от копеечных до государственных ( и тогда за это рассреливали ). Говорят во времена Андропова сели все в один день. И сразу стали честно считать. Более того, даже во времена, когда Гребенщиков распевал с подиумов
«А я сра-ал на Вашего Ленина
И на весь Ваш савейський саю-у-усь»
я спокойно подсчитывал в уме стоимость трех килограммов сельди ( которой говорят нигде не было ) по 2 рубля и 13 копеек за кило, высчитывал среднее арифметическое в расчете на одну рыбу, просчитывал отимальную стоимость покупки, в плане калорий, веса, затрат бюджета с точньстью до 4-го знака после запятой, поправлял на реальную стоимость у прилавка, платил точно до копейки, при чем продавщица не смела пикнуть, запросив 11 рублей 50 копеек за 3,5 кило, и уходил от прилавка, когда следующий покупатель выкладывал уже 13 рублей за ту же покупку, на ответ «а почему он дал меньше», продавщица вежливо отвечала «неа, он дал столько же» а на попытку упорного покупателя, изрядно отдававшего спиртным, настоять на своем вежливо отвечала «а давай подсчитаем, е*лан поганый», чем вызвала взрыв доверия всей очереди криками одобрения и немедленную оплату покупателя, счастливо расстающегося с 3-е кратной суммой, поскольку в пересчете на потерянное время от удовольствия от алкоголя с вожделенной закуской, препирательства были неуместны.
Если Вы думаете, что в США продавщицы чем — то отличаются от продавщиц СССР, Вы очень глубоко ошибаетесь. В России просто не привыкли прятаться. К моему глубокому ужасу недавно в одном из крупнейших магазинов страны я наблюдал сцену, которую очень надеялся никогда в жизни не увидеть. Пьяный муж, на чистом русском мате, охаживал свою супругу, обслуживающую покупателей, чтобы она не слишком улыбалась, или её дома ждет полный ***.
И вот во всем это, очень простом и одновременно сложном мире, за чем и кому на самом деле нужны микросервисы. Это очень важный вопрос. Потому, что в общем — то ответ на него мало отличается от ответа, за чем и кому нужны вообще компьютеры.
Я не хочу отвечать на этот вопрос. Потому, что ответ очень скучен и очень прост. Но это очень важный вопрос, с которым неизбежно сталкивается каждый специалист по созданию ПО, потолок которого чуть выше, чем поди нажми кнопку.
К чему такие вот всемирные обобщения… казалось бы все очевидно. Все было бы очень хорошо, если бы этому самому, которому нужны компьютеры, сервисы, микросервисы и прочая, в общем — то не нужная дребедень, они нужны были чуть меньше. Именно нашему, Русскому… Мы не можем себе позволить не делать. Звезда у нас такая :)
Микросервисы — есть следствие развития технологий. И это один из многих этапов этого развития, такой же, как само появление компьютеров. Если вы не понимаете зачем нужно развитие, посмотрите на то, как выглядел мир до этого развития.
Вы это хотели сказать? Тогда вы не правы.
Любая технология применима в одних ситуациях и неприменима в других. Вообще любая.
И причём тут рыба?
Причём тут IIS? Микросервис можно и на нём запустить, что именно девушка сделала не так? Докер тут вообще не причём, это всего лишь виртуальная машина.
1. Docker «это всего лишь виртуальная машина» — нет.
2. «при чем она» — ответ чуть ниже.
3. «Микросервис можно и» — нет, нельзя. Сервис, не изолированный от ОС является просто «сервисом». Другое дело, что определение не ограничивает средства изоляции контейнерами. Но то, что изоляция необходима определено совершенно четко.
4. рыба это пример, компьютеры не нужны, если принять концепцию автора «надо как дешевле, главное чтобы работало» Без компьютеров дешевле, и прекрасно работает.
Кто такую чушь сказал?
Та штука, поднятая на IIS, точно такой же микросервис. И это всего лишь вопрос удобства развёртывания и безопасности, в докере он, или нет, или в какой-то виртуальной машине, или на выделенном кластере.
Квинтессенция статьи: "если вы не понимаете каким боком к вашему приложению приплести микросервисы — спите спокойно, они вам не нужны".
сервис на базе микросервис архитектуры Docker— с каких это пор Docker стал микросервисной архитектурой?
docs.microsoft.com/en-us/dotnet/standard/microservices-architecture
github.com/dotnet-architecture/eShopOnContainers
На все остальные вопросы — Яндекс в помощь.
Накладными расходами тут являются поддержание интерфейсов между сервисами — как в архитектурном плане (согласованность форматов), так и в физическом (доступность сети), и, конечно, мониторинг. Изначально я хотел вообще сделать основной сервис с функциями контроля над добытчиками, но лимит времени на разработку не позволил чрезмерно углубляться, будет развитие проекта — вот тогда и можно будет дальше эту тему развить.
Статья же — однобокая, но она так и задумана, наверное. Ведь действительно лучше сделать работающее приложение, которое еще сто раз поменяется, чем стараться изначально все эти сто предстоящих изменений предусмотреть — и, конечно, все равно ошибиться, ибо всего не учтешь. Но то, что изначально хорошо можно вынести в отдельный сервис — почему бы и не вынести, если есть время и ясное понимание и обоснование преимуществ такого решения.
Микросервисы — общаться с другими серверами по сети, подключаемся по HTTP, пересылаем JSON, можем легко отмасштабировать горизонтально.
Идеи схожи, детали сильно различны, поэтому требуется отдельный термин (а есть еще S в SOLID, на ту же тему). Так-то ничего нового в мире, да и в литературе всего четыре сюжета.
идеи философии un*x: разделить сложную задачу на простые задачи
Нет, нету такого в философии юникс. Она про то, как писать одну программу так, чтобы её могли потом использовать как угодно. Подход к проектированию снизу-вверх.
А вот микросервисы уже про разбиение, подход сверху-вниз.
XML вместо сырого текста — это упрощение работы, а следовательно экономия времени и прочих ресурсов. Оптимизация расхода ресурсов — краеугольный камень любой экономики. Авторы вывода только потому получились такие складные, что он выкинул из расчетов экономику вообще. При этом он лицемерно писал на компьютере текст, хотя мог разослать всем нам по голубю.
В каждом своде правил/советов по хорошему коду есть пункт типа «каждый фрагмент кода должен выполнять одну задачу», разумеется, делать это правильно.
Грамотно написанные монолиты следуют этой философии. Т.к. вообще в программирование есть принцип разделения ответсвенности в коде.
Монолит отличается от микросервисов в первую очередь тем, что в монолите нельзя отключить определенный кусок когда тебе вздумается. Только целиком.
А микросервисами можно манипулировать — включил-выключил-поменял без остановки других частей.
Если все как вы говорите, то если в куске, который заменили, во время замены проблема возникнет типа «Global apocalypse panic» — она за собой только этот кусочек потянет или всю конструкцию?
Монолит отличается от микросервисов в первую очередь тем, что в монолите нельзя отключить определенный кусок когда тебе вздумается. Только целиком.
Нет никакой проблемы поменять кусок в монолите без отключения всей системы.
Серьезные системы обновляют хитрым образом. Например, blue-green deployment или gaceful restart.
Эти же технологии применяются что для монолитов, что для микросервисов.
Без использования blue-green deployment/graceful restart и пр. вы и с микросервисами не можете поменять произвольную часть системы без остановки обслуживания клиентов.
Некоторые части системы — да.
Некоторые — ключевые, нет. Отключение их равносильно отключению сервиса целиком.
А микросервисами можно манипулировать — включил-выключил-поменял без остановки других частей
Система только выглядит рабочей.
Формально — да. Если 90% микросервисов у вас работают — то система рабочая как бы на 90%?
Фактически нет. Не рабочая. Пример ниже.
Пример: Интернет магазин.
Если вы отключите подсистему, отвечающую за формирование списка товаров и навигацию по нему (каталог товаров, витрина) — ваш интернет магазин не функционирует.
И то, что у вас при это успешно работает 90% остального кода — уведомления пользователей, корзина с товаром, личный кабинет, платежная система — не имеет никакого значения.
Ваш сайт мертв пока, вы не восстановите каталог товара.
Да, возможно, в этот момент кто-то уже в корзине и просто оформляет покупку. Однако:
а) Одного покупателя дает только 1000 или даже 10 000 посетителей.
б) Покупателем он становится после длительного прохода по каталогу товаров, выбора товаров.
Так что вы обслуживаете 1 покупателя в корзине/личном кабинете и даете отлуп 10 000 или 100 000 потенциальным, что нужен каталог.
То же самое можно сказать об отключении корзины или механизма оформления покупок, платежной системы — без всего этого ваш интернет-магазин и не магазин вовсе.
Вот мне довелось обновляться — и я ВЫНУЖДЕН в монолите обновлять или всё или ничего.
И то же с масштабированием — при масштабировании микросервисного конгломерата я могу масштабировать именно тот функционал, который требует масштабирования, при монолите — допинстансы поднимаются целиком, хотя вполне возможно, что производительность проседает в отдельном модуле. То же самое — с резервированием.
Никто не говорит, что микросервисы — лучшее решение во всех случаях, или что оно «бесплатное», но и появилось оно не на ровном месте, от скуки.
Вот мне довелось обновляться — и я ВЫНУЖДЕН в монолите обновлять или всё или ничего.
Стоп-стоп-стоп. Речь совсем о разных вещах.
Во первых:
У меня написано-то, что можно модернизировать систему построенную на базе монолита, без остановки обслуживания клиентов/посетителей/заказчиков.
То есть я говорю с точки зрения конечного пользователя — об уровне эксплуатации или operations, если по ненашему. Вторая часть термина DevOps. Вы же не выключили систему на месяц, пока занимались модернизацией?
Вы же говорите о самом процессе модернизации системы, то есть об разработке или, по ненашему, development. Первая часть термина DevOps.
Во вторых:
Ваш пример — про сильносвязанную систему, изначально разработанную без учета принципов «Чистой архитектуры».
Это позволило вам изначально программировать быстрее (что само по себе и неплохо), но затруднило дальнейшие модификации. То есть таков был изначальный выбор разработчиков. Хороший он или плохой — я не говорю. Заочно тут нечего обсуждать. Выбирать можно только взвешивая все «за-против» видимые только в конкретном случае.
Важно другое — вы говорите не об корневой проблеме монолитов, а о проблеме конкретной разработки, сделанной не по принципам чистой архитектуры.
Но да, монолиты в этом смысле расслабляют разработчика. Не лишают его возможности сделать грамотный SOLID — это вовсе не принципиальное свойство монолитов. А всего-то расслабляют, так как не обязательно делать все «по-правильному, с учетом дальнейших модификаций и расширений».
Так что виноваты не монолиты. А люди.
Но и примеров, когда микросервисы не помогут при серьезных переделках — можно придумать. Например, была у вас система, где микросервисы разговаривают через шифрованную сеть, внутри сети доступ не ограничен, снаружи не попасть. Все вроде зашибись.
Но пришли безопасники и сказали — что для multi-tenancy так нельзя. И будьте добры запилить теперь в каждом микросервисе функционал для аутентификации и авторизации и шифрования для каждого tenant независимо. И вы встряли опять таки с тем же объемом работы, что и был в вашем примере. И для микросервисов он будет даже побольше, чем для монолитов.
P.S.:
Имхо, хороший монолит можно распилить на части и превратить в микросервисы относительно легко. Ибо все входящие в него сервисы изолированы, взаимодействие между ними идет на уровне известных интерфейсов.
Собственно, человечество еще в прошлом веке придумало для этого инструменты, используемые для написания больших программ — всевозможные «private vs public» в помощь принципа SOLID/«чистой архитектуры». Как раз, чтобы не было соблазна лезть в другой модуль напрямую. Как раз чтобы на выходе не получить мешанину сильносвязанного кода. Это нужно не только для микросервисов. С таким кодом и в монолите сложно работать. Например, проблема тестирования части функционала.
P.P.S.:
Довольно подробно pro/contra микросервисов рассказано тут
habr.com/company/flant/blog/424531
Ребята как раз специализируются на обслуживание всевозможных систем, в том числе и микросервисных.
Далеко не всегда:
![image](https://habrastorage.org/getpro/habr/comment_images/74e/7ac/d93/74e7acd9319a9153184139a0d9f60ab6.png)
Красным — неоправдывающиеся ожидания.
В том числе и усложнение (а не упрощение) разработки.
За деталями — туда. У них довольно развернутая аргументация.
И — они как раз любят и вовсю используют Kubernetes, созданный как раз для микросервисной архитектуры.
А вот насчет моего примера — никакая наичистейшая архитектура мне всё равно не помогла бы — разве только в той части, где я написал о «созданные им зависимости проникли в код монолита» — но это действительно не проблема монолитности, а та самая — недостаточно чисто написано. В остальном же — и в главном — мне всё равно пришлось обновлять этот потенциальный микросервис из-за того, что обновляется сама платформа, на которой построен монолит — фреймворк. Будь у меня независимый сервис — он мог бы продолжать функционировать без изменений, несмотря на обновление других частей конгломерата сервисов, которая образовывает систему.
Кстати заметить, «Чистая архитектура» — это та же проблема, что и «Чистые микросервисы» в разработке — она требует продумывания архитектуры. А до этого тоже придется дорасти. И разработчики пишут не совсем чистые монолиты отнюдь не только потому, что не умеют писать чисто, а потому, что как раз на старте еще очень и очень многого не видно, не понятно и неясно как будет — это задним умом мы все сильны.
Статьи почитаю, спасибо.
Есть и еще развернутые статьи:
m.habr.com/post/261689
Мартин Фаулер (умеет аргументировать). Компромиссы микросервисов
m.habr.com/post/311208
Микросервисы: пожалуйста, не нужно
Когда я размышляю о реализации той или иной задачи, я в первую очередь думаю… о задаче: ) Не о микросервисах или монолите, ни даже о фреймворке или библиотеках, алгоритмах — а о задаче. Я может быть несколько консервативен — но, признаюсь — я даже рисую схемы бизнес-процессов: ) стараясь в первую очередь представить картину предметной области в логическом смысле.
Получив такую картину, я начинаю понимать — что и как лучше реализовать, где можно и нужно отделить, а где — нет. Отделяю я те компоненты в микросервис, где могу делегировать ответственность в независимый компонент. Это как, например, распределение задач на выполнение разным людям — они будут трудиться совместно, но у них есть свои довольно четкие зоны ответственности. Кроме того, важно, чтобы разделение не вызывало необходимости чрезмерно плотной коммуникаций — когда людям приходится постоянно у друг друга что-то выспрашивать, чтобы выполнять свою работу. Микросервисы так же не должны разделять объекты в работе — то есть нет такого, что один объект изменяется из двух разных точек, все изменения проходят только как сообщения-данные между сервисами. Сервисы принимают данные и данные же возвращают, а не модифицируют «чужие» объекты. В общем я, даже не обладая большим опытом в микросервисах — уже, пожалуй, мог бы написать объемную статью — как именно я выбираю кандидатов на микросервисы.
Придерживаясь этих своих правил я получаю весьма надежную систему из микросервисов, получаю и все преимущества микросервисности. Плачу за это — необходимостью интерфейсов между микросервисами — они должны понимать друг друга в работе.
И пока, всё что я прочел про «недостатки» микросервисов, является по моему мнению — недостатком в понимании: что именно и как следует разделять. Многое из того, что я прочел — я изначально не стал бы выделять в сервисы и для меня дальнейшие рассуждения о превратностях микросервисов уже более чем странные. Примерно как если бы кто-то написал:… и вот мы решили отправить рыбу лазить по деревьям…
Я здесь не пишу в духе «микросервисная архитектура — это лучшая практика» и, соответственно, статьи с антогонистическим посылом для меня мимо цели.
Ну что вы. Зачем с антагонистическим…
Скажем у Фаулера — и плюс и минусы отлично расписаны.
Во второй статье по моей ссылке — в те же самое что мы здесь пытаемся в комментариях обсудить. Но сформулировано стройно и развернуто. Чувствуется, у человека есть опыт.
Интуитивно я сам пришел к выводу, что есть некоторые задачи в разрабатываемом проекте, которые прекрасно отторгаются в микросервис и дают его преимущества — независимость разработки, независимость обновления, масштабирование и прочее, но я не стремлюсь всё приложение запаковать в микросервисы — речь лишь о некоторых его частях, где плата за микросервисность окупается преимуществами. Поэтому для меня отрицание такого подхода в целом и выглядит как несостоятельное — я ведь в своих гибридах получаю обещанные микросервисами преимущества, в то время как оппоненты, по-видимому, отрицают необходимость полного разделения приложения на микросервисы — с чем я лично и не спорю.
Меня лично берет оторопь, когда пишут: «у нас 50 микросервисов» или «у нас более 100 микросервисов»… Э-э-э, что? Это действительно было необходимо? Чего вы добивались этим дроблением? Почему бы тогда уж каждую функцию-процедуру не завернуть в «микросервис»? Неудивительно, что проблема из «Большого комка грязи» превращается в проблему «Большого клубка связей».
habr.com/post/427215/#comment_19283943
Вот мне довелось обновляться — и я ВЫНУЖДЕН в монолите обновлять или всё или ничего.
А вот тут — ровно та же проблема что и у вас. Проблема с кодированием и общими библиотеками — привела людей из микросервисов к монолиту назад.
m.habr.com/post/416819
Прощайте, микросервисы: от ста проблемных детей до одной суперзвезды
Чтобы понять о чём идёт речь, подумайте о разнице между обычной многосерверной архитектрой и микросервисной? Эта разница в том, кто пишет спецификации.
«Кто пишет спецификации» — это не архитектурное решение, это административное. И его цель — разделить труд более квалифицированных и менее квалифицированных людей. Более квалифицированные думают про архитектуру, менее квалифцированные реализуют что сказали без необходимости быть визионерами и думать на 20 ходов вперёд.
Другими словами, если у вас в команде нет человека, способного написать хороший продукт из нескольких сервисов, переход на микросервисную архитектуру не сделает ничего лучше — вам всё равно нужен такой человек, чтобы писать спецификации.
Микросервисы нужны системным архитекторам, чтобы разделять зоны ответственности. Если этой проблемы нет, то и микросервисы вам ничего не сделают, кроме увеличения хлопот.
Даже хуже. Если у вас спецификации микросервисов будут писать те, кто пишет микросервисы, то вы получите комплет проблем микросервисов (сложность отладки и обслуживания), комплект проблем от неконсистентных API, комплект проблем из-за необходимости сопровождать все эти API в каждом приложении (а уж внесение изменений и поддержка нескольких версий сделает вашу жизнь восхитительно-загруженной).
Так что имея то, что имеем, я решил попробовать свои силы в этом вопросе (тем более, что за пару лет работы в компании, я получил возможность влиять на технические решения).
Главная проблема, с которой я столкнулся, — монолитическое мышление в головах у коллег. Как одно из следствий — четко определить границы сервисов не может никто (да и у меня нет-нет, да возникают большие сомнения по поводу того или иного ршения.
Вторая проблема — по сути все разработчики должны одновременно превратиться в devops, т.к. нужно уметь настроить нужный тебе стек хотя бы локально (да, мы используем докер, но если ты делаешь новый сервис, то тебе нужно уметь всё это правильно подружить). К сожалению, этот момент почти полностью свалился на меня — вся инфраструктура под микросервисы это чисто плод моего труда; никто из коллег не приложил никаких усилий к этому, хотя активно стремился спихнуть часть задачу кому-то еще, чтобы разделить участие в задаче (и как следствие, чтобы в моем лице не было SPoF).
Третья проблема — усложение процесса разработки. Когда добавление одного поля в апи требует изменения сразу в нескольких сервисах (а значит релиз каждого из них). Может так случиться, кстати, что какая-то часть сервисов ускользнёт из поля зрения, а т.к. интеграционных тестов у нас до сих пор почти нет, это может вылиться в факап (хорошо хоть у нас имеются юнит- и функиональные тесты — это как-то спасает).
Четвертая проблема — страшный бардак в легаси коде, приводящий порой к злоупотреблению багами со стороны маркетинга и «продажников». И весьма неприятно после выпуска нового сервиса в продакшн ощущать на себе лучи поноса из-за того, что какая-то багофича перестала работать (а она и должна была перестать, т.к. была обычным багом).
Пятая проблема — (она же частично следствие из 4-х предыдущих) — на начальном этапе снижение скорости разработки, т.к. много вещей надо перелопатить, что-то реверс-инжинирить, что-то откоммуницировать с фирмой, найти ответственных за данную фичу (или багофичу) со стороны «продажников» и маркетинга. Но, надеюсь, со временем всё станет лучше.
Шестая проблема — сильно выросли требования к мониторингу (теперь надо мониторить каждую часть в отдельности) и сбору метрик (их вообще раньше никто не собирал).
Но мы бы не продолжали «ехать» в данном направлении, если бы сервисы несли только проблемы. Кратко перечень выгод:
— команда начала писать тесты (в старом легаси коде это было почти невозможно из-за повсеместного использования статических функций, синглтонов и нарушение паттернов — например, когда в модели неожиданно для тебя делается бизнес-логика)
— команда несколько обновилась: те люди, которые были причастны к написанию некачественного старого кода и не смогли приспособиться к новым подходам, были вынуждены покинуть команду
— ребята (даже самый зеленый юниор) достаточно быстро учатся (если уж не в сфере девопс, так в сфере качественной организации кода и покрытия его тестами), предлагают свои идеи, которые день ото дня всё более близки к реальным потребностям нашего продукта
— нет необходимости прикручивать сбоку костыли в старом легаси, когда можно написать что-то новое, идеологически более правильное, читаемое, тестируемое, масштабируемое
— мы начали активно использовать кубернетес в своей архитектуре, что нам позволяет гибко доставлять сервисы на продакшн и масштабировать только необходимое, а не весь монолит сразу
— нет привязки к одному языку, плюс легче производится апгрейд версии языка (раньше был только php версии 5.3, сейчас есть php 7.2, go и немного nodejs).
В общем, не всё однозначно, и не для каждой фирмы это имеет смысл, но если уж фирма на это решилась, то выгода обязательно будет. Но очень не сразу (особенно, если ресурсы ограничены).
Так же в rest api гоняется очень много лишних данных из-за чего запросы по 200-300 мс — норма, хотя никакая это не норма.
Чтобы хоть как-то упорядочить весь этот ворох запросов даже придумали Swagger который реально выручает.
Но на горизонте уже виден новый многообещающий подход — graphql.
А кроме шуток, и говоря о заметке уровнем выше про опыт с микросервисами — я полагаю, что надо еще и осторожнее быть со связями между сервисами, то есть в идеале один сервис связан не более чем с двумя другими сервисами, обеспечивая иерархическое дерево, иначе геморрой при изменениях начинает расти экспоненциально как функция от количества связей на сервис. С одной стороны такое ограничение вроде как и противоречит концепции широкого переиспользования, с другой — это продиктовано соображениями локализации и сдерживания сложности. Если в такой ограниченной микросервисной архитектуре возникает необходимость изменений какого-либо сервиса, то это хотя бы не порождает слишком много изменений, а те, что неизбежны — по-крайней мере распространяются в известном направлении — вдоль своей ветви. Но это так — интуитивные соображения безопасности, реального опыта эксплуатации пока мало.
Та ситуация, с которой он позволяет бороться очень актуальна для кого-нибудь типа facebook, когда у тебя десятки тысяч независимых разработчиков и все хотят разные срезы данных. При этом в жертву приносится значительный кусок функционала от протокола HTTP (состояния, ошибки, уникальные идентификаторы ресурсов, кэширование, докачка), очень сильно ограничивается инструментарий (вся куча кода написанная для http routing) и появляется необходимость поддерживать еще один слой абстракций. Причем если худшем случае задача — только передача данных, то разработка превращается в написание очередного протокола общения с базой данных (где кстати уже есть тоже не шоколадный, но более распространенный ODATA), а если нет — то корявенький RPC.
Если задача — махровый энтерпрайз, и количество независимых разработчиков под твое приложение — ноль, то graphql это скорее помеха, опять же не сам по себе, а в связи с тем, что для него нужно тащить библиотеки — которые под кучу языков — сырые.
Второе же заключается в том, что используя graphql необходимо и мыслить в концепции graphql, попытки же сделать «как в REST, только GraphQL» к хорошему явно не приведут, что, в общем-то, справедливо для любой технологии.
если речь о том, что к любому такому инструменту нужно адаптировать свой мыслительный процесс — 100% соглашусь с вами, а вот если то, что надо бизнесс задачи переформулировать так, чтобы они под graphql подходили — это вряд ли соглашусь. Для гвоздей — молоток, для шурупов — отвертка, а микроскоп вообще не для строителя.
в идеале один сервис связан не более чем с двумя другими сервисами, обеспечивая иерархическое дерево
То есть корень дерева со стороны выглядит монолитом и отказ самого последнего листочка поднимается вверх по дереву? Как по мне, то стоит стремиться к полному устранению зависимостей сервисов друг от друга, перекладывая агрегацию данных от разных сервисов на плечи клиента или хотя бы на специальные сервисы задача которых распарсить тот же graphql запрос, раскидать его по "реальным" сервисам (где можно — параллельно) дождаться ответов (ошибок, таймаутов) и собрать ответ.
иначе геморрой при изменениях начинает расти экспоненциально как функция от количества связей на сервис.
я предпочитаю один геморрой на сервис (количество связей в нулевой степени), а не, например, 4 при связи с двумя сервисами :)
Разумеется, это не про те «микросервисы», которые на самом деле уже и не «микро», а полноценные приложения, к которым обращается масса других приложений за какими-то данными.
Вообще же, надо просто понять — откуда есть пошли эти микросервисы. Они ведь появились не только лишь потому, что кому-то вздумалось «поиграть в микросервисы». Микросервисы — это логичный ответ на конкретные задачи. Я выше тут писал про свой пример — у меня есть в проекте задача обслуживания клиента, и есть задача периодического мониторинга неких данных, которые я предоставляю клиенту. Можно обе задачи решать в рамках одного монолитного приложения, но так же хорошо эти две задачи могут работать и по отдельности. У меня нет сильной связанности между двумя этими задачами: то есть сбор (обновление) данных конечно нужен, но если он не увенчался успехом в очередной итерации — клиенту допустимо предоставить на его запрос предыдущие данные. Другая особенность состоит в том, что сбор данных может меняться довольно часто из-за того, что сам источник этих данных изменяется и под него надо подстраиваться — в условиях монолита это будет означать необходимость править весь проект, прерывать обслуживание клиентов при апдейте проекта. А в условиях микросервиса — я могу править сервис более удобнее. Я могу вынести микросервис сбора данных куда угодно — это, кстати, тоже одна из задач проекта, так как наш доблестный РКН любит поиграть в модераторов. Я могу наплодить кучу одинаковых микросервисов, тем самым зарезервировав их, я могу относительно легко добавлять микросервисы с другим типом собираемых данных — конечно же тут придется и принимающую часть изменять для того, чтобы она умела эти данные принять, но вся инфраструктура уже построена — нужны лишь кастомны методы приема и обработки конкретного типа данных.
Я не являюсь ни теоретически подкованным в микросервисах, ни обладателем обширного практического опыта их применения, ни даже их почитателем — я использую то, что разумно в поставленной задаче.
P.S. Документация к коду не полна, как всегда :( Например, там не задокументированы алиасы для полей (что позволит скрыть t.tableName за чем-то вроде pinkPony).
P.P.S. GraphQL, кстати, тоже не спасёт от тяжелых неиндексированных запросов. Вообще, произвольный доступ к данным и практически произвольная фильтрация — это достаточно сложная задача не только со стороны API, но и со стороны безопасности и эффективности.
Что значит «не заложен»? Принципі rest не запрещают вам вытягивать сколь угодно сложные схемы.
Действительно когда у нас вместо одного монолита есть пачка независимых модулей — куча сложности, которая скрыта в монолите вылазит наружу. И с этой сложностью приходится считаться не только разработчикам, но и менеджерам, продавцам, тех. саппорту и т.д.
Лично я не считаю, что один вариант лучше или хуже другого. Для каждого конкретного случая надо вводить измеримые метрики и проводить анализ. А для того, чтобы это сделать — нужен по крайней мере рабочий продукт. А так как монолит легче построит с нуля, то в очередной раз придется согласиться с автором статьи — сначала монолит. Только не так, что сначала монолит, а потом разбиение. А сначала монолит, потом метрики (желательно привязанные к бюджету), потом когда выгода от модульной структуры начинает превышать издержки (если вообще хоть когда-нибудь) — разбиение.
Ну и выгоду от разбиения сложно посчитать до него, как и издержки после в более-менее реальных случаях.
Другая причина — да начиная с MVP в котором можно даже не сильно замарачиваться архитектурой создаешь задел на будующее (ну и проблемы, конечно). Но потом опять же вицепрезиденты начинают спрашивать, а почему вы решили переписывать то-то и то-то, ведь недавно же написали?! — давайте фигачте больше функционала. Т.е. на словах все могут быть очень agile, но политика никуда не пропадает. В конечном итоге все решаемо.
Ну и последнее. Вицепрезидент может захотеть поиграть в архитектора. Или архитектор может захотеть поиграть в новые игрушки, которых не было в детстве.
Вполне имеют право на жизнь при умелом использовании
Лучше кривой и косой, монолитный прототип, чем никакого. Микросервисы — это архитектурный черный пояс. Легко перейти, если получается предметную область делить на объекты. Микросервисы дают такой же эффект изоляции сложности, инкапсулируя ненужные особенности.
Если чел банально не умеет в объекты, то естественно возникает ступор. Потому что разработка и мышление на уровне "взять данные из одной библиотеки и кинуть в другую". И вот это все как раньше летало внутри "функции main", так и продолжает летать, только между отдельными сервисами, что дает тот самый ужасный распределенный монолит.
Сначала изучать ООП, научиться заворачивать и не выпускать сложность сквозь интерфейсы, научиться понижать связность внутри одного модуля, а потом уже переходить на микросервисы.
Как вы думаете, Тобиас сидел в нерешительности, кропотливо продумывая идеальную архитектуру на микросервисах, прежде чем написать первую строчку кода?
Чёрт, нет. Я не присутствовал при разработке первой версии Shopify, которая изначально была просто интернет-магазином для сноубординга, но если Тобиас похож на меня (типичный разработчик), то процесс выглядел примерно так:
1. Изучить новую технологию в процессе написания исходного продукта.
2. Написать довольно нестандартный (поганый), но полностью рабочий код.
3. Посмотреть, как всё работает вместе и возбудиться.
4. Провести рефакторинг типа «выжигание огнём» и улучшить код, когда возникает проблема.
5. Повторять этот цикл при добавлении новых функций и запуске в рабочей среде.
До микросервисов нужно дорасти, а не начинать с них