как разбить приложение на микросервисы
Разбиваем монолит на микросервисы
В настоящее время, задача разбивки монолита на микросервисы приобрела поистине огромную популярность в бизнес среде. Как будто все корпорации России вдруг осознали все перспективы “новой” архитектуры, получили при этом пинок от начальства и бросились брать под козырек.
Ретивость корпоративных чиновников, как всегда, безумна и беспощадна. И вот опять, огромные деньги, выделенные на пилоты, бездарно тратятся на то, что в лучшем случае никогда не взлетит, а в худшем случае будет внедрено, невзирая на все ошибки. И, дамы и господа, удручает больше всего то, что это явление отнюдь не единичное. Оно характерно для всей ИТ-отрасли России.
В настоящей статье я предлагаю обсудить какие возникают ошибки при разбивке монолита на микросервисную архитектуру (МСА), почему они возникают, к чему приводят. Ну а в конце я опишу как должен быть организован эффективный и правильный процесс перевода на МСА и какой должна стать архитектура микросервисной системы.
Итак, ваша организация решила разбить старый, добрый монолит на микросервисы. Почему? Ясного ответа никто дать как правило не может, но в целом все начинают описывать как им плохо живется с монолитом. Да, релизные циклы действительно очень зацеплены за разные команды. А почему вы думаете, что в МСА по-другому? Кто-то говорит, что микросервисы гораздо лучше масштабируются. Ну можно сделать и так, а вам зачем это? Нет, может и надо, но вы понимаете зачем или просто подсмотренную фразу используете? Говорят, каждый микросервис гораздо менее требователен к ресурсам: памяти, базе данных и т.д. Ну и что? У вас деньги на сервера иссякли? Бизнес ценность-то в чем?
Коллеги, я не хочу сказать, что у МСА нет преимуществ. Эти преимущества есть. Но я хочу сказать, что какие бы цели вы ни ставили при старте проекта разбивки, вы их скорее всего ставите неправильно и никогда не достигните. Не достигните просто потому, что реализацией проекта у вас будут заниматься те люди, которые довели до состояния бездарно написанного кода ваш монолитный проект. При этом все подходы при реализации МСА настолько отличаются от монолитной, насколько телефонная лапша 20-го века отличается от сетевых технологий нынешнего 21-го века. И аналогия здесь действительно очень удачная. ТФоП доставляет в квартиру одну телефонную линию. Обрыв на любом участке линии приводит к выходу ее из строя. Сетевые технологии динамичны и позволяют строить альтернативные маршруты. Даже выход из строя 60% всей сетевой инфраструктуры страны в результате ядерной войны, не приведет к отказу оставшихся сетей.
Т.е., главное преимущество МСА перед монолитом — это динамичность, которая способна приводить к чрезвычайной надежности системы. Тут надо пояснить: из самой МСА никак не следует, что она будет надежна. МСА лишь дает вам возможность строить надежные системы. Но это ваша ответственность заложить в архитектуру вашей системы надежность и дублирование функционала.
А сейчас разберем повсеместно встречающиеся ошибки при развитии микросервисной системы предприятия.
Это реальная история неудачно спроектированной микросервисной системы. Такой финал ждет подавляющее большинство пилотов, которые сейчас стартовали. А все из-за ошибок, которые я перечислил выше.
А сейчас я хочу рассказать как надо разрабатывать микросервисную систему.
Заключение
В заключение хочу еще раз отметить, что я ни в коем случае не против разбиения монолитных систем на микросервисы. Напротив, я считаю, что такой подход может привести к значительно большей устойчивости вашей ИТ-инфраструктуры. Бездумное внедрение элементов микросервисной архитектуры без понимания общей теории построения подобных систем, полное игнорирование практики отрасли и вообще здравого смысла — вот что отправляет ваши пилоты на свалку истории. Только ошибки людей вашей организации приносят слезы и убытки ваших инвесторов.
Напротив, разумный подход может действительно ускорить ваши сервисы и сделать их чрезвычайно устойчивыми. Поэтому очень хочу пожелать мудрости всем, кто занимается развитием ИТ в компаниях.
Microservices. Как правильно делать и когда применять?
Автор: Вячеслав Михайлов
Монолитные приложения и их проблемы
Все прекрасно знают, что такое монолитное приложение: все мы делали такие двух- или трехслойные приложения с классической архитектурой:
Для маленьких и простых приложений такая архитектура работает прекрасно, но, допустим, вы хотите улучшить приложение, добавляя в него новые сервисы и логику. Возможно, у вас даже есть другое приложение, которое работает с теми же данными (например, мобильный клиент), тогда архитектура приложения немного поменяется:
Так или иначе, по мере роста и развития приложения, вы сталкиваетесь с проблемами монолитных архитектур:
Рано или поздно вы понимаете, что уже ничего не можете сделать со своей монолитной системой. Заказчик, конечно, разочарован: он не понимает, почему добавление простейшей функции требует нескольких недель разработки, а затем стабилизации, тестирования и т. д. Наверняка многие знакомы с этими проблемами.
Развитие системы
Предположим, что вы каким-то образом смогли избежать вышеозначенных проблем и все еще справляетесь со своей системой, но ведь вам наверняка нужно развивать и масштабировать ее, особенно если она приносит в компанию серьезные деньги. Как это сделать?
Три измерения масштабирования
В книге “The Art of Scalability” есть понятие «куб масштабирования» (scale cube) — из книги “The Art of Scalability”. По этому кубу мы видим, что существует три ортогональных способа увеличения производительности приложения: sharding, mirrorring и microservices.
Теорема CAP
Вообще говоря, если мы хотим развивать систему, придется решать следующие вопросы:
Эти вопросы приводят нас к CAP-теореме, сформулированной Брюером в 2000 г.
Теорема это состоит в том, что вы, теоретически, не можете обеспечить системе одновременно и согласованность (consistency), и доступность (availability), и разделяемость (partitioning). Поэтому приходится жертвовать одним из трех свойств в пользу двух других. Так же, как при выборе из «быстро, дешево и качественно» приходится выбирать только два варианта. Теперь рассмотрим разные варианты, которые у нас есть согласно теореме CAP.
CA — consistency + availability
При таком раскладе данные во всех узлах у нас согласованы и доступны. Доступность здесь означает, что вы гарантируете отклик за предсказуемое время. Это время не обязательно маленькое (это может быть минута или больше), но мы это гарантируем. Увы, при этом мы жертвуем разделением на секции — не можем развернуть 300 таких хостов и распределить всех пользователей по этим хостам. Так работать система не будет, потому что не будет согласованности транзакций.
Яркий пример CA — ACID-транзакции, присутствующие в классических монолитах.
CP – consistency + partitioning
Следующий вариант — когда данные во всех узлах согласованы и распределены на независимые секции. При этом мы готовы пожертвовать временем, которое требуется на согласование всех транзакций — отклик будет очень долгим. Это значит, что, если два пользователя последовательно будут запрашивать одни и те же данные, неизвестно, как долго будут согласовываться данные для второго пользователя.
Такое поведение характерно для тех монолитов, которым пришлось масштабироваться, несмотря на древность.
AP — availability + partitioning
Последний вариант — когда система доступна с предсказуемым временем отклика и распределена. При этом нам придется отказаться от целостности результата — наши данные больше не консистентны в каждый момент времени, и среди них появляются устаревшие (от микросекунд до дней). Но, на самом деле, мы всегда оперируем старыми данными. Даже если у вас трехзвенная монолитная архитектура с веб-приложением, когда веб-сервер отдал вам пакет с теми данными, которые вы отобразили для пользователя, они уже устарели. Ведь вы никоим образом не знаете, не пришел ли в этот момент кто-то другой и не поменял эти данные. Так что то, что данные у нас согласованные в конечном счете(eventually consistent) — нормально. «Согласованные в конечном счете» означает, что, если на систему перестанет влиять внешнее воздействие, она придет в согласованное состояние.
Яркий пример — классические DNS-системы, которые синхронизируются с задержкой до дней (во всяком случае, раньше).
Теперь, ознакомившись с теорией CAP, мы понимаем, как можем развивать систему так, чтобы она была быстрой, доступной и распределенной. Да никак! Придется выбрать только два свойства из трех.
Микросервисная архитектура
Что же выбрать? Чтобы сделать правильный выбор, нужно, прежде всего, задуматься, зачем все это нужно, — необходимо четко понимать бизнес-задачи. Ведь решение в пользу микросервисов — очень ответственный шаг. Дело в том, что в микросервисах все значительно сложнее, чем обычно, т. ч. мы можем столкнуться с такой ситуацией:
Вот нельзя просто взять и распилить все на какие-то куски и сказать, что это теперь — микросервисы. Иначе вам придется очень несладко.
Microservices & SOA
А теперь поговорим еще немного о теории. Вы все прекрасно знаете, что такое SOA — сервисно-ориентированная архитектура. И тут у вас наверняка возникнет вопрос, как же SOA соотносится с микросервисной архитектурой? Ведь, казалось бы, SOA — то же самое, о это не совсем так. На самом деле, микросервисная архитектура — частный случай SOA:
Другими словами, микросервисная архитектура — всего лишь набор более строгих правил и соглашений, как писать все те же сервисы SOA.
Что такое микросервисы?
Это архитектурный шаблон, в котором сервисы:
Теперь разберем эти понятия по отдельности.
Что значит «маленький» сервис? Это значит, что сервис в микросервисной архитектуре не может разрабатываться больше чем одной командой. Обычно одна команда разрабатывает где-то 5 – 6 сервисов. При это каждый сервис решает одну бизнес-задачу, и его способен понять один человек. Если же не способен, сервис пора пилить. Потому что, если один человек способен поддерживать всю бизнес-логику одного сервиса, он построит действительно эффективное решение. Ведь бывает так, что зачастую люди, принимая решения в процессе написания кода, просто-напросто не понимают, что именно делают — не знают, как ведет себя система в целом. А если сервис маленький, все намного проще. Этот подход, кстати, мы можем применять отдельно, даже не следуя микросервисной архитектуре в целом.
Что значит «сфокусированный» сервис? Это значит, что сервис решает только одну бизнес-задачу, и решает ее хорошо. Такой сервис имеет смысл в отрыве от остальных сервисов. Другими словами, вы его можете выложить в интернет, дописав security-обертку, и он будет приносить людям пользу.
Что такое «слабосвязанный» сервис? Это когда изменение одного сервиса не требует изменений в другом. Вы связаны посредством интерфейсов, у вас есть решение через DI и IoC — это сейчас стандартная практика, применять которую нужно обязательно. Обычно разработчики знают, почему 🙂
Что такое «высокосогласованный» сервис? Это значит, что класс или компонент содержит все нужные методы решения поставленной задачи. Однако тут часто возникает вопрос, чем высокая согласованность (high cohesion) отличается от SRP? Допустим, у нас есть класс, отвечающий за управление кухней. В случае SRP такой класс работает только с кухней и больше ни с чем, но при этом он может содержать не все методы по управлению кухней. В случае же высокой согласованности, все методы по управлению кухней содержатся только в этом классе, и больше нигде. Это важное различие.
Характеристики микросервисов
Разделение на компоненты (сервисы)
Компоненты бывают двух видов: библиотеки и сервисы, которые взаимодействуют по сети. Мартин Фаулер определяет компоненты как независимо заменяемые и независимо развертываемые. Т. е., если вы можете взять что-то и спокойно заменить на новую версию, — это компонент. А если что-то связано с другим и их независимо заменить нельзя (нужно учитывать контракты, сборки, версии…) —- они вместе образуют один компонент. Если что-то нельзя развернуть независимо, и требуется логика откуда-то еще, это тоже не компонент.
Группировка по бизнес-задачам (сервисы имеют бизнес-смысл)
Вот стандартная компоновка монолита:
Для повышения эффективности разработки вы также зачастую вынуждены делить по этим слоям и команды: есть команда, которая занимается UI, есть команда, которая занимается ядром, и есть команда, которая разбирается в БД.
Если же вы переходите к микросервисной архитектуре, сервисы и команды делятся по бизнес-задачам:
Например, может быть группа, которая занимается управлением заказами, — она группа может обрабатывать транзакции, делать по ним отчеты и т. д. Такая группа будет заниматься и соответствующими БД, и соответствующей логикой, и, может быть, даже UI. Впрочем, в моем опыте UI распиливать пока не удавалось — его приходилось оставлять монолитным. Может быть, нам удастся сделать это в будущем, тогда обязательно расскажите остальным как вы этого добились. Как бы то ни было, даже если UI остается монолитным, все равно гораздо лучше, когда остальное разбито на компоненты. Тем не менее, повторюсь, очень важно понимать, ЗАЧЕМ вы это делаете — иначе однажды придется все переделывать обратно.
Умные сервисы и простые коммуникации
Есть разные варианты взаимодействия сервисов. Бывает, что берут очень умную шину, которая знает и про роутинг, и про бизнес-правила (допустим, какой-нибудь BizTalk), и к сервисам прилетают уже готовые объекты. Тогда получается очень умный middleware и глупые endpoint’ы. Это, на самом деле, — антишаблон. Как показало время (на примере того же интернета), у нас очень простая и незатейливая среда передачи данных — ей абсолютно все равно, что вы передаете, она ничего не знает про ваш бизнес. Все мозги же сидят в сервисах. Это важно понимать. Если же вы будете все складывать в среду передачи, у вас получится умный монолит и тупые сервисы-обертки баз данных.
Децентрализованное хранение
С точки зрения сервисно-ориентированных архитектур и, в частности, микросервисов, децентрализованное хранение — очень важный момент. Децентрализованное хранение значит, что каждый сервис имеет свою и только свою БД. Единственный случай, когда разные службы могут использовать одно хранилище, — если эти службы представляют собой точные копии друг друга. Базы данных друг с другом не взаимодействуют:
Единственный вариант взаимодействия — сетевое взаимодействие между сервисами:
Middleware здесь может быть разный — мы об этом еще поговорим. Обнаружение сервисов и взаимодействие между ними может происходить просто напрямую, через вызов RPC, а может и через какой-нибудь ESB.
Автоматизация развертывания и мониторинга
Автоматизация развертывания и мониторинга — то, без чего к микросервисной архитектуре лучше даже не подходить. Т. ч. вы должны быть готовы в это инвестировать и нанять DevOps-инженера. Вам обязательно понадобится автоматическое развертывание, непрерывная интеграция и поставка. Также вам понадобится непрерывный мониторинг, иначе вы просто не сможете уследить за всеми многочисленными сервисами, и все превратится в какой-то ад. Здесь полезно использовать всякие полезные штуки, которые помогают централизовать логгирование, — их можно не писать, т. к. есть хорошие готовые решения вроде ELK или Amazon CloudWatch.
Design for Failure (Chaos Monkey)
С самого первого этапа, начиная строить микросервисную архитектуру, вы должны исходить из предположения, что ваши сервисы не работают. Другими словами, ваш сервис должен понимать, что ему могут не ответить никогда, если он ожидает каких-то данных. Таким образом, вы сразу должны исходить из ситуации, что что-то у вас может не работать.
Например, для этого компания Netflix разработала Chaos Monkey — инструмент, который ломает сервисы, хаотически их выключает и рвет соединения. Этот нужно, чтобы оценить надежность системы.
Как сервисы будут друг с другом взаимодействовать?
Возьмем пример простого приложения. Картинки, приведенные ниже, я взял из блога Криса Ричардсона на NGINX — там детально рассказывается, что такое микросервисы.
Итак, допустим, у нас есть какой-то клиент (необязательно даже UI-ный), который, чтобы предоставить кому-то нужные данные, взаимодействует с совокупностью других сервисов.
Казалось бы, все просто — клиент может обращаться ко всем этим сервисам. Но на деле это выливается в то, что конфигурация клиента становится очень большой. Поэтому существует очень простой шаблон API Gateway:
API Gateway — первое, что нужно рассматривать, когда вы делаете микросервисную архитектуру. Если у вас в бэкенде некоторое количество сервисов, поставьте перед ними простейший сервис, задача которого — собирать бизнес-вызовы к целевым сервисам. Тогда вы сможете осуществлять маппинг транспорта (транспорт будет не обязательно REST API, как на картинке, а каким угодно). API Gateway предоставляет данные в том виде, в каком они нужны конкретно именно этому типу пользователей. Например, если будет веб- и мобильное приложение, у вас будет два API Gateway, которые будут собирать данные из сервисов и предоставлять их немного по-разному. API Gateway не должен ни в коем случае содержать никакой серьезной бизнес-логики, иначе бы эта логика везде дублировалась, и ее сложно было бы поддерживать. API Gateway только передает данные, и все.
Разные типы микросервисной архитектуры
Итак, допустим, у нас есть UI, API Gateway и десяток сервисов за ним, но этого мало — так нормальное приложение не построишь. Ведь обычно сервисы как-то взаимосвязаны. Я вижу три способа связать сервисы:
Service Discovery
Service Discovery (RPC Style)
Вот простейший вариант Service Discovery:
Здесь у нас есть клиент, который обращается к различным сервисам. Однако, если в конфигурации клиента будет зашит адрес конкретного сервиса, мы будем связаны по рукам и ногам, ведь нам может захотеться развернуть все заново, или же может быть еще один экземпляр сервиса. И тут нам поможет Server-Side Service Discovery.
Server-Side Service Discovery
При Server-Side Service Discovery ваш клиент взаимодействует не напрямую с конкретным сервисом, а с выравнивателем нагрузки (load balancer):
Load balancer существует очень много: они есть у Amazon, у Azure и т. д. Load balancer на основании собственных правил решает, кому отдать вызов, если сервисов больше одного и неясно, где находится сервис.
Тут есть еще один дополнительный сервис, service registry, который также бывает разных типов — в зависимости от того, кто, как и где у него регистрируется. Можно сделать так, чтобы service registry регистрировал все типы сервисов. Load balancer берет все данные у service registry. Таким образом, задача load balancer — просто брать данные о местоположении сервисов из service registry и раскидывать запросы к ним. А задача service registry — хранить регистрационные данные сервисов, и он это делает по-разному: может опрашивать сервисы сам, брать данные из внешнего конфига и т. д. Пространство для маневров здесь видится очень широким.
Client-Side Service Discovery
Client-Side Service Discovery — другой, радикально отличающийся способ взаимодействия.
Здесь нет load balancer, и сервис обращается напрямую к service registry, откуда берет адрес сервиса. Чем это лучше? Тем, что на один запрос меньше — так все работает быстрее. Этот подход лучше предыдущего — но при условии, что у вас доверительная система, в которой клиент — внутренний и не будет использовать информацию, которую принимает от сервисов, во вред (например, для DDoS).
В целом в Service Discovery все достаточно просто — используются достаточно известные технологии. Однако тут возникает сложность — как реализовать более-менее серьезный бизнес-процесс? Все равно сервисы при таком подходе слишком сильно связаны, хотя проблему с точки зрения разворачивания и масштабирования мы решаем. Service instance A знает, что за данными нужно идти к service instance B, а если завтра у вас поменяется половина приложения и функцию service instance B будет выполнять другой сервис, вам придется многое переписывать.
Кроме того, когда возникает транзакционная ситуация и нужно согласовать действия нескольких сервисов, понадобится брокер (дополнительный сервис), который должен все согласовать.
Message Bus
Message Bus нужно уметь готовить и нужно действительно знать, что это такое. Message Bus используется для вполне определенных задач, например, не надо делать по Message Bus запросы request–reply или передавать большие объемы данных. Message Bus (и в принципе паттерн Publish/Subscribe) разрывает поставщиков и потребителей информации: поставщики не знают, кому нужна информация, а потребители не знают, откуда она берется — у одной информации теоретически могут быть разные поставщики и потребители.
И, как ни старайся, в такой системе у вас должен быть дополнительный сетевой вызов — брокеру, который собирает сообщения, и еще один вызов, когда эти сообщения нужно доставить. По моему опыту, передавать большие объемы данных (например, мегабайты) через Message Bus не стоит. Message Bus — шаблон командный; он нужен, чтобы один сервис мог сообщить другому, что у него что-то поменялось, чтобы другие сервисы могли на это среагировать.
В такой ситуации нам очень помогла бы гибридная архитектура. Тогда вы берете и кидаете по Message Bus сообщение, что какие-то данные поменялись. После этого подписчики реагируют на эти данные, идут в registry, забирают по идентификатору отправителя место, куда надо сходить за данными, и уже идут напрямую. Так вы очень многое экономите и разгрузите вашу шину.
Message Bus: «за» и «против»
Достоинства Message Bus:
Недостатки Message Bus:
Event Driven Architecture — архитектура, управляемая событиями
Когда наши сервисы взаимодействуют в стиле RPC, все понятно: у нас есть сервис, который связывает всю бизнес-логику, собирает данные из других сервисов и возвращает их, но что делать в случае архитектуры, управляемой событиями? Мы не знаем, куда идти, — у нас есть только сообщения.
В силу того, что сервисы работают только со своими хранилищами, у нас часто возникает ситуация, когда изменения в одном сервисе требуют изменений в другом. Например, у нас есть какой-то заказ (order), и нам нужно проверить лимиты, хранящиеся в другом сервисе (customer service):
У этой проблемы есть два решения.
Решение 1
Когда вы инициируете процесс создания заказа, посылаете в шину сообщение о создании сущности:
Сервис, который заинтересован в этих событиях, подписывается на них и получает идентификацию:
Затем он выполняет какое-то внутреннее действие и возвращает ответ, который потом прилетает в сервис заказов, в шину:
Все это называется конечной согласованностью (eventual consistency). Происходит это не атомарно, а за счет технологии гарантированной доставки Message Bus. При этом взаимодействие сервисов с шиной — транзакционное, и шина обеспечивает доставку всех сообщений. Поэтому мы можем быть уверены, что в конце концов до наших сервисов все долетит (если, конечно, не решим почистить сообщения брокера через консоль администрирования).
Если стандартная модель называется ACID, такая транзакционная модель называется BASE — Basically Available, Soft state, Eventual consistency, что расшифровывается примерно так: состояние, которое вы в итоге получаете, называется “soft state”, потому что вы не до конца уверены, что это состояние действительно актуально.
Решение 2
Есть и второй способ. Может оказаться, что для принятия решений в этой системе вам не хочется (или вы не можете в силу каких-то требований) каждый раз отправлять сообщение в другой сервис, ведь время отклика здесь будет большим. В такой ситуации сервис, который владеет некой информацией, — по кредитному лимиту, как в нашем примере, — в момент изменения кредитного лимита оповещает всех заинтересованных, что информация по данному клиенту поменялась. Сервис, которому нужно проверять кредитный лимит, его проверяет и сохраняет себе проекцию нужных данных, т. е. только те поля, которые ему нужны. Конечно, это будет дубликат данных, но позволит сервису никуда не обращаться, когда будет заказ.
Здесь снова срабатывает конечная согласованность: вы можете получить заказ, проверить его кредитный лимит и учесть его как выполненный. Но т. к. в этот момент кредитный лимит мог поменяться, может оказаться, что он не соответствует заказу, и вам придется выстраивать блоки компенсации — писать дополнительный код, который будет реагировать на такие внештатные ситуации. В этом вся сложность сервисных архитектур — нужно заранее понимать, что данные, которыми вы оперируете, могут оказаться устаревшими, что может привести к неправильным действиям.
Есть еще и третий, очень серьезный подход — Event Sourcing. Это большая тема, требующая отдельного обсуждения. В Event Sourcing вы не храните состояния объектов — вы их строите в реальном времени, а храните только изменения объектов (фактически, намерения пользователей что-то поменять). Допустим, происходит что-то в UI, например, пользователь хочет сделать заказ. Тогда вы сохраняете не изменение заказа, не новое состояние, а отдельно сохраняете внешние запросы: от пользователя, от других сервисов и вообще откуда угодно. Зачем это нужно? Это нужно для ситуации компенсации — тогда вы можете откатить состояние системы назад и действовать иначе.
Вообще, Message Bus — очень большая тема, в рамках которой очень многое можно рассказать о согласовании событий. Например, можно упомянуть Saga — маленький воркфлоу, который сейчас реализуют, по крайней мере, NServiceBus и MassTransit. Saga — по сути, машина состояний, которая реагирует на внешние изменения, благодаря которой вы знаете, что происходит с системой. При этом из любого состояния вы можете сделать блок компенсации. Т. ч. Saga — хороший инструмент реализации конечной согласованности.
Переход от монолита к микросервисам
Теперь хочется поговорить, как переходить от монолита к микросервисам, если вы решили, что это действительно то, что нужно. Итак, у вас есть большой монолит. Как теперь делить его на части?
Вы вынимаете нечто, ограниченное по бизнес-логике (то, что называется ограниченным контекстом в DDD). Конечно, для этого придется понять монолит. Например, хороший кандидат на выделение в отдельный сервис — часть монолита, которая требует частых изменений. Благодаря этому, вы получаете незамедлительную выгоду от выделения сервсиа — не придется тестировать монолит часто. Также хорошо выделять в отдельный сервис то, что доставляет наибольшее количество проблем и плохо работает.
Когда вы разделяете монолит на сервисы, обращайте внимание, как у вас структурированы команды. Ведь есть эмпирический закон Конвея (Conway’s law), который говорит, что структура вашего приложения повторяет структуру вашей организации. Если ваша организация построена на технологических иерархиях, построить микросервисную архитектуру будет очень трудно. Поэтому нужно выделить feature-команды, которые будут иметь все необходимые навыки, чтобы написать нужную логику от начала до конца.
На самом деле, редко бывает, что у нас чистая микросервисная архитектура. Чаще всего мы имеем нечто среднее между монолитом и микросервисами. Обычно есть какой-то большой исторически сложившийся код, и мы понемногу стараемся распутывать его и отделять от него части.
Если же мы делаем проект с нуля, нужно выбрать — монолит или микросервисы?
Монолит лучше выбирать в следующих случаях:
Микросервисы лучше выбрать, если:
Микросервисы: «за» и «против»
Немного о мониторинге и тестировании мироксервисной системы
Есть инструменты, которые позволяют развертывать такую систему и следить за ней, например, ZooKeeper, который решает проблему конфигурирования за нас. Также есть такие инструменты типа Logstash, Kibana, Elastic, Serilog, Amazon Cloud Watch. Все они следят за вашими сервисами.
Как тестировать микросервисную систему? Я вижу это следующим образом. У вас есть сервис, который решает какую-то бизнес-задачу. Ваша цель — протестировать его бизнес-контракты. Большинство тестов, которые вы делаете — модульные, которые для кода, написанного в рамках этого сервиса. Это — низ пирамиды тестирования. Следующий уровень — интеграционное тестирование, которое проверяет, как этот сервис отвечает на стандартные запросы. Тут у вас огромное пространство для интеграционного тестирования кода, написанного изолированно. Следующий этап — использование разных инструментов, чтобы гарантировать, что контракт не поменялся. В нашем проекте мы использовали Swagger, который позволяет зафиксировать контракт.
Манифест Джеффа Безоса (Amazon CEO)
Напоследок приведу хороший пример крайне успешного применения микросервисной архитектуры — Amazon.
Все началось с того, что в 2000 г. Джефф Безос, глава Amazon, написал для всех отделов своей компании следующий манифест:
Вкратце, суть манифеста такова: «Вся функциональность выставляется наружу только посредством интерфейсов сервисов. Команды (на самом деле — сервисы и их команды) взаимодействуют только посредством сетевых интерфейсов: никакого взаимодействия с базами данных, никакой общей памяти и т. д. — вне зависимости от технологий и без исключений.
Таким образом, Джефф обязал все сервисы быть готовыми, что их выставят в публичный доступ. Это привело к уровню, когда можно предлагать услугу как сервис. Это стало залогом успеха компании. Мы знаем, к чему в итоге привел этот манифест — к целой индустрии Amazon и частичное выросшей из этого индустрии AWS.