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

Spring Webflux集成Mono时出现响应格式异常问题求助

问题分析与解决思路

核心问题根源

  1. 线程安全隐患:LinkedList并非线程安全集合,在多个Mono的doOnNext回调(可能在不同线程执行)中并发修改该集合,会触发并发修改异常,进而引发NullPointerException,最终导致响应序列化失败。
  2. 异步模型混用冲突:@Async注解基于线程池实现异步,与WebFlux原生的事件循环非阻塞异步模型冲突,会导致线程模型混乱,放大并发问题的概率。
  3. 缺失异常处理逻辑:Flux.merge后的序列仅处理了onNext成功场景,未处理onError异常分支,导致DeferredResult可能未被正确设置。此时容器或全局异常处理器会额外输出错误信息,与已写入响应流的JSON数组拼接,形成[{"key":"value"}]SERVER_ERROR这类畸形响应。
  4. 错误的结果收集方式:手动通过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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.17 16:44:57