Реактивное программирование

Реактивное программирование

Svetlana @my_programmer_notes


Проблемы традиционного подхода

В классических веб-приложениях у контейнера сервлетов есть выделенный пул потоков для обработки HTTP-запросов, где каждому входящему запросу будет назначен поток, и этот поток будет обрабатывать весь жизненный цикл запроса.

На схеме представлено взаимодействие компонентов в распределенной системе. Проблема тут в том, что при использовании традиционного императивного программирования с синхронными вызовами для взаимодействия между сервисами потоки часто блокируются в ожидании ответа от другого сервиса и фактически простаивают, тратя большое количество ресурсов.

Это ограничивает количество одновременно обрабатываемых запросов. Например, если у нас есть 500 потоков со средним временем отклика 250 миллисекунд, мы сможем обслуживать около 2000 запросов в секунду. Но что произойдет, когда нагрузка возрастет? В какой-то момент свободные потоки закончатся, и мы начнем терять запросы.

Решение: реактивные системы

Чтобы избежать подобных проблем, необходимо изменить подход к проектированию систем. Для этого был создан "Манифест реактивных систем", который описывает основные принципы:

Отзывчивость: системы должны давать своевременный ответ.

Устойчивость: они остаются отзывчивыми даже в аварийных ситуациях.

Гибкость: быстрая реакция на различные рабочие нагрузки.

Асинхронный обмен сообщениями: в основе всего этого лежит асинхронность.

В тот момент когда поток А в приложении на Spring MVC + Tomcat будет находится в блокирующем ожидании (например, потоки закончились и мы больше не можем обработать ни один дополнительный запрос), поток А в приложении на Project Reactor + Netty будет обрабатывать еще один запрос.

Понимание реализации

Теперь, когда мы разобрались с архитектурой, важно понять реализацию. Для этого существует спецификация Reactive Streams — инициатива, запущенная инженерами из Netflix, Lightbend и Pivotal в 2013 году. Эта спецификация определяет типы для взаимодействия между различными библиотеками реактивного программирования на JVM.

На основе этой спецификации и был написан Project Reactor.

Project Reactor

Project Reactor (или просто Reactor) — это реактивная библиотека для создания неблокирующих приложений на JVM, основанная на спецификации Reactive Streams. Это основа реактивного стека в экосистеме Spring. Reactor использует функциональный стиль программирования и может быть сложнее для чтения, особенно если вы не знакомы с теми или иными операторами и пока вы не привыкли к новым типам данных - Mono и Flux. Но у них прекрасная документация и я очень рекомендую заглядывать в нее в первую очередь.

Spring WebFlux

Spring WebFlux — веб-фреймворк, асинхронный и неблокирующий. По умолчанию он использует Netty. Тут есть такой интересный неочивидный момент связанный с работой backpressure - "из коробки" он не работает. Можно контролировать поток данных с помощью специальных операторов Project Reactor или реализовав собственный Subscriber. Однако этот контроль далек от идеала; для полноценной реализации нам нужно прикручивать протокол — RSocket, который обеспечивает управление потоком данных. 

Взаимодействие с базами данных

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

Взаимодействие же с реляционными базами данных требует более тщательного рассмотрения. Spring Data предлагает клиент R2DBC. Это не полный ORM как JPA; он не предлагает таких функций как кеширование или отложенная загрузка. Он обеспечивает функциональность отображения объектов и абстракцию репозитория.

Однако R2DBC имеет свои недостатки. Например, производительность на Postgres может быть довольно низкой; запросы на select выполняются примерно в два раза медленнее по сравнению с Spring Data JDBC. Этот недостаток разработчики Spring Data не смогут обойти в ближайшее время из-за нюансов работы драйверов Postgres (очень советую посмотреть этот доклад). Как отмечают сами разработчики R2DBC: "Вы не переходите на реактивное программирование только потому, что хотите, чтобы ваши запросы выполнялись быстрее. Вы применяете шаблоны реактивного программирования, чтобы повысить масштабируемость и отказоустойчивость вашего приложения." Об этом стоит помнить.

Заключение

Крайне важно взвесить все плюсы и минусы реактивного стека. Он точно не нужен абсолютно каждому приложению, но иногда он необходим. Я, к сожалению, столкнулась с таким случаем, когда проект написали на Project Reactor, а потом пришлось его переписывать на классический стек. 

В общем, как и всегда — для каждого инструмента свой случай.

Report Page