Mono超时后RestController未即时返回响应,仍等待执行完成
问题分析与解决方案
问题原因
你遇到的问题核心是:Mono.fromCallable默认会在WebFlux的处理线程中执行Callable逻辑,而Thread.sleep(10000)是阻塞操作,会直接占用这个线程。虽然timeout在50ms后触发了回退逻辑,但原Callable的阻塞操作仍在占用处理线程,导致响应必须等sleep完成后才能返回——因为处理线程被卡住,没法及时把回退结果输出给客户端。
解决方法
要让超时后立即返回响应,必须把阻塞逻辑(模拟数据库调用的sleep)放到独立线程池中执行,避免阻塞WebFlux的工作线程。通过subscribeOn指定弹性调度器(比如Schedulers.boundedElastic()),让阻塞任务在单独线程里运行,WebFlux的处理线程就能及时处理超时回退并返回响应。
修改后的代码如下:
import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; import reactor.core.publisher.Mono; import reactor.core.scheduler.Schedulers; import java.time.Duration; @RestController public class DummyController { @GetMapping("/get/mono") public Mono<String> getTest(Integer groupId) { return Mono.fromCallable(() -> { Thread.sleep(10000); // 模拟长时间数据库调用 return "Success"; }) // 将阻塞任务放到弹性线程池执行 .subscribeOn(Schedulers.boundedElastic()) .timeout(Duration.ofMillis(50), Mono.fromCallable(() -> { System.out.println("timed out"); return "timeout"; })) .onErrorResume(e -> Mono.just(e.getMessage())); } }
关键说明
Schedulers.boundedElastic()是Reactor专门处理阻塞操作的线程池,会根据需求动态创建线程,同时限制最大线程数,避免资源耗尽。- 加上
subscribeOn后,Callable逻辑在独立线程执行,WebFlux处理线程不会被阻塞,超时触发后能立刻把回退结果返回给客户端,无需等待阻塞任务完成。 - 在WebFlux中,任何阻塞操作(比如Thread.sleep、JDBC同步调用等)都应该放到独立线程池执行,否则会破坏非阻塞模型的性能和响应性。
内容的提问来源于stack exchange,提问作者Rahul Gul
相关产品推荐
相关产品推荐

