Spring Webflux集成Mono时出现响应格式异常问题求助
问题分析与解决思路
核心问题根源
- 线程安全隐患:
LinkedList并非线程安全集合,在多个Mono的doOnNext回调(可能在不同线程执行)中并发修改该集合,会触发并发修改异常,进而引发NullPointerException,最终导致响应序列化失败。 - 异步模型混用冲突:
@Async注解基于线程池实现异步,与WebFlux原生的事件循环非阻塞异步模型冲突,会导致线程模型混乱,放大并发问题的概率。 - 缺失异常处理逻辑:
Flux.merge后的序列仅处理了onNext成功场景,未处理onError异常分支,导致DeferredResult可能未被正确设置。此时容器或全局异常处理器会额外输出错误信息,与已写入响应流的JSON数组拼接,形成[{"key":"value"}]SERVER_ERROR这类畸形响应。 - 错误的结果收集方式:手动通过
doOnNext往共享集合添加元素,违背响应式编程的无状态原则,是引发并发问题的直接诱因。
解决步骤与代码修正
1. 统一响应式编程模型,移除@Async
抛弃DeferredResult+@Async的混合模式,完全使用WebFlux原生响应式API,避免线程模型冲突:
修正后的Controller
@GetMapping public Mono<ResponseEntity<Object>> list(Data param) { return service.getIds(param) // 成功时返回结果数组 .map(results -> new ResponseEntity<>(results, HttpStatus.OK)) // 异常时返回统一格式的错误VO .onErrorResume(e -> { ResultVO errorResult = new ResultVO("SERVER_ERROR", e.getMessage()); return Mono.just(new ResponseEntity<>(errorResult, HttpStatus.INTERNAL_SERVER_ERROR)); }); }
修正后的Service
@Override public Mono<List<ResVO>> getIds(Data param) { List<Mono<ResVO>> monoList = new ArrayList<>(); monoList.add(this.getSomethingByWebclient(param.getName(), specificServerInfo)); // 用响应式操作符collectList直接收集结果,无需手动维护共享集合 return Flux.merge(monoList) .collectList(); }
2. 补充异常边界处理
针对单个Mono调用的异常,可以添加容错逻辑,避免单个请求失败导致整个序列中断:
// 在添加Mono时,为单个调用添加容错 monoList.add(this.getSomethingByWebclient(param.getName(), specificServerInfo) .onErrorReturn(new ResVO())); // 或返回默认值、空对象
3. 关于畸形响应的解释
当原代码中Flux序列触发异常时,subscribe仅处理了成功场景,DeferredResult未被正确设置。容器会判定请求未正常完成,触发超时或异常处理流程,将错误信息(如SERVER_ERROR)写入响应流;而此时之前已经有部分JSON数组内容被输出,最终导致两者拼接形成非法响应格式。
内容的提问来源于stack exchange,提问作者Minseok Jeon
相关产品推荐
相关产品推荐

