Микросервисы - не способ масштабироваться
В большом количестве статей, источников микросервисы, помимо всего прочего, представляются как способ построить масштабируемое решение. Рассмотрим на примерах, почему это не так. А так же попытаемся внести свою лепту в извечный вопрос:
Что лучше: монолит или микросервис?
Рассмотрим пример.
Допустим, у нас есть микросервис A
, выполняющий авторизационные
запросы “имеет ли право пользователь выполнить операцию?”.
Поскольку изолированно такой микросервис существовать не может, то в
паре с ним существует другой микросервис B
, который сохраняет в
хранилище список соответствий пользователи-права.
Примерная схема микросервисов показана на рисунке:
В результате изменений в пользовательских данных (регистрация новых
пользователей, ограничения на существующих и т.п.) микросервис B
“следит” за актуальностью данных в хранилище, которое использует
микросервис A
для выполнения авторизационных запросов.
Простая схема. Просто устроена, надёжно работает.
Предположим, что количество пользователей, подключающихся к нашей системе, растёт. Каковы “узкие” места в этой архитектуре?
- нагрузка на CPU в микросервисе
A
- нагрузка io-read/select в БД
- нагрузка на CPU в микросервисе
B
- нагрузка io-write в БД
Вопросы с CPU в микросервисах решаются просто добавлением экземпляров в игру. Здесь масштабирование простое, и не стоит его обсуждать:
Давайте посмотрим, что будет с ростом нагрузки на
микросервис A
и B
?
В определённый момент времени БД перестанет справляться с потоком
запросов на чтение от микросервиса A
. При наступлении этих проблем
обычно вводят в игру RO-реплики БД:
Поскольку микросервис A
не модифицирует записи в БД, то добавлением
реплик к БД можно решить практически все вопросы масштабирования этого
микросервиса.
Но вот вопрос: а что делать, когда микросервис B
приведёт master-БД к
лимиту, определённому максимумом нагрузки на запись (io-write)?
Вариантов решения этих проблем довольно немного. Все они сводятся к
тому, чтоб распределить запись в БД по нескольким хостам. Используем
схему шардинга или иной масштабируемый multi-master:
Вместо одной БД у нас имеется X
шардов БД, позволяющих масштабировать
нагрузку на запись, и к каждому шарду - реплики (всего - Y
),
позволяющие масштабировать нагрузку на чтение.
Итого:
По мере роста нагрузки в нашем примере сами микросервисы претерпели немного изменений. Большинство изменений при масштабировании было в хранилище данных.
Если рассмотреть более обобщённо, то при масштабировании микросервисная архитектура сталкивается со следующими проблемами масштабирования:
- Ограничения CPU на хостах
- Ограничения IO в хранилищах данных
- Ограничения пропускной способности сети между хостами
Способы преодоления этих проблем масштабирования ничем не отличаются от способов, применяемых в немикросервисных архитектурах. Мало того, третья проблема встречается в основном именно в микросервисной архитектуре.
Выводы
- Микросервисная архитектура не является способом масштабирования проекта. Микросервисная архитектура - это способ разделения проекта на модули и инкапсуляции кода и данных.
- Основу масштабирования практически любого большого проекта следует искать в области хранения и обработки хранящихся данных.
Монолиты и микросервисы: граница
Если рассмотреть развитие аналогичного монолитного сервиса примерно в таком же ключе, как мы рассматривали выше развитие микросервиса, то в результате его развития будут пройдены те же стадии преодоления проблем. В итоге структура монолита будет включать в себя те же самые компоненты. А если взглянуть на серверное разделение, то будут включать в себя выделенный сервер (кластер серверов) авторизации и сервер регистрации пользователей. Однако, эта структура будет оставаться монолитной.
В чём же отличие? Почему монолит, разделённый на сервисы, остаётся монолитом? Потому что во всех его сервисах используется единая кодовая база.
Если один и тот же код, не будучи выделен в библиотеку, работает во множестве микросервисов, то это - монолитная архитектура.
Построение проектов с нуля: Монолит vs микросервис
Если не рассматривать вновь запускаемые проекты на лямбдах/FaaS, то можно отметить одну чуть ли не во всех проектах встречающуюся особенность:
как правило, проект на стадии запуска реализации и на стадии запуска MVP отличается довольно сильно. Видение бизнес-развития проекта в стадии после MVP отличается от стартового ещё сильнее. И, чем больше времени проект развивается, тем сильнее эти отличия.
Бизнес-требования к стартующему проекту обычно меняются прямо в процессе реализации его MVP. Да, это не для всех случаев так, но для огромного пула стартапов это именно так.
Что из этого следует? Из этого следует эмпирическое правило: для запуска стартапов необходимо выбирать технологии, исходя из критериев:
- в дальнейшем понятно, как масштабировать (в основном, это относится к хранилищу)
- сравнительно просто рефакторить (это относится к выбору технологии построения кода)
- простое покрытие автоматическими тестами
Для рефакторинга и простоты покрытия тестами монолиты подходят идеально
- позволяют работать в режиме “сперва взлетаем, а затем думаем о том, какие крылья нам лучше использовать”.
И, исходя из написанного, энергию вечного спора “монолит vs микросервис” на стадии запуска проекта надо направить в русло проработки хранилища данных с изначальной ориентацией на масштабирование. А в процессе развития монолит и микросервис будут иметь весьма похожую архитектуру. Настолько похожую, что отличить их друг от друга будет сложно.