Service-based architecture: семь раз отмерь, один раз попили монолит
Рассказываем, что делать, когда проект уже вырос из пелёнок монолита, а примерять микросервисную архитектуру ещё рано.
Кадр: сериал «Остановись и гори»
Георгий Андрончик
Software Architect в Almus., Data Engineer в mesto.co и преподаватель курса «Профессия Архитектор ПО» в Skillbox.
Вокруг микросервисной архитектуры (microservice architecture, MSA) много хайпа. Все о ней говорят, хотят внедрить, и некоторые даже внедряют. Вот только на практике не все остаются довольны переходом на MSA, а некоторые и вовсе жалеют.
Микросервисная архитектура — это не универсальное средство от всех болячек. У неё есть ряд плюсов и минусов, она подходит не для каждого проекта и не на всех этапах его жизни. Если бизнес растёт и вы столкнулись с ограничениями монолита, это ещё не повод бросаться с головой в микросервисный омут.
В этой статье описывается архитектурный подход, который подойдёт для большого количества бизнес-приложений. Однако, если вы планируете перейти от монолита к распределённой системе, рекомендую подробно ознакомиться со всеми стилями распределённых архитектур и сделать осознанный выбор.
Какие ограничения есть у монолита
Рассмотрим типичную ситуацию. В небольшой компании над проектом работает команда из 5–8 человек. Когда бизнес растёт, система усложняется: появляется больше функций и приходят новые клиенты — нагрузка увеличивается. Чтобы справиться с ростом и проверять больше гипотез, компания нанимает дополнительных разработчиков.
И тут бизнес сталкивается со следующими проблемами:
- Команде становится тесно. Всё больше людей трудится над проектом. Работать с общей кодовой базой сложнее, при мержах возникает всё больше конфликтов.
- Усложняются релизы. Маленькие релизы становятся дорогими, ведь даже при незначительном апдейте приходится тестировать весь монолит. Скоуп растёт, релизы затягиваются, а бизнес теряет время, за которое мог бы получить данные по гипотезам, сделать выводы и двигаться дальше. А если релиз откатят (например, из-за нестабильности какой-нибудь фичи), то откатится весь скоуп.
- Плохое масштабирование. Одни юзкейсы всегда популярнее других, и трафик между соответствующими частями приложения распределяется неравномерно. Но проекты с монолитной архитектурой масштабируются целиком, а значит, бизнес зря тратит деньги.
- Отсутствует эластичность. Требуется много времени для старта нового экземпляра монолитного приложения (в силу его тяжеловесности). Поэтому поднимать новые инстансы на лету не получится.
Как раз на этом этапе многие компании переходят на микросервисную архитектуру, потому что она решает описанные проблемы. А чтобы понять, каким образом, — рассмотрим MSA поближе.
Что такое микросервисная архитектура
Приложения с микросервисной архитектурой (MSA) состоят из n небольших подсистем, или микросервисов. Эти подсистемы и их экземпляры могут выполняться как на одной физической машине, так и на нескольких. Во втором случае отказоустойчивость сервиса выше.
Часто в микросервисной архитектуре можно встретить API layer — это общая точка входа в систему. Она балансирует нагрузку и ограничивает доступ к сервисам, но не является обязательным компонентом.
В основе дизайна MSA лежат принципы domain-driven design, в частности ограниченные контексты и домены. Таким образом, каждый микросервис реализует домен или поддомен, имеет собственную базу данных (или не имеет, но у сервисов нет общей БД) и не имеет общей кодовой базы с другими микросервисами. Хотя есть и исключения, например шаблон sidecar.
Микросервисы слабо связаны друг с другом. Чаще всего они общаются синхронно через HTTP или асинхронно с помощью очередей сообщений (RabbitMQ, Redis, Amazon SQS) или логстрима (Kafka, Amazon Kinesis). У такого подхода много преимуществ.
Масштабируемость. Благодаря высокой гранулярности и отсутствию централизованных баз данных масштабируемость микросервисной архитектуры достигает наивысшего показателя. Дополнительные экземпляры микросервисов поднимаются легко, и при этом база нагружается запросами лишь от одного микросервиса, а не от всей системы. А ещё мы экономим деньги, потому что масштабируем только те сервисы, на которые идёт трафик.
Эластичность. Этот показатель тоже достигает экстремально высокого уровня. Новые экземпляры запускаются в течение десятых, а то и сотых долей секунды. При резком увеличении нагрузки на определённые микросервисы система может автоматически поднять дополнительные реплики.
Гибкость и изменяемость. Как бы хорошо ни был спроектирован монолит, когда система разрастается, его всё тяжелее поддерживать. Поэтому рано или поздно код придётся рефакторить. Микросервисы в этом плане очень удобны. Они похожи на кубики лего, из которых можно пересобрать новую фигуру, а если нужно, дополнить проект новыми «кубиками».
Скорость и изолированность доставки. Микросервисы просто разрабатывать и деплоить. Отдельный микросервис устроен довольно просто, а значит, его гораздо легче дорабатывать, тестировать и развёртывать, чем монолит с комплексной логикой.
Изолированность разработки. Много маленьких команд справятся с общей задачей быстрее, чем одна большая. Согласитесь, менеджерить работу в команде из трёх человек куда проще, чем в команде из 30.
Надёжность. Тут тоже всё на высоком уровне, хотя не всё так однозначно. С одной стороны, изолированность микросервисов повышает отказоустойчивость всей системы, а с другой — чем больше интеграций, тем выше риск отказа. Тем не менее на практике этот показатель у проектов на MSA — выше среднего.
Кроме того, каждый микросервис может строиться на любых технологиях — полная свобода! И всё-таки лучше держать техническую фантазию под контролем, чтобы не развести технозоопарк.
Все эти достоинства звучат довольно привлекательно. Именно поэтому (ну, и благодаря хайпу) многие компании с энтузиазмом переходят на микросервисы. Однако «нет худа без добра и добра без худа». Архитектура — это всегда компромиссы. И у микросервисной архитектуры, при всех очевидных плюсах, есть ряд существенных минусов.
Что не так с микросервисами
Основные минусы MSA — низкая производительность и сложности, которые подстерегают вас на всех уровнях.
Проектирование продукта. Сложно правильно выделить границы сервисов. Цена ошибки довольно высока, потому что иногда, чтобы её исправить, может потребоваться несколько команд, куча денег и времени. И даже если вы всё сделаете правильно, то всё равно потратите много времени на проектирование.
Поддержка контрактов. Микросервисы общаются между собой и используют формат данных, который вы для этого определили. Но будьте уверены: рано или поздно вам придётся обновлять контракты, и чем больше у вас микросервисов, тем сложнее это сделать.
Распределённые транзакции. Вообще, распределённых транзакций нужно избегать. Но если вы всё-таки допустили ошибку в проектировании и обратной дороги нет, они могут потребоваться. Также иногда невозможно достичь компромисса в НФТ и продукт приходится разделять на два сервиса, но транзакционные границы приходится сохранять.
Поиск ошибки. Найти ошибку в MSA — часто нетривиальная задача, потому что в процессе могут участвовать несколько сервисов. Также усложняется процесс тестирования межсервисных взаимодействий, хоть тестирование отдельных сервисов — довольно простой и приятный процесс.
Дорогой мониторинг. Чем больше компонентов и интеграций в вашей системе, тем важнее становится мониторинг. В то же время мониторить такие системы сложнее и дороже.
Необходимость DevOps. С MSA точно не удастся обойтись без DevOps. Такая архитектура в принципе неотделима от контейнеризации, оркестрации и других DevOps-практик :)
Низкая производительность. Тут всё просто: чем больше сервисов участвует в цепочке вызова, тем больше задержка. Согласитесь, вызов функции намного быстрее сетевого запроса.
Высокая цена. Несложно догадаться, что реализовать и поддерживать такую махину очень дорого.
Как видите, у микросервисов есть ряд накладных расходов, которые окупаются только на больших масштабах. А на ранних этапах это просто нецелесообразно — всё равно что организовать работу компании из 20 человек наподобие корпорации. Менеджеры введут кучу регламентов, создадут «отделы» из одного человека, а в результате компания погрязнет в бесполезной бюрократии. Но что же подойдёт для организации из 30–100 человек?
Решение есть: service-based architecture!
В русскоязычном интернете редко упоминают сервисную архитектуру (service-based architecture, SBA), и, вероятно, для многих это то же самое, что MSA. Отчасти они правы — это упрощённая, более грубая версия микросервисов.
Основные отличия service-based от microservice — меньшая гранулярность сервисов и отсутствие коммуникации между ними. Количество сервисов, как правило, варьируется от 4 до 12, а их границы совпадают с границами доменов. Микросервисы же исчисляются десятками или сотнями и разделены гораздо детальнее.
Рассмотрим систему заказа билетов в кинотеатре. У нас есть три операции:
- инициализация заказа;
- оплата билетов;
- отмена заказа.
В MSA за каждую из операций мог бы отвечать отдельный микросервис, а в service-based вся логика реализуется в рамках одного сервиса. Подчеркну ещё раз: в service-based нет межсервисной коммуникации. Это избавляет разработчиков от множества проблем, потому что интеграции — одно из самых узких мест системы.
На рисунке выше видно, что база данных в базовой топологии — общая. Но для некоторых сервисов или групп иногда выделяют отдельные базы.
Если сервисы вашей системы используют общую базу данных, работающую по принципу ACID, то советую разделять их схемы. В противном случае вы не сможете развивать их изолированно. Если, дорабатывая один сервис, вы будете обновлять схему БД, это может «зааффектить» другие сервисы. На те же грабли можно наступить при разделении хранилищ.
Сервисная архитектура позволяет реализовать ACID-транзакции в рамках домена — чего нельзя сказать о микросервисах. Балансировать нагрузку можно с помощью reverse proxy, API gateway или даже на стороне клиента. А если сервисы существуют в единственном экземпляре, то балансировка и вовсе не нужна.
К SBA очень легко перейти от монолита. Особенно если при проектировании монолита вы логически выделили модули, которые станут сервисами.
А что с дизайном самих сервисов? Тут два пути:
- если уверены, что дальше service-based-система не эволюционирует, можете выбрать слоистый стиль;
- если же переход на MSA в будущем неизбежен, выделите внутри сервисов поддомены, которые впоследствии вырастут в микросервисы.
А теперь ещё раз пробежимся по проблемам MSA и посмотрим, как сервисная архитектура с ними справляется. Результаты сравнения приведены в таблице.
Проблема | Microservice architecture | Service-based architecture |
---|---|---|
Сложность проектирования | Сложно правильно выделить границы сервисов, а чтобы исправить ошибки, может понадобиться несколько команд, много денег и времени. | Выделить домены несложно, а цена ошибки невысока. Рефакторить код тоже недорого, так как над одним сервисом работает одна команда. |
Поддержка контрактов | Сложно обновлять контракты межсетевого взаимодействия. | Сервисы не общаются между собой, а потому и контрактов не нужно. |
Распределённые транзакции | Иногда приходится к ним прибегать. | Распределённых транзакций нет. |
Поиск ошибок | Поиск ошибок в системе из нескольких десятков микросервисов — часто нетривиальная задача. | Искать ошибки так же просто, как в монолите. |
Сложность мониторинга | Микросервисы исчисляются десятками, а то и сотнями — чем их больше, тем труднее мониторинг. | Мониторить сервисы чуть сложнее, чем монолит, зато не будет челленджей, как в MSA. |
Необходимость DevOps | MSA невозможно реализовать без контейнеризации, оркестрации и других DevOps-практик. | DevOps не нужен. |
Низкая производительность | Чем больше сетевых взаимодействий между сервисами, тем медленнее работает вся система. | Нет сетевых взаимодействий, всё работает быстро. |
Высокая цена | Дорого реализовывать и поддерживать. | Такая же, как у модульного монолита. |
Кажется, очень даже неплохо. Минусы микросервисов для service-based практически ничего не стоят.
Давайте сравним с плюсами:
Достоинство | Microservice architecture | Service-based architecture |
---|---|---|
Масштабируемость | Микросервисы логически разделены и независимы, в приложении нет централизованных баз данных. | Если у MSA этот показатель на 5, то у service based — на 3+ из-за более грубой гранулярности. |
Эластичность | Новые экземпляры микросервисов запускаются в течение десятых, а то и сотых долей секунды. | Тут всё слабо. Новые экземпляры поднимаются медленно. |
Гибкость и изменяемость | Микросервисы похожи на кубики лего, из которых можно пересобрать новую фигуру или дополнить проект новыми «кубиками». | Сервисы довольно массивные, пересобрать из них новое приложение не выйдет. Но у нас как минимум есть разделение на домены, поэтому оценка — средняя. |
Скорость и изолированность доставки и разработки | Над микросервисом работает одна команда. | Над сервисом тоже работает одна команда. |
Надёжность | Изолированность микросервисов повышает отказоустойчивость всей системы. | Довольно высокая надёжность. Интеграций нет, но всё же, если откажет подсистема, будет больнее, чем если откажет микросервис. |
Как видим, у service-based слабая эластичность, зато такой архитектурный подход лишён недостатков MSA и имеет неплохую масштабируемость и изменяемость.
Резюме:
- Microservice architecture — очень гибкая, масштабируемая и эластичная архитектура, но при этом дорогая, сложная и не очень производительная.
- Сервисная архитектура — это «младшая сестра» MSA. Она может выступать промежуточным этапом между монолитом и микросервисами. Монолит довольно просто попилить на service-based.
- Service-based лишена слабостей микросервисов, но проигрывает в эластичности. А ещё она менее масштабируемая и гибкая. С другой стороны, такие свойства нужны не каждому проекту и не на каждом этапе развития.