Хотя мысль об этом витала уже некоторое время, необходимость ее записать возникла недавно, после захватывающего разговора с парой человек. Меня пригласили для того, чтобы достичь кворума при решении одной весьма интересной проблемы: "что такое микросервисы и должны ли мы следовать этой архитектуре для нашего ПО?".
По первой части вопроса договорились легко, а вот вторая встала весьма остро. Через пару минут общения часть фактов была собрана:
- Есть желание использовать микросервисную архитектуру в будущем продукте, надо это как-то утвердить.
- Кворум был в большей степени нетехническим, чем больше он переходил в "техническую плоскость", тем более неуместным он становился.
- Длинные паузы и отсутствие вопросов подразумевали незнание функционала как веб-сервисов, так и микросервисов.
Я их не осуждал за отсутствие знаний о функциях веб-сервисов или о следствиях работы микросервисов. Собеседники, конечно же, весьма превосходили меня в работе в своей отрасли. Но они хотели запрыгнуть в микросервисы без оценки возможных последствий!
Впервые о микросервисах я узнал еще в 2013 году, это был ролик на YouTube, где пояснялась архитектура сервисов Netflix. Было весьма впечатляюще, но я пропустил все без сожаления, поскольку это было слишком сложно для меня, человека, который тогда только начал постигать принципы разработки. Скоро это стало навязчивой идеей, поскольку на новом проекте объявили о внедрении микросервисов. Разработка проекта была увлекательной, он до сих пор остается лучшим по кодовой базе из тех, что попадали ко мне.
Честно говоря, широкие возможности модульной разработки были где-то далеко от меня, и я просто добавил дополнительный уровень сложности, будучи невежественным разработчиком, скрывавшимся от DevOps. Спустя пять лет я уже работаю с совершенно новым продуктом и другими людьми. У меня куча проблем, возникших из-за плохо разработанных микросервисов, приправленных тактикой девопсов-любителей (девляпсов?, прим. переводчика). Пелена довольно скоро спала, обнажив хрупкость микросервисов. Также я начал смотреть на всю эту архитектуру более опытным взглядом. Было поздно, но лучше поздно, чем никогда.
Видя, что микросервисы играют роль как главного героя, так и главного злодея, я сознательно убедил себя стать "адвокатом дьявола". А если вы архитектор или разработчик, выбирающий микросервисную архитектуру по умолчанию — придержите коней и задайте себе несколько вопросов.
Ваше приложение достаточно большое, чтобы дробить его на микросервисы?
Давайте признаем это. Не все приложения достаточно велики, чтобы их можно было разбить на более мелкие сервисы. Микросервисы, как следует из их названия, предоставляют набор небольших обособленных сервисов, каждый из которых выполняет некую роль. В идеальном мире ожидается, что каждый сервис будет сам по себе полноценным приложением.
На картинке выше представлено сравнение "стоимость строчки кода" между микросервисами и монолитным приложением. Микросервисы дороже, даже в простом случае, поскольку есть минимальная цена ресурсов, необходимых для работников и оборудования. Об этом должен думать каждый, а если вас это не заботит, возможно, вам вообще не стоит принимать такие решения.
Никто не спорит, что в будущем размер кодовой базы может вырасти, а это может добавить новый уровень сложности, но также не стоит забывать о том, что правильно разработанный код всегда можно перевести на микросервисы при приближении к пороговому значению.
Нужно ли на самом деле масштабировать отдельные компоненты приложения?
Предположим, что владелец продукта обратился к вам с идеей системы управления для автоматизации отдела кадров компании, в которой работает от 10.000 сотрудников. Технический энтузиаст внутри вас ответит мгновенно: микросервисная архитектура.
Конечно, это граничный случай, но общий смысл вы уже уловили! Одно из основных преимуществ использования этой архитектуры — простота масштабирования отдельного компонента. Можно найти множество приложений, в которых этот принцип сработает, но нужно ли это вашему приложению?
Есть ли транзакции, распределенные между сервисами?
Возможно, это наиболее сложное и стратегическое решение, ведь транзакции, охватывающие несколько сервисов, отвечают за всю архитектуру. Обработка транзакций в разных сервисах означает совместную блокировку между сервисами, часть таких взаимоблокировок трудноотслеживаемы и создают состояние гонки. Эти плавающие ошибки причиняют вред сервисам, а иногда портят кровь и инженерам.
Cервисы REST не имеют состояния по определению, а также не должны участвовать в транзакциях, которые проходят по нескольким сервисам. В мире высокой производительности двухфазный коммит (2PC) — ненужное зло. Шаблон SAGA добавляет еще один уровень сложности, к которому вы не будете готовы.
Микросервисы создают возможные проблемы целостности данных из-за того, что по своей сути настойчиво предлагают децентрализованное управление данными. В монолитном приложении можно обновить очень многие вещи в одной транзакции, а для обновления этих же данных в микросервисах требуется несколько заходов, в то время как распределенные транзакции не приветствуются (по уважительной причине). Поэтому разработчики должны знать о проблемах целостности, а также выяснять, как выявить отсутствие синхронизации, прежде чем писать код, о котором потом пожалеют. Martin Fowler
Возможно ли иметь распределенные по сервисам транзакции?
Да, конечно.
Стоит ли делать цепочку действий поверх сервисов без состояния (stateless)?
Возможно, не стоит!!
Есть ли нужда в частом общении между сервисами?
В обычном монолитном приложении каждый экземпляр микросервиса предоставляется в виде модуля системы. Связь между модулями работает в памяти с задержкой, близкой к нулю. Внедряя микросервисы не стоит забывать, что связь между сервисами перешла от транзакций в памяти к передаче по проводам.
Есть много проверенных и протестированных решений, но цена у них одна и та же — задержка. При переходе к микросервисам задержка вырастает от наносекунд до микросекунд. Попробуйте представить, что есть три сервиса, передающие данные по сети. Предполагая, что каждый вызов сервиса занимает 100мс (не очень быстро под нагрузкой), вы потратите 300мс только на сеть.
Следует учесть, что некоторые приложения тесно связаны с компонентами и сервисами по своему устройству. Дополнительные издержки связи могут привести к катастрофическим последствиям в приложениях, обрабатывающих данные в реальном времени. Попробуйте представить задержку связи в хирургическом костюме или в системе управления воздушным движением!
Кое-что ещё
- Добавленная сложность — конечно же, она не может быть определена в абсолютном значении, но ее можно сравнить относительно чего-либо. Несмотря на то, что микросервисы изначально разрабатывались для уменьшения сложности за счет разбиения приложения на более мелкие части — их архитектура более сложная в развертывании и обслуживании.
Следует помнить, что усредненная организация в IT не обладает такими же навыками, как команды инженеров Netflix. mike_pfeiffer
- Цена распределенных систем. Микросервисы — распределенные системы с молекулярностью. Но распределенность имеет свою цену. Монолит был бы развернут на большой виртуалке или в некотором предварительно сделанном контейнере. Но микросервисы должны разворачиваться независимо (в идеальном мире) на нескольких виртуалках или контейнерах. Конечно же, они бывают небольшими по размеру, но это стоит также учитывать, и помнить, что я еще не начал говорить о затратах на управление и обслуживание.
- Внедрение DevOps может быть как полезным, так и вредным. DevOps — проверенное и широко распространенное решение, но если вы работаете в маленькой компании, создание команды DevOps может принести больше вреда, чем пользы. Но одно можно сказать точно — нельзя поддерживать и управлять микросервисами без команды DevOps.
- Сильная связность — некоторые приложения сильно связаны по своей природе. Разбиение на микросервисы, чтобы «вписаться» в архитектуру, будет катастрофическим.
- Отсутствие опыта. Отсутствие опыта имеет решающее значение для любой проблемы, и оно не ограничивается только SOA. Но когда дело доходит до микросервисов, это может стать решающим фактором. Если вы разворачиваете приложения в неправильном порядке или произойдет сбой и зависимый сервис упадет — будет уже поздно.
- Сквозное тестирование. Типичное монолитное приложение позволяет запускать тест практически мгновенно. Наличие нескольких служб с взаимозависимостью задержит тестирование без жизнеспособной оркестровки.
- Хаотичные контракты на данные. Разработка и хранение контрактов на данные внутри команды сильно отличается от их распространения среди команд. А когда вы работаете с микросервисами, ваша команда может не находиться в одном регионе, не говоря уже об использовании одного и того же языка программирования. Выработка контрактов с данными для особых нужд будет стоить вам времени и места.
- Устаревшая кодовая база — давайте будем честными. Для большинства из нас работа с устаревшей кодовой базой — это повседневная деятельность. Это хлеб с маслом большинства организаций. Быстро меняющиеся технологические достижения ставят нас впереди всех, но и в то же время они изолируют нас от устаревшей кодовой базы. Вы уверены, что только что разработанный фреймворк, использующий RabbitMQ, хорошо работает с устаревшим приложением, работающим на AIX?
- Устранение неполадок — каждый сервис будет иметь свой собственный набор файлов журналов для анализа. Больше сервисов = больше файлов.
Заключение
Я здесь, чтобы сказать "Не используйте микросервисы"?
Конечно же, нет!!
Микросервисы заслужили известность. Они решили проблемы, которые считались неразрешимыми. История Netflix по применению микросервисов вдохновила многих. И список определенно не останавливается на Netflix. Uber, SoundCloud и гигантский Amazon — вот некоторые примеры, которые у всех на слуху. И не думайте, что истории успеха ограничиваются только потребительскими приложениями. Я имел непосредственный опыт работы с американским гигантом здравоохранения и был очарован возможностями дизайна, каждый раз, когда открывал исходный код.
Если бы вы доверились этой идее пять лет назад — я бы не осуждал вашу доверчивость. Время было другое, но теперь все, что можно сделать — быть честным. Сейчас 2020 год. Мы достаточно обожглись, и вокруг нас слишком много авгиевых конюшен. Внедряя бездумно архитектуру микросервисов, вы просто превращаете свой плохой код в плохую инфраструктуру.
Мне нравятся увлеченные программисты, я сам такой когда-то был таким, да и все еще есть. Они преданы тому, что делают, и переходят все границы, чтобы решить свою проблему. Но вы не можете так же энергично принимать решения, которые могут стоить вам и организации целое состояние, извините за резкость. Микросервисы не должны быть вашей архитектурой приложений по умолчанию. Это не серебряная пуля, которую вы искали. Сохраняйте равновесие с KISS и YAGNI.
Как сторонник технологии и энтузиаст, вы имеете право что-либо предпочитать. Однако то, что показывает вас с лучшей стороны — способность выбирать прагматично, когда варианты находятся между «правильным выбором» и «вашим любимым выбором».