Как работает реактивное программирование
Что проверяют
Понимаете ли вы модель, а не только названия Mono и Flux: кто кому передаёт данные, откуда берётся обратное давление и на каких потоках всё выполняется. Следом часто спрашивают, зачем реактивный стек после виртуальных потоков Java 21.
Как отвечать
Основа это спецификация Reactive Streams, с Java 9 она есть в JDK как java.util.concurrent.Flow:
Publisher: источник, у него толькоsubscribe;Subscriber: получает сигналыonSubscribe,onNext,onError,onComplete;Subscription: канал между ними. Черезrequest(n)подписчик говорит, сколько элементов готов принять, это и есть backpressure;Processor: подписчик и издатель одновременно, промежуточное звено.
Цепочка операторов (map, flatMap, filter) это описание конвейера, и до подписки не выполняется ничего. Запросы request идут снизу вверх, данные сверху вниз. Потоками управляют явно: subscribeOn задаёт поток, где стартует источник, publishOn переносит дальнейшую обработку на другой Scheduler.
Библиотеки: Project Reactor (Mono на 0..1 элемент, Flux на 0..N) лежит в основе Spring WebFlux, который крутится на нескольких потоках event loop Netty; в RxJava 3 backpressure есть у Flowable, а у Observable нет. Для базы нужен неблокирующий драйвер R2DBC, JDBC в такую цепочку не встраивается.
Честный вывод: реактивный стек даёт пропускную способность при множестве медленных I/O-операций на малом числе потоков, но не ускоряет отдельный запрос и усложняет отладку. С виртуальными потоками многие такие сервисы проще писать в блокирующем стиле.
Где спотыкаются
Вызывают блокирующий код внутри цепочки: JDBC, Thread.sleep, block(). Поток event loop встаёт, а с ним и все запросы на нём. Такие места ловит BlockHound.
Собирают цепочку и не подписываются, а потом удивляются, что запрос не ушёл.
Не могут прочитать стектрейс, состоящий из внутренностей операторов. Помогают checkpoint() и Hooks.onOperatorDebug().