В чем разница создания Mono через just и через Callable в Spring WebFlux
Что проверяют
Понимаете ли вы разницу между сборкой реактивной цепочки и её выполнением. Это главный источник ошибок в WebFlux: блокирующий вызов, который случайно выполняется при сборке. Следом обычно спрашивают, как правильно обернуть блокирующий JDBC-вызов или старый клиент.
Как отвечать
Mono.just(value): жадный. Аргумент вычисляется сразу, как только Java дошла до выражения, до всякой подписки.Mono.just(repository.findById(id))сходит в базу, даже если наMonoникто не подпишется, а каждый подписчик получит одно и то же заранее посчитанное значение.Mono.just(null)броситNullPointerException, для nullable естьMono.justOrEmpty;Mono.fromCallable(callable): ленивый.Callableвызывается заново при каждой подписке. Исключение, даже проверяемое, превращается в сигналonError, и его ловятonErrorResumeилиonErrorReturn. Если вернулсяnull,Monoзавершится пустым;- поток выполнения:
fromCallableсам по себе не асинхронный. Код выполнится там, где произошла подписка, в WebFlux это event loop Netty. Блокирующий вызов уводят явно:Mono.fromCallable(() -> jdbc.query(...)).subscribeOn(Schedulers.boundedElastic()); - родственники:
Mono.fromSupplierто же без проверяемых исключений,Mono.defer(() -> Mono.just(compute()))откладывает сборку целогоMono.
Короткое правило: just для готового значения или константы, fromCallable для дорогого вычисления, побочного эффекта или кода, который может бросить исключение.
Где спотыкаются
Пишут Mono.just(blockingCall()) и считают код реактивным. Вызов блокирует поток при сборке, а его исключение вылетит мимо цепочки, и onErrorResume его не увидит.
Думают, что fromCallable сам уходит в фоновый поток. Без subscribeOn блокирующий вызов займёт поток event loop, и под нагрузкой сервис встанет.
Ждут от just нового вычисления при повторной подписке, например в retry(). Повтор получит то же значение, для настоящего повтора нужен fromCallable или defer.