Этот текст не объясняет, что такое микросервисы и как работает такая архитектура. Но если вы впечатлены историей успеха микросервисов и надеетесь на них как на панацею в своем приложении — этот материал написан для вас. Этот текст о сложностях, с которыми вы столкнетесь при использовании микросервисов, а также об ответственности разработчиков при выборе инструментов для работы.
- Ваше приложение действительно настолько велико, что его можно разбить на микросервисы?
- Отдельные компоненты приложения действительно нуждаются в масштабировании?
- Есть ли у вас транзакции, связанные с услугами?
- Сервисам необходимо часто общаться друг с другом?
- И еще немного фактов
- Итоги
Примечание — Это адаптированный перевод статьи STOP!! You don’t need Microservices Эбина Джона, архитектора сетей и веб-разработчика. Повествование ведётся от лица автора оригинала.
Впервые я услышал о микросервисах в 2013 году из видео на YouTube, посвященном архитектуре Netflix. Это потрясающая история успеха, но тогда я не придал ей значения. Она была слишком сложной для меня — человека, который на тот момент только начал постигать принципы разработки.
Затем наступил бум микросервисов, — а проект, в котором я работал, перешел на эту архитектуру. Буду честен: широкие возможности модульной архитектуры тогда были скрыты от меня. И я, будучи невежественным разработчиком, который старается держаться подальше от DevOps, просто добавил дополнительный слой плотности.
Спустя пять лет я работал над другим проектом и с другими людьми. Тогда я столкнулся с множеством проблем, которые возникли из-за плохо спроектированных микросервисов в сочетании с результатом работы девопсов-любителей. Тогда туман рассеялся и я увидел, насколько хрупки и несовершенны микросервисы — это заставило меня заново взглянуть на архитектуру в целом. Лучше поздно, чем никогда.
Если вы архитектор или дизайнер сервисов, который раздумывает над внедрением микросервисов как архитектуры по умолчанию, призываю вас задать себе несколько вопросов.
Читайте также: DevOps — что это такое и почему эти практики меняют мир разработки уже сейчас
Ваше приложение действительно настолько велико, что его можно разбить на микросервисы?
Не все приложения достаточно велики, чтобы их можно было разбить на отдельные, более мелкие части. Микросервисы, как следует из названия, представляют собой набор служб, у каждой из которых есть определенная функция. В идеальном мире каждая из них представляет собой законченное приложение.
На графике ниже — гипотетическое сравнение «затрат на строку кода» между микросервисами и традиционным монолитом. Микросервисы требуют больших ресурсов (времени и вычислительных мощностей) на старте, но чем больше будет сервис, тем дешевле они обходятся. Стоимость — это то, о чем должен думать каждый. Если вас это не заботит, вероятно, вам вообще не стоит принимать такие решения.
Ваша база кода будет расти в будущем, а это может добавить новый уровень сложности. Но не стоит забывать, что правильно разработанный код всегда можно перевести на микросервисы при приближении к точке пересечения графиков.
Отдельные компоненты приложения действительно нуждаются в масштабировании?
Объясню на примере. Допустим, product owner пришел к вам с идеей сделать новый сервис — систему управления персоналом (HRMS) для организаций, численность сотрудников которых превышает 10 тысяч человек. Энтузиаст технологий внутри вас сразу предложит решение — микросервисная архитектура. Дальше происходит примерно следующее:
На картинке показан крайний случай, но он помогает понять суть вопроса. Одно из главных преимущества использования архитектуры микросервисов — простота масштабирования отдельно взятого компонента. Можно найти множество приложений, в которых этот принцип сработает, но нужно ли это вашему приложению?
Есть ли у вас транзакции, связанные с услугами?
Здесь речь пойдет, вероятно, о самом стратегически важном вопросе, ведь транзакции, охватывающие несколько сервисов, отвечают за всю архитектуру. Распределение транзакций между службами может привести к их взаимной блокировке в результате серии тупиковых ситуаций, которые трудно отследить. Кроме того, возникает перманентное состояние гонки (race condition), которое может нанести ущерб работоспособности всего сервиса и портить кровь инженерам.
Cервисы REST не имеют состояния по определению и не должны участвовать в транзакциях, которые проходят по нескольким сервисам. В мире высокой производительности двухфазная фиксация (2PC) — ненужное зло. А протокол SAGA добавит еще один уровень сложности, к которому вы можете быть не готовы.
«Микросервисы в конечном счете создают проблемы согласованности, поскольку они стремятся к децентрализации управления данными. С монолитом вы можете выпустить множество обновлений за одну транзакцию. Микросервисам для обновления тех же данных требуется больше ресурсов, в то время как распределенные транзакции не приветствуются (по уважительной причине). Разработчики должны знать о проблемах с согласованностью и иметь под рукой инструмент, способный обнаружить асинхронность между службами прежде чем писать код, о котором потом пожалеют» — писал программист Мартин Фаулер.
Итак, можно ли добиться того, чтобы транзакции охватывали разные службы? Конечно! Но стоит ли делать цепочку действий поверх сервисов без состояния (stateless)? Вероятно, нет.
Читайте также: Как устроен функциональный диалект Лиспа Clojure и почему использующие его программисты восхищаются им
Сервисам необходимо часто общаться друг с другом?
В традиционном монолитном сервисе каждый экземпляр микросервиса представлен в виде модуля системы. Связь между ними осуществляется в памяти, а задержка близка к нулю. Внедрение микросервисов означает, что коммуникация между службами переходит от транзакций в памяти к сетевым инструкциям.
Есть множество проверенных решений этой проблемы, но все они имеют свою цену — задержку. Переход от транзакций, которые выполняются в памяти, к сетевой связи, увеличивает единицу задержки с наносекунд (нс) до микросекунд (мс).
Представьте, что три разных сервиса общатся друг с другом по сети. Если каждый вызов службы занимает 100 мс (а так нередкое бывает под нагрузкой), 300 мс придется тратить только на ожидание ответа сети.
Некоторые приложения по своей природе тесно интегрированы с компонентами и сервисами. Дополнительный уровень связи, который увеличивает задержку, может привести к катастрофическим результатам — например, в приложениях, которые обрабатывают данные в реальном времени. Представьте, что вы решили увеличить задержку связи диспетчеру в аэропорту или хирургу во время операции — это почти то же самое.
И еще немного фактов
Дополнительная сложность. Да, сложность решения не может быть определена количественно и сравнивать ее можно только в относительном выражении. Хотя изначально микросервисы предназначены для того, чтобы упростить архитектуру приложения за счет разделения монолита на части, микросервисная архитектура сложна в развертывании и обслуживании.
Стоимость развертывания. Микросервисы — это распределенные системы с молекулярной структурой, а распределение имеет свою цену. Если монолит можно развернуть на одной виртуальной машине или в контейнере, то каждой службе в микросервисной структуре (по крайней мере, в идеальном мире) требуется отдельная виртуальная машина или контейнер. Их объем меньше, чем у монолита, но, скорее всего, они обойдутся дороже даже без учета стоимости обслуживания.
Команда DevOps. Без команды DevOps поддерживать и контролировать микросервисы не получится, а найм таких специалистов может и помочь, и навредить компании. С одной стороны, DevOps — это широко распространенное и проверенное операционное решение. Но если компания небольшая, найм таких специалистов скорее принесет убытки, чем сделает организацию более прогрессивной.
Высокая связанность компонентов. Некоторые части сервиса тесно связаны друг с другом. Попытка разделить их только для того, чтобы «вписаться» в архитектуру, может закончиться катастрофой. Отсутствие опыта. Отсутствие опыта имеет решающее значение при решении любых проблем — в том числе вопросов, связанных с сервисно-ориентированной архитектурой. Когда дело доходит до микросервисов, ущерб приумножается: если вы разворачиваете сервисы в неправильном порядке или происходит сбой, при котором один из зависимых сервисов выходит из строя, менять что-то может быть слишком поздно.
Сквозное тестирование. Традиционное монолитное приложение позволяет запускать тесты почти мгновенно. Наличие нескольких сервисов с взаимозависимостью приведет к задержке тестирования без жизнеспособной оркестрации. Хаотические контракты на данные. Разработка и хранение контрактов на данные внутри команды сильно отличается от обмена ими между командами. При работе с микросервисами ваша команда может не находиться в одном регионе, не говоря уже об использовании одного и того же языка программирования. Выработка контрактов с данными для особых нужд будет стоить вам времени и места.
Устаревшая база кода. Давайте будем честны — большинство из нас каждый день работает с устаревшей базой кода. Быстро развитие технологий двигает нас вперед, но в то же время они еще больше изолируют нас от legacy-кода. Вы уверены, что только что разработанный фреймворк на RabbitMQ, будет хорошо работать с устаревшим приложением на IBM AIX?
Проблемы при отладке. Каждая служба будет иметь собственный набор файлов журнала для анализа. Чем больше сервисов, тем больше файлов.
Выше я пытался ответить на вопрос, стоит ли использовать микросервисы. Мой ответ — определенно нет.
Итоги
Микросервисы заслужили свою популярность — они помогли решить проблемы, которые в сообществе считались неразрешимыми. Истории Netflix, Uber, SoundCloud и Amazon об адаптации микросервисов для многих стали источниками вдохновения — и успех не ограничивается только потребительскими приложениями. У меня был опыт работы с американским гигантом в сфере здравоохранения и я был очарован возможностями дизайна архитектуры каждый раз, когда открывал исходный код.
Я не стану осуждать вашу доверчивость, если вы внедрили микросервисы пять лет назад. Тогда было другое время и все, что мы можем сделать сейчас — честно рассказать о минусах этой архитектуры. К 2021 году сообщество достаточно обожгло об нее руки. Теперь ясно, что безосновательно внедряя архитектуру микросервисов, вы превратите свой плохой код в плохую инфраструктуру.
Мне нравятся увлеченные программисты — я сам был и остаюсь таковым. Они поклоняются тому, что делают, и переходят все границы, чтобы решить стоящую перед ними проблему. Но руководитель компании не может поступать также — это может стоить организации целое состояние. Мне жаль вас разочаровывать, но микросервисы не должны быть архитектурой приложения по умолчанию — это не та серебряная пуля, которую вы искали. Сохраняйте равновесие с KISS и YAGNI.
Как у энтузиаста, у вас есть право иметь свои технологии-фавориты. Однако важно сделать прагматичный выбор между тем, что вам больше нравится, и тем, что лучше всего подходит.
Всегда продолжайте учиться: На Хекслете есть треки — набор курсов с проектами для разработчиков, которые уже работают, и хотят прокачать свои навыки