Реактивное программирование
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, а потом пришлось его переписывать на классический стек.
В общем, как и всегда — для каждого инструмента свой случай.