Пользователь
0,0
рейтинг
29 января 2015 в 14:20

Разработка → Микросервисы (Microservices) из песочницы

От переводчика: некоторые скорее всего уже читали этот титанический труд от Мартина Фаулера и его коллеги Джеймса Льюиса, но я все же решил сделать перевод этой статьи. Тренд микросервисов набирает обороты в мире enterprise разработки, и эта статья является ценнейшим источником знаний, по сути выжимкой существующего опыта работы с ними.

Термин «Microservice Architecture» получил распространение в последние несколько лет как описание способа дизайна приложений в виде набора независимо развертываемых сервисов. В то время как нет точного описания этого архитектурного стиля, существует некий общий набор характеристик: организация сервисов вокруг бизнес-потребностей, автоматическое развертывание, перенос логики от шины сообщений к приемникам (endpoints) и децентрализованный контроль над языками и данными.

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

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

Для того, чтобы начать рассказ о стиле микросервисов, лучше всего сравнить его с монолитом (monolithic style): приложением, построенном как единое целое. Enterprise приложения часто включают три основные части: пользовательский интерфейс (состоящий как правило из HTML страниц и javascript-а), база данных (как правило реляционной, со множеством таблиц) и сервер. Серверная часть обрабатывает HTTP запросы, выполняет доменную логику, запрашивает и обновляет данные в БД, заполняет HTML страницы, которые затем отправляются браузеру клиента. Любое изменение в системе приводит к пересборке и развертыванию новой версии серверной части приложения.

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

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

image

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

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

Свойства архитектуры микросервисов


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

Разбиение через сервисы

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

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

Архитектура микросервисов использует библиотеки, но их основной способ разбиения приложения — путем деления его на сервисы. Мы определяем библиотеки как компоненты, которые подключаются к программе и вызываются ею в том же процессе, в то время как сервисы — это компоненты, выполняемые в отдельном процессе и коммуницирующие между собой через веб-запросы или remote procedure call (RPS).

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

Другое следствие использования сервисов как компонент — более явный интерфейс между ними. Большинство языков программирования не имеют хорошего механизма для объявления Published Interface. Часто только документация и дисциплина предотвращают нарушение инкапсуляции компонентов. Сервисы позволяют избежать этого через использование явного механизма удаленных вызовов.

Тем не менее, использование сервисов подобным образом имеет свои недостатки. Удаленные вызовы работают медленнее, чем вызовы в рамках процесса, и поэтому API должен быть менее детализированным (coarser-grained), что часто приводит к неудобству в использовании. Если вам нужно изменить набор ответственностей между компонентами, сделать это сложнее из-за того, что вам нужно пересекать границы процессов.

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

Организация вокруг потребностей бизнеса

Когда большое приложение разбивается на части, часто менеджмент фокусируется на технологиях, что приводит к образованию UI команды, серверной команды и БД команды. Когда команды разбиты подобным образом, даже небольшые изменения отнимают много времени из-за необходимости кросс-командного взаимодействия. Это приводит к тому, что команды размещают любую логику на тех слоях, к которым имеют доступ. Закон Конвея (Conway's Law) в действии.

«Любая организация, которая проектирует какую-то систему (в широком смысле) получит дизайн, чья структура копирует структуру команд в этой организация»
— Melvyn Conway, 1967

image
Закон Конвея (Conway's Law) в действии

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

image
Сервисные границы, подкрепленные границами команд

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

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

Насколько большими должны быть микросервисы?

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

Это приводит к вопросу о том, есть ли существенная разница в том, сколько человек должно работать на одном сервисе. На данный момент мы считаем, что оба этих подхода к построению команд (1 сервис на 12 человек и 1 сервис на 1 человека) подходят под описание микросервисной архитектуры, но возможно мы изменим свое мнение в будущем. (прим. перев.: со времен статьи появилось множество других статей, развивающих эту тему; наиболее популярным сейчас считается мнение о том, что сервис должен быть настолько большим, чтобы он мог полностью «уместиться в голове разработчика», независимо от количества строк кода).

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

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

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

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

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

Умные приемники и глупые каналы передачи данных (Smart endpoints and dumb pipes)

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

Комьюнити микросервисов предпочитает альтернативный подход: умные приемники сообщений и глупые каналы передачи. Приложения, построенные с использованием микросервисной архитектуры, стремятся быть настолько незавимыми (decoupled) и сфокусировнными (cohesive), насколько возможно: они содержат собственную доменную логику и выступают больше в качестве фильтров в классическом Unix-овом смысле — получают запросы, применяют логику и отправляют ответ. Вместо сложных протоколов, таких как WS-* или BPEL, они используют простые REST-овые протоколы.

Два наиболее часто используемых протокола — это HTTP запросы через API ресурса и легковесный месседжинг. Лучшее выражение первому дал Ian Robinson: «Be of the web, not behind the web».

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

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

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

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

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

Разбивая монолит на сервисы, мы имеем выбор, как построить каждый из них. Хотите использовать Node.js для простых страничек с отчетами? Пожалуйста. C++ для real-time приложений? Отлично. Хотите заменить БД на ту, которая лучше подходит для операций чтения вашего компонента? Ради бога.

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

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

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

Комьюнити микросервисов ценит сервисные контракты, но не любит оверхеды и поэтому использует различные пути управления этими контрактами. Такие шаблоны как Tolerant Reader и Consumer-Driven Contracts часто используются в микросервисах, что позволяет им эволюционировать независимо. Проверка Consumer-Driven контрактов как часть билда увеличивает уверенность в правильности функционирование сервисов. Мы знаем команду из Австралии, которая использует этот подход для проверки контрактов. Это стало частью их процесса сборки: сервис собирается только до того момента, который удовлетворяет требованиям контракта — элегантный способ обойти диллему YAGNI.

Пожалуй наивысшая точка в практике децентрализованного управления — это метод, популизированный Амазоном. Команды отвечают за все аспекты ПО, которое они разрабатывают, включая поддержку его в режиме 24/7. Подобная деволюция уровня ответственности совершенно точно не является нормой, но мы видим все больше и больше компаний, передающий ответственность командам разработчиков. Netflix — еще одна компания, практикующая это. Пробуждение в 3 часа ночи — очень сильный стимул к тому, чтобы уделять большое внимание качеству написанного кода.

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

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

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

Безусловно, многие практики, используемые в микросервисах, пришли из опыта интеграции сервисов в крупных организациях. Шаблон Tolerant Reader — один из примеров. Другой пример — использование простых протоколов — возник как реакция на централизованные стандарты, сложность которых просто захватывает дух.

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

Множество языков, множество возможностей

Рост платформы JVM — один из последних примеров смешивания языков в рамках единой платформы. Переход к более высокоуровневым языкам для получения преимуществ, связанных с использованием высокоуровневых абстракций, был распространенной практикой в течение десятилетий. Точно так же, как и переход «к железу» для написания высокопроизводительного кода.

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

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

Децентрализованное управление данными предстает в различном виде. В наиболее абстрактном смысле это означает, что концептуальная модель мира у разных систем будет отличаться. Это обычная проблема, возникающая при интеграции разных частей больших enterprise-приложений: точка зрения на понятие «Клиент» у продажников будет отличаться от таковой у команды техподдержки. Некоторые атрибуты «Клиента» могут присутствовать в контексте продажников и отсутствовать в контексте техподдержки. Более того, атрибуты с одинаковым названием могут иметь разное значение.

Эта проблема встречается не только у разных приложений, но также и в рамках единого приложения, особенно в тех случаях когда это приложение разделено на отдельные компоненты. Эту проблему хорошо решает понятие Bounded Context из Domain-Driven Design (DDD). DDD предлагает делить сложную предметную область на несколько контекстов и мапить отношения между ними. Этот процесс полезен как для монолитной, так и для микросервисной архитектур, но между сервисами и контекстами существует естественная связь, которая помогает прояснять и поддерживать границы контекстов.

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

image

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

Подобное использование транзакций гарантирует консистентность, но приводит к существенной временной зависимости (temporal coupling), которая, в свою очередь, приводит к проблемамм при работе с множеством сервисов. Распределенные транзакции невероятно сложны в реализации и, как следствие, микросервисная архитектура придает особое значению координации между сервисами без использования транзакций с явным обозначением того, что консистентность может быть только итоговой (eventual consistency) и возникающие проблемы решаются операциями компенсации.

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

Стандартны, проверенные в бою, vs навязанные стандарты

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

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

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

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

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

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

image

Так как эта статья не про Continuous Delivery, мы уделим внимание лишь паре его ключевых моментов. Мы хотим получать как можно больше уверенности в том, что наше приложение работает, поэтому мы запускаем множество автоматических тестов. Для выполнения каждого шага автоматического тестирования приложение разворачивается в отдельной среде, для чего используется автоматического развертывание (automated deployment).

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

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

image

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

Проектирование под отказ (Design for failure)

Следствием использования сервисов как компонентов является необходимость проектирования приложений так, чтобы они могли работать при отказе отдельных сервисов. Любое обращение к сервису может не сработать из-за его недоступности. Клиент должен реагировать на это настолько терпимо, насколько возможно. Это является недостатоком микросервисов по сравнению с монолитом, т.к. это вносит дополнительную сложность в приложение. Как следствие, команды микросервисов постоянно думают на тем, как недоступность сервисов должна влиять на user experience. Simian Army от Netflix искуственно вызывает (симулирует) отказы сервисов и даже датацентров в течение рабочего дня для тестирования отказоустойчивости приложения и служб мониторинга.

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

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

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

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

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

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

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

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

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

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

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

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

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

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


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

Известные нам пионеры этого архитектурного стиля — это такие компании как Amazon, Netflix, The Guardian, the UK Government Digital Service, realestate.com.au, Forward и comparethemarket.com. Конференции 2013 года были полны примеров команий, движущихся в направлении, которое можно классифицировать как микросервисы, например, Travis CI. К тому же, существует множество организаций, которые уже давно используют то, что мы называем микросервисами, но не используют это название. (Часто это называется SOA, хотя, как мы уже говорили, SOA может являться в самых разных и, зачастую, противоречивых формах.)

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

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

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

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

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

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

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

Ссылка на оригинал статьи: Microservices
Vladimir @vkhorikov
карма
43,0
рейтинг 0,0
Реклама помогает поддерживать и развивать наши сервисы

Подробнее
Реклама

Самое читаемое Разработка

Комментарии (39)

  • +1
    Хотелось бы посмотреть на реальные примеры проектов которые построены по этой архитектуре. А то идея вроде бы интересная но требует довольно много людей для поддержки разных сайтов.
    • 0
      TM?
    • +1
      Netflix. Thumblr. SoundCloud.
    • 0
      uploadcare.com
    • 0
      amazon, e-bay
  • +4
    Спасибо за труд.
  • 0
    Странно что в статье нет ни слова про OSGI. Хотя именно выход осги контейнеров в ентерпрайз, как по мне, и дал возможность делать хорошие не-монолитные приложения ентерпрайз уровня: с разбиением функционала на отдельные бандлы, с очередями, транзакциями и всем тем что раньше было возможно только на j2ee серверах.
    • 0
      OSGI это тоже скорее из области олд-скул SOA
      • 0
        Я не соглашусь. Просто OSGI отлично подходит под определение микросервисов. Но если зависимости приложения не адаптированы для OSGI, то адаптация — дополнительные сложности и боль на проекте.

        В каких-то случаях эта технология — логично выбранный инструмент. В других — дополнительная сложность и проблемы)

        Почитайте посты как современный вариант решения Micro Services the easy way with Fabric8 , Apache Camel for Micro­service Architectures

        У меня есть опыт разработки приложения, которое можно разворачивать как в OSGI контейнере, так и в обычной jvm без OSGI зависимостей благодаря использованию blueprint в OSGI (dependency injection framework) и немного своего кода для деплоймента в обычную jvm
        • 0
          Про fabric8 не слышал, гляну, спасибо. Просто последнее время большой хайп вокруг деплоймента не то, что в виде отдельных процессов, но и процессов в отдельном контейнере. Я имею в виду mesos, marathon, docker и так далее. Оно и понятно — чем больше изоляция, тем больше безопасности и управляемости, но и проблем с latency тоже больше, это понятно. Я так понимаю вы оптимизируете больше под latency, в то время как веб он обычно больше throughput ориентированный.
          • 0
            Про fabric8 мои знания теоретические, документация и общение с архитектором из Red Hat. Я работал с fuse fabric на одну версию раньше. Все никак не попробую деплоймент в kubernetes/CoreOS и мониторинг cAdvisor. За год появилось очень много нового и перспективного
    • 0
      OSGI поидее скорее о модульности сервиса. Может в частном рассмотрении это конечно что-то вроде…
      Но «true» микросервисы это разделении монолитной аппликации на сервисы (процессы) не на модули.
    • 0
      Толковый комментарий

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


      В java возможно развертывание множества микросервисов в рамках одного процесса используя OSGI: что решает проблему с переразвертыванием в процессе работы, а так же четко определенными интерфейсами самих сервисов и реестра в котором они регистрируются. Микросервис в отдельном процессе выгоден в том случае, если требования по отказоустойчивости, времени отклика(настройка сборщика мусора под конкретный сервис с заданным требованием к максимальным паузам GC). Распределенность всегда добавляет накладные расходы и определенные сложности. Все зависит от требования и задачи!

      Тем не менее, использование сервисов подобным образом имеет свои недостатки. Удаленные вызовы работают медленнее, чем вызовы в рамках процесса, и поэтому API должен быть менее детализированным (coarser-grained), что часто приводит к неудобству в использовании.

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

      В случае последовательных синхронных вызовов в распределенной системе большое значение имеют задержки (latency) а не только пропускная способность канала передачи данных(throughput). Асинхронность добавляет дополнительную сложность, но и ускоряет работу во многих случаях. В случае развертывания микросервисов в одном процессе можно выбирать между удаленными вызовами(REST, Distributed OSGI или другие) и локальными в рамках одного процесса

      Читал эту статью Мартина Фаулера в оригинале, как всегда порадовался систематизации от теоретика и гуру того что происходит разработке
  • +2
    коммуницирует с остальными используя легковесные механизмы, как правило HTTP.

    Ох…
    • 0
      Это жизнь. У меня раньше между сервисами бегало всё в JSON-RPC через TCP. А потом потребовалось дать доступ к сервисам для сторонних разработчиков, и всё, оппаньки — пришлось заворачивать всё в HTTP, добавлять OAuth, для событий EventSource (ну хоть его не сильно переусложнили). А куда деваться, если клиентом для сервисов может выступать в т.ч. и одностраничное веб-приложение на js? Плюс пришлось обходится без реестра сервисов (чтобы не усложнять для средних js-программистов и без того непростой внешний интерфейс ещё и дополнительным приключением «найди как подключится к нужному сервису и будь готов к тому, что он может пропасть или поменять адрес»), что не лучшим образом сказалось на динамичности и простоте администрирования всей системы.
      • 0
        Сделали бы midlware, которая с клиентами общается так как им хочется а с Вашей средой как Вам. И безопаснее и проще и имеет много других ++. У меня страшный SOAP завернулся nodejs в json-rpc2 за день.
        • 0
          Сделал бы. Может дальше что-то такое и нарисуется, но пока в этом нет смысла — 98% API всех сервисов, по задумке, должно быть доступно внешним клиентам. В таких условиях проще наоборот, для своих же сервисов организовать точно такой же стиль доступа (через OAuth), как для внешних клиентов.
  • +1
    О, наконец-то как-то назвали то, что я делаю с 2009-го. Это очень хорошо, такую архитектуру надо активно популяризовать. Возможность постоянно работать с проектом, который полностью «помещается в голове» — это реально game changer! Но вот первоначальная разработка архитектуры — интерфейсов и связей между основной массой базовых сервисов, решение проблем поддержания целостности данных без ущерба для производительности — это пока что очень нетривиальная задача, на которую уходит много времени при старте проекта.
    • 0
      Дык не надо всю архитектуру сразу и доконца. ;)
      В этом то и «геймчэнджер» что ошибки прощаются легче, и можно начинать с известных и понятных сервисов… (Аджайл!)
      И все можно достаточно незатратно поменять в процессе, огхраничив последствия изменений в переделах одного сервиса (в идеале конечни).
      А вот продеалть тоже в «Энтерпрайзном» монолите через годик работы — писец :)
      • +1
        О всей сразу речь и не идёт, разумеется. Но пяток-десяток базовых сервисов, без которых все остальные работать вообще не смогут — нужны (реестр сервисов, генератор uid, диспетчер событий, аудит/логи, мониторинг, защита от DoS, ну и прочая аутентификация с авторизацией и минимально нужными им базами юзеров, клиентов, их прав, базовый профайл юзеров, OAuth, etc.). Плюс продумать протоколы, контроль прав доступа, кеширование этих прав, как делать eventual consistency хотя бы для этих сервисов, защиту от зацикливания запросов между сервисами…

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

          А мы вот именно с такой штуки стали делать с микросервисами… и биллинг и центральная аутнетикация…
          Но архитектурных проблемы решаем по ходу дела…
          Проблемы скорее в том что участники проекта не особо опытны и еще не вьехали зачем «столько сервисов»… Ну я новый в их команде… и тут сновыми идеями их учу…
          Каменты типа… «слава богу что новый сервис не стали делать»… Что свидетельствую о том что народ пока не проникся… ;))
  • +2
    Один из упущенных в статье моментов — такая архитектура подразумевает, что все эти микросервисы должны работать полностью асинхронно. Точнее, это мельком упоминается, но не говорится о том, что необходимость писать асинхронный код, почему-то, вызывает большие проблемы у многих разработчиков. Так что микросервисы написанные слабыми командами мы можем вообще никогда не увидеть, именно по этой причине.

    Кстати, требование полной асинхронности, в свою очередь, ведёт к тому, что писать эти микросервисы на языках без поддержки лёгких нитей (вроде Perl или Node.js) значительно сложнее, чем на языках где она есть (вроде Go).
    • 0
      Требования что эти микросервисы обязательно должны использовать лишь ассинхронную модель нет.
      Хотя я согласен с вами, лучше сразу её закладывать. Тоже сталкивался с каким-то срахом у разработчиков перед таким подходом…
      Но в принципе не обязательно и вовсе не везде…
      Тут Кроме ассинхронности куча нестандартынх решений… Например людей приходится убеждать что
      одна большая база данных это уже не торт как в 80/90 тых.
      • 0
        Коллеги, поделитесь мыслями о том — каким образом в SOA или в микросервисной решить проблему, когда ты не можешь декомпозировать сервис на меньшие части просто потому, что несмотря на отдельно различимые предметные области, описываемые в модели БД сервиса, эти модели имеют такой объем внешних ключей друг на друга… что даже при их разделении на отдельные сервисы, они будут абсолютно и полностью зависимыми)

        И при этом получается, что под шиной может сидеть до 10-20 сервисов, данные в БД каждого из которых на 30-40% дублируются, просто из-за реляционных связей, через которые можно связывать другие 60-70% данных этих сервисов)

        Сколько ни искал за последний месяц, так и не нашел внятных предложений о том, как можно вынести логику реляционных связей между крупными моделями в БД — на уровень ESB или ее аналога в микросервисной.
        • 0
          В такой формулировке вопроса — скорее никак. Микросервисы — это про частные решения небольших частных проблем наиболее подходящими для этим проблем способами. Чем более общий вопрос — тем хуже он решается микросервисами. Если ставить вопрос «у меня куча данных, как их распихать по микросервисам», то ответ будет «никак, пихай в одну обычную реляционную субд».

          Но если включить телепатическую машинку и начать гадать по симптоматике, то обычно такая проблема возникает в двух ситуациях: либо Вы пытаетесь делить на части не в том месте — правильное место то, где между частями получится небольшое количество слабых связей, либо Вы пытаетесь решить проблему недостаточным количеством сервисов — попробуйте посмотреть что получится, если разделить всё на ещё более мелкие части.
        • +1
          А ещё имеет смысл присмотреться не к данным и их предметным областям, а под другим углом — к связям между данными. Нередко оказывается, что разные связи нужны совершенно разной функциональности, так что можно для каждой такой функциональности сделать отдельный сервис и изолировать нужные ему связи внутри его собственных данных.
        • 0
          Присоединяясь к powerman дополню, что делить ради деления смысла то может и нет. Если не видите явных причин иметь отдельные сервисы то может не надо вам это…
          В общем пережде чем делить в рамках SOA вам надо задать себе вопрос какую business capability (бизнесс-возможность/способность) должен олицетворять сервис.
          Она должна быть осмысленна иммено с этой стороны. Например, сервис который знает что-то про запчасти, про сотрудников, третий про ресурсы, четвертый бух-учет, и т.д.
          И даже если у сервиса сорудников и сервиса бугучета будут общие обьекты это совершенно не помешает вам (см: BoundedContext: martinfowler.com/bliki/BoundedContext.html )

          Причем в моем понимании сервисы в корпоративной SOA это немног не о том… Можно конечно так её рассматривать. Но всеже…
          Например можно иметь один достаточно сложный сервис в соа который будет сам по себе аппликацией сосотоящей из микросервисов.
          В моем понимании парадигма микросервисов это скорее девелопер-дривен. А СОА это бизнес-дривен.
          • 0
            У меня rule of thumb — всё, что может быть (адекватно, без создания сильных связей между сервисами) вынесено в отдельный сервис — должно быть вынесено! Исключительно из соображений, что чем меньше каждый микросервис — тем проще его поддерживать, и из чем большего количества отдельных микросервисов составляется общий проект — тем гибче его архитектура и тем легче её изменять под меняющиеся бизнес-требования. Вы этот подход назвали «developer-driven», или что-то другое?
            • 0
              Ну да вы с технической и пракатической на это смотрите. Если я правильно понимаю то что вы описывате вы полностью прониклись идеей ;)
              Я в свой фирме тоже в эту сторну толкаю, но люди привыкшие к монолитам пока с недоверием смотрят на мой проект ;)
              Есть частный проектик, где я имею все свободы — и там я тоже руководствуюсь юниксовской филисофией ( do one thing and do it well)

              Просто пару лет я был консалтером и мы продовали «Эндерпрайз СОА». И вот в этом мире другие вещи в фокусе.
              Там речь идет о орхестрации сервисов и их подвешиванием к примеру в сьиты BPM или приочие штуке длай мэнеджмента.
              При этом вопросы о архитектуре самих сервесов или их легковесности не в фокусе тематики…
              И сервисом может оказаться вдруг какая-нибудь страшная API управляющая какими-то сущностями в SAP/R3 или прочее легаси…
              Это конечно не лучшие примеры, но тоже СОА…
        • +2
          Ну в общем о декомпозиции сервисов может показаться интересным вот это: www.infoq.com/articles/microservices-intro
          • 0
            Нда. Спасибо за ссылку. Я там много чего перечитал за эти часы, в общем всё достаточно адекватно.

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

            Закончится всё бедой — вполне обычные ошибки и неудачные подходы к решению проблем, когда они коснутся асинхронности, eventual consistency, fault tolerance и прочих критичных для микросервисов вещей, выльются в гораздо более крупные неприятности, чем они привыкли для монолитных приложений. Вернёмся к этому комментарию лет через пять, убедимся.
            • 0
              Может быть… Но с другой стороны, так хайпует Docker со всей этой (легковесной) экосистемой вокруг него… Что очень обнадеживает
  • 0
    Я имею ввиду вот что. Предположим, у меня есть 2 достаточно громоздких модели данных в БД. Предприятия, автомобили и запчасти. Получается, что я не могу их разделить на 3 (микро)сервиса, ибо внешние ключи. Если я хочу разделить их на 2 — в обоих (микро)сервисах должны быть данные, которые позволят связать данные отсюда, с данными в других.

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

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

    Получается, что без такого дублирования данных в отдельных сервисах не обойтись, т.к. только оно и связывает данные в них?
    • +1
      Вы не о том думаете. Микросервисы — это не про вынос отдельных таблиц из РСУБД в виде отдельных сервисов. Если у Вас реляционная природа данных — оставьте их в РСУБД. Думайте не о данных (БД), а о функциональности (сервис), которая поверх этих данных реализуется. И стройте архитектуру микросервисов так, что большинство новых фич можно было добавлять в виде отдельных сервисов, а не нескольких дополнительных полей и связей в общей базе.

      Если, например, автомобили и их запчасти сильно связаны между собой и большинству фич нужно оперировать одновременно и тем и другим — сделайте отдельный сервис, реализующий все эти фичи, и хранящий автомобили и запчасти в собственной реляционной базе. Так делать можно, иногда нужно, но вообще-то этот сервис скорее всего будет не «микро» — если большинство изменений функциональности будет требовать добавлений и даже изменений API этого сервиса, значит Вы идёте не туда. (К сожалению, пока что у меня складывается впечатление, что enterprise будет делать «микро»сервисы именно таким образом, из чего ничего хорошего не выйдет — сделав из одного монолита 5-10 таких псевдо-микро-сервисов они по сути просто добавят в середину своего монолита массу ненужной сложности с асинхронностью, eventual consistency, etc. — и это будет очень-очень больно в конечном счёте.)

      Ещё может помочь фантазия на тему: а что, если бы всех этих автомобилей и запчастей не было у Вас в базе, и не было достаточно простого способа их туда положить? Представьте себе, что в ТЗ на разработку Вашего приложения было бы сказано, что информацию по автомобилям и/или запчастям нужно получать через REST API какого-нить внешнего сервиса(ов). Как могло бы выглядеть API этого(их) сервиса(ов), предоставляющих информацию по автомобилям и запчастям, но ничего не знающих о конкретных нуждах и фичах Вашего приложения, и как бы Вы реализовывали нужные Вашему приложению фичи в этих условиях? Как выглядела бы реализация Ваших фич? А чтобы исключить рефлекторную попытку всё сделать как обычно — эти внешние сервисы не дают возможность выкачать всю их базу и в дальнейшем её обновлять — например, у них доступ к API платный и/или ограничения на количество вызовов API.
      • 0
        Возможно, это будет в тему
    • 0
      Кто и что является клиентами этой базы данных?
      Какие запросы типичны?
      Если вам кроме широковещательных запросов пригодятся к примеру API только по моделям, только по предприятиям или только по запчастям, то почему бы не разделить на 3 сервиса…
      А по широковещатесльным дергать все 3?
      Но это только попытка понять задачу…
      Может вам всегда надо список деталий, с моделями и предприятими и быстро… а по отдельности никогда не интересно впринципе… тогда и делить может не надо.

      Дело ИМХО в том что нет четкого определения что такое микро (несмотря на то что его дают многие ).
      В одном контексте микросервисы будут умещатся в 100 строк кода…
      В другом это может быть большая штука мэнеджирующая какие-то бизнесс обьекты, которые нет смысла делить.
  • 0
    Так я сам и пишу, что не совсем понимаю метод декомпозиции)) И прошу совета у тех из вас, кто уже разобрался в теме)) Потому что получается, что если исходить из принципиальной автономности сервиса от других — то по мере того как ты все больше и больше моделей в БД и функциональности для них впихиваешь в сервис… в конце-концов, ты возвращаешься к монолитному приложению, от которого пытался уйти.

    Для меня это значит, что либо было выбрано неверное направление декомпозиции. Либо для самой задачи подход SOA или микро-сервисов не является корректным. Однако SOA практически везде, где я о нем читаю, преподносится как панацея)) Вот мне и интересно, предлагает ли SOA какие-то свои способы декомпозиции (ориентированные на функционал сервисов, а не на модели хранения данных)… или все это на совести и фантазии архитектора?)
    • 0
      Вы постоянно отвечаете не на комментарий, а на сам топик, из-за этого если бы я случайно не зашёл в топик то не узнал бы о Вашем ответе. Нужно нажимать на "ответить" под комментарием. Это древняя проблема юзабилити хабра.

      Панацей не бывает.

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

      Так что нет, ни SOA ни микросервисы не предлагают никаких «своих способов декомпозиции» и прочих магических решений проблем, у которых есть только одно единственное решение: сильный архитектор в команде, который сможет адекватно разделить проект на слабосвязанные компоненты. Что действительно предлагают микросервисы — это способ сохранить это разделение в процессе работы над проектом. Ибо, хоть я и сказал выше, что компоненты можно делать как ООП-шные классы, на практике, если их не изолировать действительно непреодолимыми барьерами (вроде сетевого API предлагаемого микросервисами), то разработчики будут нарушать границы между компонентами, и в результате получится обычный цельный комок кода который все боятся трогать.

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

      P.S. На самом деле я много лет назад «придумал» микросервисную архитектуру когда пытался отобразить модульный подход используемый в Limbo (есть такой язык в OS Inferno, фактически он один из языков из которых вырос Go) на языки без лёгких нитей — Perl, Python. И я должен заметить, что оригинальный подход, который я «сэмулировал» в виде микросервисов — намного проще и эффективнее. К сожалению, одних лёгких нитей для него недостаточно, нужна ещё нативная поддержка 9P, которой в Go, насколько мне известно, нет. Другой пример этого же модульного подхода, для разнообразия используя внешние приложения — UNIX шелл и тысяча мелких, ничего не знающих друг о друге команд, работающих совместно через конвейеры. Так что цимес вовсе не в микросервисах, а в правильной декомпозиции и умении её сохранять в процессе развития проекта.
    • 0
      Очевидно нет универсального метода декомпозиции, есть несколько эвристик и опыт.
      И как обычно в архитектурах куча Trade-offs :)

Только зарегистрированные пользователи могут оставлять комментарии. Войдите, пожалуйста.