Архитектура ПО
November 3

Микросервисы

Термин «микросервисная архитектура» появился в последние несколько лет для описания особого подхода к разработке программных приложений в виде наборов независимо развертываемых сервисов. Хотя у этого архитектурного стиля нет четкого определения, ему присущи определенные общие характеристики, такие как организация вокруг бизнес-возможностей, автоматическое развертывание, интеллектуальные конечные точки и децентрализованное управление языками и данными.

Авторы: James Lewis, Martin Fowler

«Микросервисы» — ещё один новый термин на оживлённых улицах архитектуры программного обеспечения. Хотя нам свойственно презрительно относиться к таким вещам, эта терминология описывает стиль разработки программных систем, который становится всё более привлекательным. За последние несколько лет мы видели множество проектов, использующих этот стиль, и результаты были настолько положительными, что для многих наших коллег этот стиль стал стандартом для разработки корпоративных приложений. К сожалению, информации о том, что такое микросервисный стиль и как его реализовать, не так много.

Вкратце, микросервисная архитектура 1 — это подход к разработке единого приложения в виде набора небольших сервисов, каждый из которых работает в отдельном процессе и взаимодействует с другими с помощью облегчённых механизмов, часто через API HTTP-ресурсов. Эти сервисы создаются на основе бизнес-возможностей и могут быть развёрнуты независимо друг от друга с помощью полностью автоматизированных механизмов развёртывания. Централизованное управление этими сервисами сведено к минимуму. Они могут быть написаны на разных языках программирования и использовать разные технологии хранения данных.

В моём Руководстве по ресурсам для микросервисов вы найдёте ссылки на лучшие статьи, видео, книги и подкасты о микросервисах.

Чтобы объяснить, что такое микросервисный стиль, полезно сравнить его с монолитным стилем: монолитное приложение представляет собой единое целое.

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

Серверное приложение будет обрабатывать HTTP-запросы, выполнять бизнес-логику, получать и обновлять данные из базы данных, а также выбирать и заполнять HTML-шаблоны для отправки в браузер. Это серверное приложение является монолитом — единым логическим исполняемым файлом. Любые изменения в системе требуют создания и развертывания новой версии серверного приложения.

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

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

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

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

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

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

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

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

Рис. 1. Монолиты и микросервисы

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

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

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

Особенности микросервисной архитектуры

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

Компонентизация с помощью Сервисов

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

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

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

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

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

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

В первом приближении можно заметить, что сервисы сопоставляются с процессами среды выполнения, но это лишь первое приближение.

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

Организация на основе бизнес-возможностей

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

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

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

Другими словами, логика повсюду. Это пример закона Конвея в действии.

Любая организация, разрабатывающая систему (в широком смысле), создаст проект, структура которого будет копией коммуникационной структуры организации.

— Мелвин Конвей, 1968
Рис. 2. Закон Конвея в действии

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

Рис. 3. Границы сервисов, усиленные границами команд

Одна из компаний, организованных таким образом, — это www.comparethemarket.com. Межфункциональные команды отвечают за создание и поддержку каждого продукта, а каждый продукт разделён на несколько отдельных сервисов, взаимодействующих через шину сообщений.

Большие монолитные приложения также всегда можно модульизировать с учетом бизнес-возможностей, хотя это не обычный случай. Certaкроме того, мы бы настоятельно призвали большую команду, создающую монолитное приложение, разделиться по бизнес-направлениям. Впроблема, которую мы видели здесь, заключается в том, что они, как правило, организованы вокруг слишком многих контекстов. Если чеe monolith охватывает многие из этих модульных границ, и отдельным членам команды может быть трудно поместить их в свою кратковременную память. Кроме того, мы видим, что модульные линии требуют строгой дисциплины. Неизбежно более чёткое разделение, требуемое для сервисных компонентов, упрощает соблюдение границ команды.

Насколько велик микросервис?

Хотя «микросервис» стал популярным названием для этого архитектурного стиля, оно приводит к нежелательному акценту на размере сервиса и спорам о том, что считать «микро». В беседах с разработчиками микросервисов мы видим, что сервисы бывают разных размеров. Самые крупные сервисы соответствуют понятию Amazon о команде из двух пицц (т. е. вся команда может прокормиться двумя пиццами), то есть не более чем о дюжине человек. В меньших масштабах мы видели системы, в которых команда из полудюжины человек поддерживала полдюжины сервисов.

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

Продукты, а не Проекты

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

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

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

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

Умные конечные точки и тупые каналы

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

Сообщество разработчиков микросервисов предпочитает альтернативный подход: умные конечные точки и «глупые» каналы. Приложения, созданные на основе микросервисов, должны быть максимально независимыми и согласованными. Они содержат собственную логику предметной области и действуют скорее как фильтры в классическом понимании Unix: получают запрос, применяют соответствующую логику и выдают ответ. Они управляются с помощью простых протоколов REST, а не сложных протоколов, таких как WS-Choreography, BPEL или оркестрация с помощью центрального инструмента.

Чаще всего используются два протокола: HTTP-запросы с ответами и API ресурсов, а также облегчённый обмен сообщениями. Без первого не обойтись.

Будь в сети, а не за её пределами

— Иэн Робинсон

Команды, работающие с микросервисами, используют принципы и протоколы, на которых построена всемирная паутина (и в значительной степени Unix). Часто используемые ресурсы можно кэшировать без особых усилий со стороны разработчиков или администраторов.

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

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

Микросервисы и SOA

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

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

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

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

Децентрализованное Управление

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

Разделяя компоненты монолита на сервисы, мы получаем выбор при создании каждого из них. Выхотите использовать Node. js для создания простой страницы с отчетами? Делайте это. C++ дляособо сложного компонента, работающего в режиме, близком к реальному времени? Отлично. Выхотите заменить базу данных на другой тип, который лучше подходит для чтения данных одним из компонентов? У нас естьтехнология для его пересоздания.

Конечно, если вы можете что-то сделать, это не значит, что вы должны это делать, но разделение системы таким образом означает, что у вас есть такая возможность.

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

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

Для сообщества разработчиков микросервисов накладные расходы особенно нежелательны. Это не значит, что сообщество не ценит контракты на обслуживание. Совсем наоборот, их, как правило, гораздо больше. Просто они ищут другие способы управления этими контрактами. Такие шаблоны, как Tolerant Reader и контракты, ориентированные на потребителя, часто применяются в микросервисах. Это помогает сервуконтракты ice развиваться независимо. Выполнение минусовконтракты, основанные на umer, как часть вашей сборки повышают доверие и обеспечивают быструю обратную связь о том, функционируют ли ваши службы. Действительно, мы знаем из команды в Австралии, которая занимается созданием новых сервисов с помощью контрактов, ориентированных на потребителя. Они используют простые инструменты, которые позволяют им определять контракт на услугу. Это становится part автоматизированной сборки еще до того, как будет написан код для нового сервиса. Сервис затем разрабатывается только до той степени, в которой он соответствует контракту. Это элегантный подход, позволяющий избежать дилеммы «YAGNI»9 при разработке нового программного обеспечения. Эти методы и инструменты, которые развиваются вокруг них, снижают потребность в централизованном управлении контрактами за счёт уменьшения временной связи между сервисами.

Возможно, апогеем децентрализованного управления является принцип «создай и запусти», популяризированный Amazon. Команды отвечают за все аспекты создаваемого ими программного обеспечения, включая его круглосуточную эксплуатацию. Передача такого уровня ответственности определённо не является нормой, но мы видим, что всё больше компаний перекладывают ответственность на команды разработчиков. Netflix — ещё одна организация, которая приняла этот принцип10. Когда вас будят в 3 часа ночи с помощью пейджера, это, безусловно, мощный стимул сосредоточиться на качестве кода. Эти идеи настолько далеки от традиционной модели централизованного управления, насколько это вообще возможно.

Много языков, много вариантов

Развитие JVM как платформы — это лишь последний пример смешения языков в рамках общей платформы. На протяжении десятилетий было принято использовать оболочки для языков более высокого уровня, чтобы воспользоваться преимуществами абстракций более высокого уровня. То есть опускаться до базового уровня и писать код, чувствительный к производительности, на языке более низкого уровня. Однаково многих монолитах нет необходимости в таком уровне оптимизации производительности, а также в DSL и абстракциях более высокого уровня (к нашему разочарованию). Вместо этого монолиты обычно написаны на одном языке, и тенденция такова, что количество используемых технологий.

Децентрализованное управление данными

Децентрализация управления данными проявляется по-разному.На самом абстрактном уровне это означает, что концептуальная модель мира в разных системах будет отличаться. Это распространённая проблема при интеграции в рамках крупного предприятия: представление о клиенте в отделе продаж будет отличаться от представления в отделе поддержки. Некоторые объекты, которые в отделе продаж называются клиентами, могут вообще не отображаться в отделе поддержки. Те, что отображаются, могут иметь разные атрибуты и (что ещё хуже) общие атрибуты с немного отличающейся семантикой.

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

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

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

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

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

Проверенные в боях стандарты и обязательные стандарты

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

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

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

Автоматизация инфраструктуры

За последние несколько лет методы автоматизации инфраструктуры претерпели значительные изменения. Развитие облачных технологий и, в частности, AWS снизило операционную сложность создания, развертывания и эксплуатации микросервисов.

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

Рис. 5. Базовый конвейер сборки

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

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

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

Рис. 6. Развертывание модулей часто отличается

Сделайте так, чтобы поступать правильно было легко

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

Конструкция на случай отказа

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

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

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

Это особенно важно для архитектуры микросервисов, поскольку предпочтение, отдаваемое микросервисами хореографии и совместной работе над событиями, приводит к эмерджентному поведению. Хотя многие эксперты высоко оценивают ценность эмерджентного поведения, правда в том, что иногда оно может быть нежелательным. Мониторинг необходим для быстрого выявления нежелательного эмерджентного поведения, чтобы его можно было исправить.

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

Команды, работающие с микросервисами, ожидают увидеть сложные системы мониторинга и ведения журналов для каждого отдельного сервиса, например информационные панели, отображающие состояние «онлайн»/«офлайн» и различные операционные и бизнес-метрики. Подробная информация о состоянии автоматического выключателя, текущей пропускной способности и задержке — это другие примеры, с которыми мы часто сталкиваемся.

Синхронные вызовы считаются вредными

Каждый раз, когда у вас происходит несколько синхронных вызовов между сервисами, вы сталкиваетесь с мультипликативным эффектом простоев. Проще говоря, это когда простой вашей системы становится результатом простоев отдельных компонентов. Вам предстоит сделать выбор: сделать вызовы асинхронными или управлять простоями. На сайте www.guardian.co.uk на новой платформе действует простое правило: один синхронный вызов на запрос пользователя, а в Netflix при редизайне API платформы асинхронность была встроена в структуру API.

Эволюционный Дизайн

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

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

Веб-сайт The Guardian — хороший пример приложения, которое было спроектировано и создано как монолитное, но развивается в направлении микросервисов. Монолит по-прежнему является основой веб-сайта, но разработчики предпочитают добавлять новые функции, создавая микросервисы, которые используют API монолита. Такой подход особенно удобен для функций, которые по своей сути являются временными, например для специализированных страниц, посвященных спортивным событиям. Такую часть веб-сайта можно быстро создать с помощью языков быстрой разработки и удалить после завершения мероприятия. Мы видели подобные подходы в финансовом учреждении, где новые услуги добавляются для удовлетворения потребностей рынка, а через несколько месяцев или даже недель от них отказываются.

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

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

За микросервисами будущее?

Наша главная цель при написании этой статьи — объяснить основные идеи и принципы микросервисов. Мы потратили время на написание этой статьи, потому что считаем, что архитектурный стиль микросервисов — это важная идея, заслуживающая серьёзного рассмотрения при создании корпоративных приложений. Недавно мы создали несколько систем в этом стиле и знаем других разработчиков, которые используют и предпочитают этот подход. Среди тех, кто, насколько нам известно, в той или иной степени является первопроходцем в области архитектурного стиля, — Amazon, Netflix, The Guardian, Цифровая служба правительства Великобритании, realestate.com.au, Forward и comparethemarket.com. На конференциях в 2013 году было много примеров компаний, которые переходили на микросервисную архитектуру, включая Travis CI. Кроме того, существует множество организаций, которые уже давно используют то, что мы называем микросервисами, но никогда не используют это название. (Часто это называют сервис-ориентированной архитектурой, хотя, как мы уже говорили, сервис-ориентированная архитектура имеет множество противоречивых форм.

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

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

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

Недостатки микросервисов

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

Мартин Фаулер

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

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

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

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

Перевод: https://martinfowler.com/articles/microservices.html