Reactor doOnNext中阻塞/非阻塞代码逻辑及Metrics优化咨询
Reactor doOnNext 相关问题解答
先看你的代码场景:
fun test1(): Mono<ResponseFromTest2> { return test2() .flatMap { responseFromTest2 -> // do some operation Mono.just(responseFromTest2) } .doOnNext { publishMetrics(value1) publishMetrics(value2) } }
针对你的三个问题,逐一解答:
1. 在doOnNext中执行阻塞代码与非阻塞代码分别会发生什么?
doOnNext是Reactor的副作用操作符,它的执行线程完全依赖上游Mono的订阅线程(比如Netty的EventLoop线程或自定义调度器线程)。
- 阻塞代码:会直接占用当前线程,导致线程无法处理其他任务。如果是EventLoop线程被阻塞,会彻底破坏Reactor的异步非阻塞模型,引发线程池耗尽、响应延迟飙升等问题——毕竟EventLoop线程是事件驱动的,被卡住后整个事件循环都会停摆。
- 非阻塞代码:如果是纯内存操作(比如内存指标计数、简单数据计算)这类不会卡住线程的逻辑,执行起来毫无问题,只会在当前线程快速完成副作用,不会影响数据流的正常流转和系统性能。
2. 若publishMetrics为void类型函数,是否属于阻塞代码且效率低下?
返回值类型和函数是否阻塞没有直接关联,关键看函数内部实现:
- 如果
publishMetrics只是做内存级别的操作(比如给Micrometer的Counter加1、写入本地内存缓存),那它就是非阻塞的,效率极高,完全适配Reactor的模型。 - 如果
publishMetrics内部做了同步IO操作(比如同步调用HTTP接口、写数据库、读写本地文件),那不管它返回void还是其他类型,都是阻塞代码,会卡住当前线程,严重拖慢系统效率。
3. 若publishMetrics改为返回Mono以避免线程阻塞,该Mono在doOnNext中会如何处理?一般来说,doOnNext内调用函数返回的Mono会有怎样的行为?
核心点:doOnNext的参数是Consumer<T>,它只接收上游数据执行副作用,完全忽略任何返回值。所以你在doOnNext里调用返回Mono<Boolean>的publishMetrics,这个Mono根本不会被订阅——也就是说,publishMetrics内部的异步逻辑完全不会执行,等于白写。
如果要执行返回Mono的副作用,同时保留原数据流,正确的做法是改用flatMap操作符,比如:
.flatMap { response -> publishMetrics(value1) .then(publishMetrics(value2)) .then(Mono.just(response)) }
这样两个publishMetrics的Mono会被依次订阅执行,最后返回原响应数据,既保证副作用执行,又不破坏异步模型。
内容的提问来源于stack exchange,提问作者Aditya
相关产品推荐
相关产品推荐

