Архитектурные паттерны: горизонтальное масштабирование и CQRS

Не секрет, что сегодня интернет-сервисы подвергаются усиленной нагрузке. Случается, что торговые сети даже останавливают сайты с заказами по причине нехватки имеющихся мощностей. Но если запросы клиентов не обрабатывать или обрабатывать с перебоями, клиенты, разумеется, уйдут к конкурентам. Избежать этого помогут архитектурные практики, позволяющие создавать быстрые и отказоустойчивые сервисы. Рассмотрим некоторые из таких паттернов.

Горизонтальное масштабирование

Хорошо известное решение. При горизонтальном распределении нагрузки сервисы могут работать параллельно, то есть нагрузку можно распределять. При вертикальном масштабировании в случае повышения нагрузки вам надо заказывать более мощные серверы либо оптимизировать код.

Для примера можно взять абстрактное облачное хранилище файлов:

Думаю, разница между подходами очевидна: в вертикальном масштабировании мы наращиваем мощность имеющихся узлов, в горизонтальном — добавляем новые узлы, позволяющие распределять нагрузку.

Все бы ничего, но горизонтальное масштабирование имеет свои системные сложности. Например, в плане синхронизации сервисов. Что будет, когда пользователь сохранит файл с планшета, а потом захочет посмотреть этот файл со смартфона? И так далее.

CQRS

Command Query Responsibility Segregation — важный архитектурный паттерн, позволяющий различным клиентам как подключаться к разным сервисам, так и получать одинаковые потоки событий. Для простого приложения бонусы CQRS могут быть не так очевидны, а вот для нагруженного все с точностью да наоборот. Суть следующая: входящие и исходящие потоки данных пересекаться не должны. Грубо говоря, вы отправляете запрос в сервис A, а ответ получаете в сервисе B.

Бонус № 1 — возможность разрыва соединения во время выполнения долгого запроса.

Возьмем для примера классическую последовательность: 1. Клиент отправляет запрос на сервер. 2. Сервер запускает долгую обработку. 3. Сервер отвечает клиенту, предоставляя результат.

Допустим, в пункте № 2 произошел обрыв связи (или, к примеру, сеть переподключилась либо пользователь перешел на другую страницу, оборвав при этом соединение). В обычной ситуации серверу будет непросто отправить ответ пользователю с данными, что именно обработалось. Если же использовать CQRS, последовательность слегка изменится: 1. Клиент подписывается на обновления. 2. Клиент отправляет запрос на сервер. 3. Сервер отвечает, что «запрос принят». 4. Сервер отвечает результатом, используя для этого канал из пункта «1».

Эта схема уже сложнее. Мало того, здесь отсутствует интуитивный подход request-response. Зато обрыв связи во время обработки запроса не станет причиной ошибки, что немаловажно. Мало того, если пользователь действительно подключен к сервису сразу с нескольких устройств (к примеру, со смартфона и ПК), то можно сделать так, дабы ответ приходил сразу на 2 устройства.

Если посмотреть на это с практической точки зрения, мы получим дополнительные бонусы, которые связаны с тем, что однонаправленный поток можно будет обрабатывать в функциональном стиле (применяя RX и аналоги). А вот это уже является существенным плюсом, ведь, по сути, программное приложение можно сделать реактивным, причем еще и с использованием функционального подхода. Что уже может сэкономить ресурсы на разработку и техподдержку.

Объединив данный подход с горизонтальным масштабированием, мы получим очередной бонус: возможность, позволяющую отправлять запросы на один сервер, а ответы получать от другого сервиса. В результате клиент сможет самостоятельно выбирать более удобный для него сервис, а уже система внутри все равно корректно обработает события.

По материалам блога компании «Технологический Центр Дойче Банка».