Когда задумывается большой продукт или маленький софт начинает вырастать в левиафана, какой путь развития выбрать? Стоит ли все переписывать с нуля или продолжать «исторически сложившиеся» традиции? Да и вообще, стоит ли пересматривать саму концепцию архитектуры?
Здравствуйте, хабровчане! Меня зовут Константин, и я являюсь ведущим разработчиком одной довольно крупной системы, которая начиналась когда-то как эксперимент. Небольшой сайт на PHP, созданный буквально «на коленке» и, конечно же, монолитом. Шло время, сайт рос и столкнулся с рядом проблем, решение которых поставило вопрос «а дальше-то как?».
Монолит или микросервисы? Что же выбрать? Базовое отличие подходов, на мой взгляд, состоит в том, что монолит подразумевает централизованный цикл обработки запроса пользователя, а микросервисы — децентрализованный. Общие достоинства и недостатки обоих подходов широко известны и не раз перечислены (например, тут, тут или тут). Однако есть и не столь явные и очевидные факторы, влияющие на выбор архитектуры.
- Загрузка штатных сотрудников. При бурном развитии продукта накапливается пул задач, решить которые в приемлемые сроки нельзя — все заняты. И если такой «завал» единичен, расширять штатный состав разработчиков не имеет смысла. Очевидное решение — внешние подрядчики (компании или фрилансеры). Но как отдать им в работу фрагмент продукта, не рискуя превратить его в OpenSource, грубо говоря? В случае использования микросервисов такую задачу решить гораздо легче.
- Сложность погружения. Чем больше продукт, тем больше времени требуется новому сотруднику, чтобы освоиться в нем. Особенно, если в коде много различных умолчаний (да, хорошее документирование существенно облегчает процесс, но если его нет?) и кастомизаций для отдельных частных случаев. С этой точки зрения разобраться с небольшой независимой частью гораздо легче, чем с целым продуктом сразу.
- Ресурсы при пиковых нагрузках. В моей практике был случай, когда нагрузка на серверах иногда вырастала до неприемлемых значений (а если конкретно — исчерпывалась ОЗУ, задействовался своп и сервер превращался на время в овощ), при свободной работе все остальное время (загрузка составляла не более 30% по показателю ОЗУ). И такие пики случались нерегулярно и с большими паузами. В случае монолита (считаем, что оптимизировать код уже некуда) спасает только подключение новых серверов с копией всей системы для распределения нагрузки. А в случае микросервисов можно организовать очередь обработки (конечно, это применимо только для некритичных по времени операций, которые быстро и не ожидаются, вроде выгрузки таблицы Excel).
- Выполнение задач в «фоновом» режиме. В какую сторону этот фактор «качает лодку», зависит от его величины и сложности. Как правило, достаточно монолита и таймера (cron). Но если задачи начинают скапливаться в большие группы, появляются и проблемы. Надо балансировать уже и cron-задачи. Микросервисы существенно облегчают такую ситуацию.
- Сложность сопровождения. В случае микросервисов повышаются требования к сопровождению железа — где, что и как должно быть настроено. Монолит — грубо говоря, одни настройки на все ноды, микросервисы — настройки зависят от требований конкретных микросервисов, которые на ноде запущены.
- Требования к квалификации. Чем больше зоопарк технологий (а он, как правило, растет, если система микросервисная), тем выше требования к навыкам и знаниям штатных сотрудников. Ведь случись доработки или проблемы, они должны справиться. Или же нужно больше специалистов — для покрытия всего стека.
А надо ли вообще выбирать? Стоит ли игра свеч? Я думаю, безусловно, стоит. Но подходить к вопросу надо с холодной головой. Иначе одни проблемы просто заменятся на другие.
В моем случае переломный момент наступил, когда сайт перестал «вмещаться» в ресурсы одного сервера. Тогда было принято решение переписать систему в соответствии с наилучшим по моему мнению вариантом — гибридным. Есть общие рекомендации по развитию продукта в «гибридном случае». Но я хотел бы его дополнить несколькими рекомендациями.
Проектируя стартовый монолит, возможности дальнейшего подключения внешних сервисов нужно заложить сразу. Сразу строить межкомпонентные связи так, как будто эти компоненты уже (или почти уже) «внешние сервисы». Особенно, если они ресурсоемкие.
Сразу же продумать политику работы с кешами и хранилищами данных (БД и файлы). Например, обеспечить общий доступ к сессии пользователя.
И самое главное — не кидаться в крайности. Для себя я вывел следующую формулу хорошего проекта: «эффективная система — это сбалансированная система». В частности, это выражается в том, что в микросервисы я выношу только большие, логически обособленные фрагменты ядра. И то, только при выполнении хотя бы одного из условий:
- требуется ограничение и прогнозирование нагрузки, задержка по времени из-за выполнения задач поочередно не является критичной
- обособление даст значительный прирост производительности.
Как результат такого подхода получилась система, 95% функционала, которой располагается в ядре, и 5% — в микросервисах. Но при этом на ядро приходится только 60% всей нагрузки. И при этом можно гарантировать, что нагрузка на отдельные сервера не превысит критических значений.
Так что микросервисы — это (с определенного размера продукта) хорошо, если использовать их только в случае реальной необходимости, а не в силу моды или «потому что круто». Есть большие продукты, которые успешно живут монолитом. Но иногда монолитом задачу не решить…
Спасибо!