Project Reactor错误处理器默认运行在守护线程吗?异步测试解惑
关于Project Reactor异步操作在测试中无输出的问题解释
问题描述
这是相关问题的后续提问。在学习Project Reactor时,尝试捕获框架内部吞掉的错误,编写了如下测试代码:
@Test void test() { WebClient.builder().build() .get() .uri("https://httpbin.org/status/404") .retrieve() .bodyToMono(String.class) .subscribe( s -> System.out.println("Success: " + s), t -> System.out.println("Failure: " + t.getMessage()) ); }
运行后发现程序没有任何输出,直到在方法末尾添加Thread.sleep(5000)后,才会打印出错误信息:
@Test void test() { // 上述WebClient代码... Thread.sleep(5000); }
推测是代码在守护线程中执行,导致JVM不会等待其完成,但官方指南和方法文档中没有明确说明默认调度器/线程的相关细节,调试也没发现明显的并发类,想知道该行为的正确解释。
问题解释
核心原因是测试主线程与异步操作线程的生命周期差异:
- 调用
subscribe()后,WebClient基于Netty发起的HTTP请求是异步执行的,默认使用Netty的事件循环线程——这类线程默认是守护线程。 - JVM的退出规则是:只要所有非守护线程都执行完毕,JVM就会终止,不管守护线程是否还在运行。
@Test方法运行在测试主线程(非守护线程)中,调用subscribe()后会立即返回,主线程很快执行完整个test()方法就结束了。此时Netty的守护线程还没来得及完成HTTP请求、触发subscribe()里的回调,JVM就已经退出,自然看不到任何输出。- 添加
Thread.sleep(5000)本质是让测试主线程暂停,给守护线程足够的时间完成HTTP请求并执行回调逻辑,所以能看到输出。
测试中正确的处理方式
在Reactor测试场景下,不推荐用Thread.sleep()这种不可靠的方式,更规范的做法有两种:
- 使用
block()方法:让当前线程(测试主线程)等待异步操作完成,比如把subscribe()换成block(),或者在链末尾调用.block():@Test void test() { try { WebClient.builder().build() .get() .uri("https://httpbin.org/status/404") .retrieve() .bodyToMono(String.class) .block(); } catch (Exception e) { System.out.println("Failure: " + e.getMessage()); } } - 使用Reactor官方的测试工具
StepVerifier:专门用于测试Reactor序列,能精准控制异步操作的执行和断言:@Test void test() { WebClient.builder().build() .get() .uri("https://httpbin.org/status/404") .retrieve() .bodyToMono(String.class) .as(StepVerifier::create) .expectErrorMatches(t -> t.getMessage().contains("404")) .verify(); }
内容的提问来源于stack exchange,提问作者Sergey Zolotarev
相关产品推荐
相关产品推荐

