В работе рассматривается способ организации архитектуры web-приложения на основе паттерна CQRS. В основе архитектуры лежит разделение на write- и read- модели, которые используют SQL и NoSql базы данных. Результатом применения архитектуры стало возможность масштабирования системы. В стуки система способна обрабатывать 700 тыс. запросов на чтение, а время отдачи данных в веб часть при такой нагрузке составляет 150 мс в среднем, что является хорошим показателем для высоконагруженного web-приложения.
Ключевые слова: CQRS, архитектура, web-приложение, масштабирование
В течение нескольких лет мы разрабатывали информационный веб-портал для создания, распределения и назначения заказов. Сначала система была развернута внутри компании и пользователями являлись сотрудники, которые контролировали заказы. Заказы исходили только от одной компании. Внешние пользователи лишь могли получать информацию.
Следующим этапом было масштабирование системы на нескольких заказчиков, что пропорционально увеличивало внешних пользователей. С увеличением количества пользователей портал перестал справляться с нагрузкой. В пике время отдачи данных в веб часть могло составлять 400–700 мс. После анализа было установлено, что узким местом оказалась реляционная база данных (SQL).
Были проведены несколько оптимизаций:
1) Проведена оптимизация скриптов выборки;
2) Некоторые скрипты выборок были вынесены в хранимые процедуры SQL;
3) Была убрана ORM;
4) Была проведена оптимизация индексов.
Все это некоторое время помогало, но в итоге было принято решение переводить систему на новую архитектуру.
О CQRS паттерне
Шаблон Command-Query Responsibility Segregation (CQRS) предлагает разделение работы с объектом (и это не обязательно база данных) на Запросы (Query) и Команды (Commands). При этом необходимо соблюдать следующие правила:
– Запросы возвращает данные и, что важно, никогда не меняют состояние объекта;
– Команды изменяют состояние объекта, но, в идеальном случае, не должны ничего возвращать.
Из этого следует, что во время отсутствия Команд:
– одинаковые Запросы гарантировано вернут одинаковый результат;
– любое количество любых Запросов не изменят состояние объекта;
– удаление Запроса из кода абсолютно прозрачно для объекта и не может дать побочных действий.
Кроме того, подход, предлагаемый CQRS, позволяет сделать код приложения более понятным именно благодаря разделению Команд и Запросов. Соответственно, в дальнейшем такое приложение легче поддерживать и модифицировать.
Но основным преимуществом применения CQRS является разделение хранилищ информации на два типа:
1) Реляционное хранилище для Команд;
2) Денормализованное хранилище для Запросов.
Эволюция CQRS добавила использование паттерна Event Sourcing (хранилище событий). Идея Event Sourcing в том, чтобы записывать каждое событие, которое меняет состояние приложения в базу данных. Таким образом получается, что хранится не состояние сущностей, а все события, которые к ним относятся. В обычной реляционной модели происходит манипулирование состоянием, оно храниться у в базе, и всегда есть возможность его посмотреть. В случае с Event Sourcing оперируем с состоянием сущности. Но в отличии от обычной модели это состоянием не хранится, а воспроизводится каждый раз при обращении.
Большим преимуществом Event Sourcing является, то что используя его можно восстановить любое состояние системы.
На рисунке 1 представлена типовая схема паттерна CQRS.
Рис. 1. Архитектура приложения, основанная на паттерне CQRS
Проектирование архитектуры приложения
При переходе на новую архитектуру важна была скорость самого перехода, а проектирование агрегатов занимало много времени, которое решено было сохранить. Как итог совместно с Event Store осталась реляционная модель данных. Event Store остался в пользование только как надежная синхронизация данных для Read модели.
Далее встал вопрос: каким образом из Event Store обновлять Read DB. Read модель отвечает только за запросы к Read DB, а значит, что применять Event в базу не является ее ответственностью.
Для обновления Read DB был создан Event Service, который получал уведомление о новом Event, читал его с Event Store и обновлял Read DB.
Следующий вопрос, который необходимо было решить: надежность обновления Read DB. Использование Event Store поднимало надежность системы в том, что данные точно не будут потеряны. Но если в процессе выполнения операции в Write модели, после сохранения Event в Event Store, связь для обновления Read DB будет разорвана, то Read DB обновлена не будет. Поэтому для нотификации Event Service была выбрана очередь сообщений на основе сервиса RabbitMQ.
RabbitMQ имеет два необходимы преимущества для проекта:
1) Гарантированная доставка сообщения
2) Восстановление после потери связи
Последний вопрос: в каком виде хранить данные в Read DB. База данных для Read DB для простоты была выбрана MS SQL. База данных не имеет связей между таблицами, а вместо связей в таблицах хранятся значения. Таким образом происходит выигрыш в чтении, так как читаются плоские данные.
Итоговая архитектура портала приведена на рисунке 2.
Рис. 2. Архитектура web-приложения
Вывод
Итогом новой применения новой архитектуры стало значительное увеличение нагрузки на приложение без увеличения времени ответа. Статистика показывает, что в сутки приходит в среднем 700 тысяч запросов данных, а среднее время ответа сервера составляло 150 мс. Но у данной архитектуры есть и минусы:
– Размер инфраструктуры увеличился в разы относительно многослойной, что увеличило затраты на проект;
– Для обеспечения принципа согласованности в конечном счете в системе ожидается, когда данные обновятся в Read DB, что привело к увеличению на 20 % времени обработки запроса на запись;
Литература:
- Greg Young — CQRS Documents by Greg Young [Электронный ресурс] // CQRS Documents. Режим доступа: http://cqrs.files.wordpress.com/2010/11/cqrs_documents.pdf
- Dominic Betts, Julián Domínguez, Grigori Melnik, Fernando Simonazzi, Mani Subramanian, Foreword by Greg Young — Exploring CQRS and Event Sourcing [Электронный ресурс] // Режим доступа: http://www.microsoft.com/enus/download/details.aspx?id=34774
- Martin Fowler — CQRS [Электронный ресурс] // Режим доступа: http://martinfowler.com/bliki/CQRS.html.