Pull to refresh

Comments 30

Ну смысла в этой статье нет уже книгу написали.
Не всегда микрокосервисы лучше монолита. не говорите мне про масштабируемый код в вакууме )
При переходе на микросервисы надо учитывать общую стоимость владения, а не только затраты на разработку. Разработка микросервисного приложения и правда может оказаться дешевле. Но жизнь продукта разработкой не заканчивается, далее следует саппорт, затраты на который обычно в разы превышают затраты на разработку. По разным оценкам стоимость разработки составляет примерно 10-20%, в то время как саппорт — 60-80% в общих затратах в течение периода эксплуатации.

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

Процитирую свой коммент к другой статье, почему это происходит
В моей компании тоже все на микросервисах, только счастья это не приносит:

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

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

  • особенно странно видеть, как зоопарк микросервисов на разных версиях разных языков оперирует с одними и теми же логическими связанными данными. Я, конечно, понимаю, что хотели делать масштабируемо, но на уровне данных все SQL-запросы в итоге идут к одним и тем же базам данных, от чего БД становятся узким местом. Но отмасштабировать БД, как правило, намного труднее.

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

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

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

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

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

В общем это не факт, что на микросервисах удасться сэкономить.
Почему микро-сервис — это обязательно отдельный проект? Ведь можно иметь единую кодовую базу и при этом с успехом использовать микро-сервисы. Например, монолитный код проекта можно запускать с разными конфигами, таким образом формируя любое необходимое количество микро-сервисов.
Ключевое слово «можно». Любой проект, особенно, разбитый на атомарные независимые модули, развивается неравномерно. Некоторые части, стабильно работающие и не требующие дополнения функционала, могут прожить нетронутыми несколько лет. За это время многое может произойти: схлопывание delphi и FoxPro, выход нескольких новых стандартов ЯП, релиз очередной Windows и т.д. и т.п. В итоге, микросервис, являвшийся подпроектом, устаревает. Проблемы совместимости в нём чинят методом «лишь бы работало», так как есть куда более важные задачи, нежели переписывание РАБОЧЕГО кода.

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

И совершенно естественно, что неэффективную систему поддерживать дорого. Но ведь можно сделать и эффективно.

Плюс, они зачем-то имеют зоопарк шин (печальный опыт, не недостаток микросервисов).

А вторая половина проблем в цитате из спойлера — из-за отсутствия или недостатка тестов и документации.

Тоже хотел сказать об этом, но тут есть доля правды. Юнит-тесты не спасают от ломания всего при смене логики какого-то сервиса. Так что тут либо соблюдать compatibility promise, либо страдать. Собственно, там описываются страдания от первого варианта.

Но вот лично у нас gprc, и, как мне кажется, изменения логики при сохранении совместимости — не такая уж и тяжелая задача. Возможно, только пока.

Стратегией нахождения бага в супер-пупер-микросервисном проекте с тестами и с одним разработчиком без доверия к тому, что совместимость идеальна:


  1. Предполагаем, что виноват сервис "А"
  2. Видим точку выхода в сервис "Б"
  3. Читаем документацию сервиса "Б", убеждаемся, что ответ от "Б" не совпадает с документацией
  4. Пишем тест на код, показывающий, что сервис "А" работает корректно
  5. Закрываем папку с сервисом "А", открываем "Б"
  6. Переходим к первому пункту

Такой подход решит все проблемы из спойлера-комента, которые не зависят от деплоя. А остальное решается правильным и автоматическим развертыванием.


Но вы тоже правы — тесты и документация не спасают от того, что нужно продолжать думать головой.

собственно, эта ветка комментариев четко дает понять, что в программировании и, тем более, в проектировании архитектуры не существует «silver bullets» — любое решение должно быть аргументировано. И прямые руки и голова тоже нужны при любой архитектуре. Такие статьи и такие комментарии как раз и нужны для того, чтобы помочь не наступать на грабли, на которые другие уже наступили
Проблема в том, что система у нас очень большая, с кучей взаимосвязанных компомнентов, одним словом, сложная.
Микросервисная архитектура позволяет уменьшить сложность конкретных микро-приложений, но при этом возрастает сложность на уровне интерфрейсов между приложениями, так что общая сложность системы в целом не изменяется.
Этот перенос сложности с одного уровня на другой имеет как плюсы (упрощение конкретных приложений-микросервисов), так и минусы (из спойлера).
Поэтому для каждого конкретного компонента надо смотреть отдельно, как лучше его реализовать. А микросервис — это лишь один из инструментов, который может где-то в чем-то помочь.
Извините, но вы сами не замечаете?
«с кучей взаимосвязанных компомнентов» как-то слабо кореллирует с «Микросервисная архитектура». По крайней мере в моём понимании — микросервисная архтиектура подразумевает независимость сервисов друг от друга на уровне логики. Возможно при разбиении сервисов были допущены серьёзные промахи «где резать»?

Ну и как тут рядом говорят — невозможность локального разворачивания сервисов == хреновая схема деплоя.
Вот, например, пользовательский интерфейс и хранилеще данных, они попадают под термин «взаимосвязные компоненты»? Уверен, что да. И, чаще всего они реализуются как раз в виде микросервисов, и начали так реализовываться задолго до того, как появилось ругательство «микросервисы».

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

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

Конкретный пример? Приложение, рассчитывающее какой-нибудь сложный интеграл, основное приложение, задающее часть параметров по свойствам описываемого объекта, и система ГИС, отвечающая за требуемые координаты. Плюсы, шарпы и html с нодом.
Интегралы, с которыми сталкиваются, к примеру, экологи, могут иметь под 50 параметров, 49 из которых необязательны и 15-20 используются очень редко, а сама формула периодически обновляется, приобретая мелкие нюансы, дающие разброс до 10% и больше. Да даже просто переименовываются некоторые параметры, это нужно вносить в приложение, потому как искать через 10 лет историю переименования параметров формулы — проще всё написать с нуля. Вот и получается, что ни тестов железобетонных написать, ни заморозить API не получается, тестирование становится беззубым, а единственным надёжным помощником в отладке становится калькулятор. Ну ничего себе, технический прогресс!
> как появилось ругательство «микросервисы»

Простите, но это явное предвзятое мнение. Основанное на неудачном опыте… вследствии криворукости.

> Так вот, если у вас эти компоненты реализуются в виде динамических библиотек, как это делают в «монолите», многие ошибки можно отловить на этапе компиляции, те же несовпадения типов и числа параметров.
Если же у вас всё в виде микросервисов, то такие банальные ошибки отловить не получится, так как всё будет завёрнуто в json\html или во что вам заблагорассудится. При этом далеко не всегда предворительные проверки отловят ошибочное место.

Т.е. с валидацией данных/интерфейсов вы не знакомы? Json-schema для json и xsd для xhtml для вас ни о чём не говорят? Проблемные места должны быть покрыты валидацией. Интерфейсы должны иметь взоможность валидации на этапе разработки (как минимум).
Если ваше окружение уровня домашней поделки — там оно может и не надо, а вот коммерческая разработка таки требует таких вещей. Посмотрите на SOAP — там интерфейс описывается схемой и от этой схемы строится клиентское и серверное приложение! В REST (json) — это так же возможно.

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

Простите, но что значит «одновременная корректная правка узких мест»? Интерфейсы должны поддерживаться в единообразии ВСЕГДА. Внутренняя логика НЕ ИМЕЕТ права ломать контракт интерфейсов. Всё остальное решается достаточно легко.

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

Не поверите — делал ошибки в монолите. Опечатки! Обнураживалось не всегда сразу — несколько раз уходило до Q&A. После этого полной уверенности, что не ушли в прод — у меня нету. Для любых систем необходимы гарантии соблюдения контрактов интерфейсов. Будет это в монолите или в микросервисах — накосячить можно всегда. Валидировать тоже можно всегда.

Ваш конкретный пример показывает стандартную проблему «надо было сделать ещё вчера». Некоторые компании действительно работают по такой схеме. Я бы не хотел пользоваться их софтом вне зависимости от того, будет ли там монолит или будут микросервисы. Этого нужно избегать. Как? Например версионирование и валидация интерфейса — в пределах одной версии интерфейса мы гарантируем его состояние и проверяем корректность данных валидацией. Возможно конкретная ситуация требует ещё каких-то действий. Но проблемы тут явно не из микросервиски растут.
> Простите, но это явное предвзятое мнение.
Да это вообще не мнение, а фигура речи. У меня нет никаких претензий к инструментам, включая микросервисы, у меня они только к евангилистам, которые взялись инструмент узкого применения проповедовать, как очередную серебряную пулю.

> /* о валидации и ошибках */
Ошибки можно допускать всегда и везде, и очень часто они проживают достаточно долго. Другое дело, что добавляется ещё один уровень, на котором эти ошибки могут допускаться.
Валидация — это хорошо, это очень хорошо, почти так же хорошо, как и юнит-тестирование, но это тоже не серебряная пуля. И в файлах валидации тоже можно допустить досадные ошибки. Причём, такие, что, утрируя, из-за пропущенного слеша или запятой валидация будет успешной даже на левых данных. А ошибочные интерфейсы и запросы проверяют ох как редко.

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

В целом, я не склонен с вами спорить в том, что правильно спроектированные микросервисы могут быть очень удобны и полезны, ибо есть никсовый терминал и огромный список BSD утилит, делающих ровно по одной вещи, но хорошо. Я просто напомню вам, что, какими бы хорошими они не были, GNOME написан не на них. Как бы ни были хороши микросервисы, внедрять их имеет смысл только там, где накладные расходы от их внедрения и работы окажутся меньше текущих. А если дешевле по старинке подгружать библиотеки и шарить данные между потоками, не надо выискивать способ этого не делать.
У нас в компании есть таки пример идеального микросервиса — конвертор XML в PDF: никаких зависимостей или связей. Многие приложения его успешно используют чтобы отрендерить печатную версию произвольной страницы.
Все остальные более или менее связаны: сервис учета покупок не может без сервиса покупателей, склад не может без покупок и покупателей, учет гарантийного обслуживания не может без всего перечисленного, и т.п.
Ну хорошо, на компоненты побили, все в разных базах, масштабируемо и т.п. Красота. Потом бизнес начинает требовать отчеты, в которых эти все данные надо опять соединить. И тогда надо либо внедрять Data Warehouse, либо самим писать интерфейсы для передачи данных в общую базу, либо писать отчеты, которые смогут тянуть и Join-ить данные из разных БД без лишних тормозов. И то, и другое, и третье дорого, как в плане разработки, так и особенно поддержки.
Поэтому общая цена может оказаться сильно выше.
Конвертор XML в PDF — а это как? XML же не имеет визуального представления своих данных… или есть какой-то шаблон, а по нему строится pdf-документ? Или имелось в виду xhtml?
Все эти статьи про микросервисы не учитывают одну важную деталь — если у вас монолит из говна и палок, то распилив это поделие на микросервисы ничего хорошего не выйдет:

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

Вот в этом я не могу согласиться. Если эти сервисы взаимодействуют между собой (не важно, обычные сокеты, или http поверх, или очередь какая-то вроде RabbitMQ) и нужно организовать их доступность, то всё очень усложняется. Тут помимо разработки ещё усложняется администрирование и т.п. Так что, не всё так однозначно.
Вообще тема это Self-Contained Systems — и модульное и не настролько раздробленное. Модульность продиктована бизнес функционалом, а не желанием каждую функцию в сервис обернуть.
В микросервисах главная проблема — гранулярность и повышенное желание поболтать друг с другом через сетевые интерфейсы. На коленке всю эту инфраструктуру поднять тяжеловато будет — приходится использовать всякие Azure Service Fabric(который весьма неплох). Это все в любом случае усложняет процесс разработки, отладки и администрирования. Для всего нужна золотая середина.
А кто что использует в качестве шины, чтобы связать между собой весь ворох этих микросервисов и минимизировать latency? RPC какой-нибудь поверх rabbitmq или что-то ещё?
У нас лично сервис дискавери через consul.io
Сами сервисы обмениваются кто по обычным tcp сокетам, кто по http между собой. В некоторых местах использовали RabbitMQ, но решили что обычные http запросы с keep-alive соединением как-то попроще.
Для RPC есть gearman, хотя он выглядит заброшенным, я для одного проекта использовал этот велосипед (на GoLang), работает стабильно. В плане RPC он быстрее чем RabbitMQ в несколько раз.
zeromq можно, но для RPC он не очень, вызовы блочатся на разрывах, в итоге приходится городить костыли вокруг.
Вот почему когда я читаю такие статьи мне представляется икающий Роберт Мартин?

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

И самый главный критерий необходимости начать переходить на микросервисную архитектуру — когда стоимость добавления новой фичи начинает превосходить выгоду от этой самой фичи.

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

«Отказоустойчивость», «распределенность», «масштабируемость»

Это тоже просто фичи проекта, которые дешевле реализовывать поверх хорошего кода.
Выводы напомнили как Лесли Нильсен в роли президента США готовился произносить речь «Подъем экономики — хорошо, спад экономики — плохо...» :)
Sign up to leave a comment.

Articles