Монолит vs Микросервисы
Ниже рассматриваем плюсы и минусы подходов.
Монолит
Плюсы
- Все команды работают с одними и теми же библиотеками, очень практично.
- Увеличивается скорость разработки, т.к. исправили ошибку в одном месте - исправили везде (но, если исправили неправильно, к сожалению, исправили неправильно везде).
- Чем меньше API-сервисов, тем проще разрабатывать, дебажить и нет оверхеда.
- На начальном этапе не заставляет думать о сервисе внутри кодовой базы, как о сервисе внешнем, благодаря чему экономится время разработки (SOA никто не отменял).
- Следить на бизнес-обязанностями сервисов в одном репозитории гораздо легче, чем в нескольких, а если не уследили, то отрефакторить гораздо проще.
Минусы
- Большая кодовая база замедляет IDE
- Большая кодовая база требует большого ручного тестирования или долгого автоматизированного тестирования
- Большая кодовая база с малым покрытием тестов не даёт быстро обновлять или заменять библиотеки
- Малое покрытие тестами означает, что после создания релиза следует длительный период тестирования и стабилизации кодовой базы
- Монолитная архитектура обычно несёт зависимость от устаревших технологий
Микросервисы
Решение о том, когда следует переходить на микросервисную архитектуру, является нетривиальным.
Плюсы
- Масштабируемость. Действительно микросервисную архитектуру очень легко масштабировать: проще запустить несколько процессов микросервиса и поставить перед ними балансировщик для обработки возросшего потока данных.
- Отказоустойчивость. Надёжность достигается за счёт того же запуска дополнительных процессов. В отличие от того же монолита или stateful-сервисов микросервис требует значительно меньшего количества ресурсов для запуски нового экземпляра.
- Обновляемость. Малая связанность сервисов и их относительная простота делает процесс тестирования и обновления технологий очень приятным занятием. Для того, чтобы опробовать новую платформу для разработки не нужно переписывать всё приложение целиком. Точнее «всё приложение»-микросервис стало значительно меньших размеров и переписать его стало возможно в обозримые сроки. Таким образом, приложение с микросервисной архитектурой довольно легко обновлять.
- Упрощение автоматического развёртывания (меньше стек, и контейнеризация, и прочие разности, которыми обладает простое и понятное солидное приложение).
- Возможность развивать продукт независимыми сегментами. Т.е. сегодня вы захотели впилить новый сегмент, который потенциально может дать вам прибыль. Вы берете отдельную команду, ставите задачу и она уходит в запил со своим стеком технологий (в разумных пределах). Он максимально адаптирован для нужд субпродукта. На выходе вы получаете результат, который интегрируете в уже существующую АС. Если не выстрелило — сервис выпиливаться без лишних страдений.
- Адекватное масштабирование микросервисов. Пример — микросервис авторизации, который эмитирует JWT, допустим. Вероятность атаки на него крайне высока. В момент DDoS мы можем масштабировать только этот микросервис, что позволит справиться с атакой. При этом не нужно будет разворачивать кучу нод полноценной АС, как в случае с монолитом.
- Независимая релизность. Зачастую, чтобы выкатить релиз монолита нужно провести грандиозный регресс. Релиз раз от раза откладывается из-за обнаруженных багов в различных областях монолита. Это неадекватно задерживает выпуск нужных фич из-за багов в каких-то мелочах. Не будем забывать, что разработка не прекращается. И разрыв между версиями с каждым часом прирастает. Микросервисы позволяют выпуск дробить. И выкатывать сегменты системы независимо.
- Гибкое управление контурами безопасности. Монолит имеет доступ ко всем данным. Это грандиозная дыра. Контролировать контуры безопасности с микросервисной архитектурой гораздо проще и надежнее. Тот же сервис авторизации может находиться в отдельной подсети к которой доступ будет иметь ограниченный круг лиц. Я имею ввиду прямой и косвенный (через кодовую базу монолита).
- Микросервисный код — относительно короткоживущий. Месяцы, но не годы. А значит можно не заморачиваться на многие вещи. Т.е. раз в 9-12 месяцев каждый сервис выкинули и переписали под новый стек, требования, новые регуляции, изменившийся профиль нагрузки и т.д. Не надо сложной архитектуры, не надо переживать несколько эволюций рефакторинга, не надо оптимизировать, не надо долго и нудно тестировать. CI не свалился — выкатываем на blue-green через K8s/Helm/CloudFormation, смотрим на процент 500 в New Relic, если что-то закривело — откатываем, и по-новой. Как едко выразился мой коллега — "говнокод-ориентированная архитектура".
А теперь, внимание, жахнем теорией заговора. С точки зрения цинично-прагматичного VP of development, это дает две очень полезные вещи:
6. Разрабатывать можно дешевыми кодерами, которые умеют в CRUD + пакетную обработку запросов. Качество кода не особо важно, ибо живет все равно недолго. Оттуда и низкая стоимость поддержки: квалификация и зарплата ниже у "поддерживающих".
7. дешевые кодеры и их линейный менеджмент отлично заменяются без ущерба для процесса. Потому что научить копипастить можно и за неделю. 20% attrition rate в год? Да фиолетово. Надо увеличить команду на треть и потом сократить на половину? Не вопрос. Можем еще недорогих контрактников привезти. И матричную занятость без трекинга времени. В таком раскладе можно выжимать людей до сухой кости в промышленных масштабах. Отсюда и увеличение скорости разработки.
Минусы
- Требуется разбиение бизнес-логики на независимые сервисы и организация взаимодействия между ними. При проектировании солидной сервисной архитектуры, неважно какого размера, имеет смысл руководствоваться правилами, изложенными CEO Amazon Джеффом Безосом: основная идея заключается в том, чтобы сервисы взаимодействовали исключительно через предоставляемый ими API и не использовать никаких других способов будь то общая база данных или другое общее хранилище. В противном случае сервис запутается в тентаклях логики надстроенной над общими данными. Собственно, подобная ситуация противоречит использованию простейших каналов данных, поскольку БД таковым не является. Чтобы исключить подобный плачевный сценарий развития событий нужно, в первую очередь, хорошо представлять себе потоки данных: какие структуры необходимы для обеспечения работы и какими способами их можно получить, — для чего в свою очередь нужно хорошо знать предметную область приложения.
- Может получится распределенный монолит. Сложно подобрать подходящий набор сервисов (если вы неправильно разделили систему, у вас получится распределенный монолит — набор связанных между собой сервисов, которые необходимо развертывать вместе). Распределенному монолиту присущи недостатки как монолитной, так и микросервисной архитектуры.
- Усложняется разработка:
- требует поддержание согласованности данных между сервисами - реализации распределенных транзакции или повествований (саги). Каждый сервис имеет собственную базу данных, что затрудняет реализацию комбинированных транзакций и запросов + приложение, основанное на микросервисах, должно использовать так называемые повествования (или саги), чтобы сохранять согласованность данных между сервисами).
- требует получение согласованного представления данных (когда UI состоит из нескольких микросервисов)
- микросервис должен работать, даже если необходимый ему сервис не работает (ваш код должен уметь справляться с частичными сбоями и быть готовым к недоступности или высокой латентности удаленного сервиса). - Усложняется тестирование. По хорошему нужно будет внедрять:
- тестирование контрактов с расчетом на потребителя — проверка того, что сервис отвечает ожиданиям клиентов.
- тестирование контрактов на стороне потребителя — проверка того, что клиент может взаимодействовать с сервисом.
- тестирование компонентов сервиса — тестирование сервиса в изоляции. - Требуется тщательная координация развертывания крупного функционала (охватывающего несколько сервисов).
- Латентность сети
- Оверхед: сетевой, сериализация/десериализация
- Ухудшение доступности из-за синхронного взаимодействия
- Требуется инструмент обнаружения работающих сервисов и их инстансов. Вам придется усложнять инфраструктуру, которая будет следить за работоспособностью еще одного сервиса и реагировать на проблемы недоступности и неправильной работы сервиса.
- Требуется постоянный контроль независимости сервисов
- Требуется еще больших контроль инструментария - стек растет и вам придется его контролировать, а не то он превратится в "монстра"
- https://m.habr.com/ru/company/flant/blog/540406/
- https://m.habr.com/ru/company/flant/blog/424531/
- https://habr.com/ru/company/flant/blog/347518/
- Сложнее понять, почему произошла ошибка. К примеру:
- Первый сервис запросил данные у второго, а тот у третьего и нам нужно понять, что привело к ошибке на третьем сервисе.
- Первый запросил данные у второго, подождал и отвалился
- Один сервис запросил данные у второго, а второй снова у первого.
Что с этим делать?
- запросу инициатору устанавливаем уникальных X-Request-ID $request_id
- отслеживаем таймаут с помощью $time_local запроса инициатора
- отслеживаем рекурсию запросов с помощью X-Request-Names
С помощью таких манипуляций с запросами, мы можем посмотреть на всю цепочку запроса или завершить запрос по времени, чтобы не жрать лишние ресурсы. Подробнее »
10.09.2021 21:22
Комментарии
Комментариев пока нет.