You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Reactor中Mono顺序执行报错:block()阻塞操作不被支持

问题解决:Reactor中block()调用导致的IllegalStateException

场景与问题

现有两个响应式函数:

Mono<Void> messageEvent(MessageEvent messageEvent);
Mono<Victim> getPersonById(String personId);

需求:messageEvent执行成功后,调用getPersonById,并将返回的Victim存入数据库。

尝试代码:

spi.messageEvent(message).doOnSuccess(it -> {
    spi.getPersonById(evt.getVictimId()).doOnSuccess(victim -> {
        repository.save(victim);
    }).block();
}).block();

触发错误:

2023-03-09 21:56:20.191 ERROR 21080 --- [nister-group]-0]
c.s.e.v.s.q.i : on()方法中出现异常,原因='NULL',异常信息='block()/blockFirst()/blockLast()是阻塞操作,在reactor-http-nio-2线程中不被支持'
java.lang.IllegalStateException: block()/blockFirst()/blockLast()是阻塞操作,在reactor-http-nio-2线程中不被支持
at reactor.core.publisher.BlockingSingleSubscriber.blockingGet(BlockingSingleSubscriber.java:83)

错误原因

你在Reactor的非阻塞IO线程(如reactor-http-nio-2)中调用了block(),违反了响应式编程的非阻塞原则:

  • IO线程是Reactor专门处理非阻塞任务的资源,数量有限
  • 阻塞调用会占用线程,导致线程池耗尽,服务无法处理后续请求

正确实现

情况1:数据库操作是响应式的(如Spring Data Reactive)

用响应式操作符链式调用,完全避免阻塞:

spi.messageEvent(message)
    // 等待messageEvent执行完成,再触发getPersonById
    .then(spi.getPersonById(evt.getVictimId()))
    // 将查询结果存入数据库
    .flatMap(victim -> repository.save(victim));

情况2:数据库操作是阻塞的(如Spring Data JPA)

需将阻塞操作包装到弹性线程池执行,避免阻塞IO线程:

import reactor.core.scheduler.Schedulers;

spi.messageEvent(message)
    .then(spi.getPersonById(evt.getVictimId()))
    // 用fromCallable包装阻塞的save方法,指定弹性线程池执行
    .flatMap(victim -> Mono.fromCallable(() -> repository.save(victim))
                            .subscribeOn(Schedulers.boundedElastic()));

注意事项

如果这段代码用于WebFlux控制器,直接返回该Mono对象即可,框架会自动处理订阅和线程调度,绝对不要手动调用block()。

内容的提问来源于stack exchange,提问作者Aymen Kanzari

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.28 18:27:18