Spring Reactive упрощенно

Реактивщина стала поддерживаться Спрингом с 2017 года. Но через 6 лет многие так и не осознали, где её применять и зачем она нужна. А ведь для Спринга это стало целой новой эпохой.

Spring Reactive упрощенно IT, Программист, Java, Telegram (ссылка), Длиннопост

Реактивнища это не изобретение Спринга.

Первое что нужно знать — реактивщина и реактивный подход не являются изобретением Спринга. Наоборот спринг как обычно поглотил очередную технологию, в данном случае Project Reactor

Реактивный дух времени.

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

Синхронный блокирующий подход:

Предположим что на мнужно выполнить 3 задачи. В старом синхронном подходе мы получим код который будет запущенным одним потоком поочередно и блокирующе.

Spring Reactive упрощенно IT, Программист, Java, Telegram (ссылка), Длиннопост

синхронный подход

Реактивный подход

Теперь перепишем ту же задачу используя реактивщину, встроенную в JDK еще с Java 9:

Spring Reactive упрощенно IT, Программист, Java, Telegram (ссылка), Длиннопост

Создаем паблишер и отправляем ему 3 задачи.

Различия. Небольшая большая разница.

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

Но почему реактивщина стала с каждым днем становится все более популярней?

Причин довольно много, но я упомяну лишь два наиболее важных (по моему мнению):

  1. Эффективное использование ресурсов (это утверждение истинно только если вы используете неблокирующий код).

  2. Отзывчивость системы (Responsive)

Реактивный + неблокирующий подходы = нефть, золото, греча и, конечно же, акции эппл.

Spring Reactive упрощенно IT, Программист, Java, Telegram (ссылка), Длиннопост

Отгадайте, кто сожрет все потоковые ресурсы?

Если проанализировать среднестатистический цикл жизни потока то можно заметить что большую часть времени (иногда по 99% времени)

находится либо:

  1. В состоянии ожидании доступа к локу те в состоянии ожидании монитора BLOCKED или WAITING

  2. В "логическом ожидании" какого то ресурса как то ответа базы, сервиса или просто базы.

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

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

Отзывчивость системы. (Responsiveness)

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

Давайте уже, что нибудь попишем.

Как я уже упомянул есть несколько реактивных решений, но в рамках статьи мы напишем Спринговые классические решения.

Перед тем как начать. Если выше на путь реактивнищы - пути обратно нет. Все слои внутри приложений надо стараться писать именно в этом стиле. И конечно не забывать про неблокируйщий подход. Если следовать блокирующему то потоки будут стопорится именно у такой блокируюей воронки.

Что это значит на практике?

А на практике это значит что старые библиотеки вроде rest template (общение по http), jdbc template (связь с базой) должны быть выпилены и заменены не реактивные и неблокирующие:

  1. Rest Template -> WebClient

  2. JdbcTemplate -> DatabaseClient (R2DBC)

  3. FileSystemResource (чтение файлов) -> ReadableResource

  4. И так далее для соответствующий библиотек.

Пишем классический Фронт Бэк База на Спринге.

Пример ниже не является самым сложным, но скорее показательным. Напишем простую локику:

  1. К нам приходит http запрос

  2. Мы перенаправляем запрос в базу

  3. Как база дает первый ответ мы кидаем данные на фронт

  4. Данные на фронте парсятся по мере появления (а не лишь когда все будут доступны)

Если говорить упрощенно то весь код по сути будет сводится к тому что мы будем прокладывать трубы в виде Flux или Mono объектов.

Стоит помнить что вся реактивная магия Спринга возможно лишь когда при его старте используется WebFlux'овый фреймворк. Старый вариант запуска Спринга с каким нибудь томкатом под капотом не сработает. Поэтому зависимости ниже обязательны:

Spring Reactive упрощенно IT, Программист, Java, Telegram (ссылка), Длиннопост

Начнем с базы, используем

Пробросим Flux используя DatabaseClient:

Spring Reactive упрощенно IT, Программист, Java, Telegram (ссылка), Длиннопост

Теперь полученный Flux выставим на сторону фронта:

Spring Reactive упрощенно IT, Программист, Java, Telegram (ссылка), Длиннопост

Наверно вы заметили что возвращаемый тип не application/json. Да тип x-ndjson. Если использовать стандартный application/json то данные которые будут улетать на фронт будут неполный и конвертировать их в целые объекты будет головной болью. Это недостаток потокового подхода (хотя по сути это 1 http запрос, просто растянутый). x-ndjson формат позволяет кидать на фронт кидать объекты

Читаем на фронте. Код неидеален, скорее служит в качестве простого примера.

Spring Reactive упрощенно IT, Программист, Java, Telegram (ссылка), Длиннопост

Код выше делает следующее:

  1. Делает http запрос

  2. Ожидает ответа

  3. Как только ответ прилетает начинает читать его по частям и писать в конcоль

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

Еще раз про эффективный расход процессорных ресурсов.

Я еще раз хочу упомянуть что реактивные, неблокирующие решение написанные прямыми руками позволяют выжать максимум из предоставленных ресурсов. Особенно это актуально во времена микросервисов. Все чаще на проектах под небольшой микросервис могут выделить не более чем 1/0.5/0.1 CPU и я в общем поддерживаю такой подход.

Виртуальные потоки VS Реактивщина.

Эта тема заслуживает отдельного поста. И он будет следующим если эта статья зайдет. Дайте знать в комментах если интересно.

Спасибо за внимание, больше бесплатных статей и стримов в моей группе про джаву и смежные технологии.