15 мая 2024
Выбор между монолитом и микросервисами зависит от задач системы и команд. Нужно оценить нагрузку, границы ответственности, требования к независимым изменениям и стоимость эксплуатации. Ниже разбираю компромиссы, которые появляются при разделении приложения на сервисы.
Скрытые издержки микросервисов
Стоимость сетевых вызовов
Каждая граница сервиса добавляет:
- Сериализацию и десериализацию данных
- Сетевые переходы (network hops)
- Retry-логику и таймауты
- Задержку на каждом переходе
Микросервисы: Монолит:
[Client] -> [API Gateway] [Client] -> [Monolith Cluster]
| |
[Service A] (Internal function calls)
| |
[Service B] [DB]
|
[Service C]
|
[DB]
Каждый сетевой переход между сервисами добавляет задержку и операционную нагрузку. Монолит маршрутизирует вызовы внутри памяти - быстрее, дешевле, проще.
Сложность эксплуатации
Каждый новый сервис требует:
- Мониторинга и алертинга
- CI/CD пайплайна
- Incident playbook
- On-call ротации
- Документации API
- Версионирования контрактов
Сложности отладки
То, что раньше было одним вызовом функции, теперь - distributed trace через десятки сервисов. Найти причину проблемы в цепочке из 20 сервисов значительно сложнее, чем в стектрейсе монолита:
- Correlation IDs помогают связать логи разных сервисов с одним запросом. Идентификатор нужно передавать через границы сервисов и записывать в логи; вместе с трассировкой это упрощает поиск причины проблемы.
- Distributed tracing: инструменты вроде Jaeger или OpenTelemetry помогают визуализировать путь запроса, но требуют инструментации каждого сервиса.
- Частичные сбои: в монолите запрос либо работает, либо нет. В микросервисах один из 10 сервисов в цепочке может деградировать, возвращая данные с задержкой или неполные - и это сложнее диагностировать, чем полный отказ.
- Воспроизводимость: проблема может проявляться только при определенной комбинации версий сервисов и сетевых условий, что делает воспроизведение на локальной машине затруднительным.
Модульный монолит как альтернатива
Между монолитом и микросервисами существует промежуточный вариант - модульный монолит. Код организован в изолированные модули с четкими границами и контрактами, но деплоится как единое приложение. Это дает:
- Четкие границы ответственности (как у микросервисов)
- Простоту деплоя и отладки (как у монолита)
- Возможность выделить модуль в отдельный сервис позже, когда появится реальная потребность
Shopify - один из известных примеров успешного модульного монолита на Ruby on Rails, обслуживающего миллионы магазинов.
Учебный пример: критичный сценарий
Предположим, платежный сценарий проходит через несколько сервисов. Каждый сетевой вызов добавляет задержки и возможные сбои. Нужно выяснить, оправданы ли эти границы независимым деплоем, владением данными или изоляцией нагрузки.
Объединение частей может упростить операции, но потребует согласовать ответственность и масштабирование. Это задача для измерений и анализа зависимостей, а не доказательство превосходства монолита. Пример не описывает внутреннюю архитектуру Netflix.
Почему индустрия это упустила
1. Популярность подхода
Микросервисы стали восприниматься как единственный «правильный» подход. Вопрос «а подходит ли это нам?» стал задаваться все реже.
2. Устройство команд и системы
Микросервисы решают проблему масштабирования команд, не всегда - масштабирования систем. Это разные проблемы.
3. Выбор примеров для конференций
На конференциях рассказывали об успешном переходе на микросервисы. Опыт сохранения монолитной архитектуры обсуждался реже. Никто не выступает с докладом "Мы не перешли на микросервисы и у нас все хорошо".
Как выбрать подход
Вопрос 1: Сколько у вас команд?
Одна команда?
└── Предпочтите монолит
• Быстрее итерации
• Проще операции
• Меньше координации
Много команд?
└── Микросервисы могут уменьшить friction
• Независимые деплои
• Автономия команд
• Чёткие границы владения
Вопрос 2: Что является bottleneck - трафик или люди?
Bottleneck - трафик?
└── Монолит часто выигрывает
• Меньше сетевых переходов
• Проще масштабирование
• Ниже латентность
Bottleneck - люди?
└── Микросервисы могут помочь
• Независимая работа команд
• Меньше merge conflicts
• Быстрее onboarding в отдельный сервис
Матрица решения
| Мало команд | Много команд | |
|---|---|---|
| Bottleneck - трафик | Монолит | Модульный монолит или крупные сервисы |
| Bottleneck - люди | Монолит (пока) | Микросервисы |
Резюме
- Архитектуру выбирают по нагрузке, зависимостям и требованиям к изменениям
- Сетевые вызовы между сервисами имеют стоимость (сериализация, hops, retries)
- Микросервисы решают проблему масштабирования команд, не систем
- Трафик и число команд не задают архитектуру однозначно: нужны измерения и анализ границ
- "Современно" ≠ "правильно для вашего случая"
Источники
- Netflix Tech Blog.
- Shopify Engineering.
- Melvin Conway. «How Do Committees Invent?» (1968) - закон Конвея.
- Melvin Conway: How Do Committees Invent?