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

WebFlux项目中如何获取Mono值?解决block()阻塞报错问题

解决Spring WebFlux中block()阻塞调用的问题

问题分析

你的代码中使用block()同步获取Mono<MapFile>和Mono<UserInfo>的结果,违反了WebFlux的非阻塞响应式编程原则。WebFlux运行在非阻塞IO线程(如reactor-http-nio-*)上,阻塞调用会占用线程资源、破坏异步链特性,最终触发报错。

修改方案

核心是移除所有block()调用,改用响应式操作符组合异步数据流,通过Mono.zip合并所有需要的异步结果,在flatMap中一次性获取数据并执行后续逻辑。

修改后的完整代码

public Mono<ServerResponse> downloadAttachment(final ServerRequest request) {
    String fileName = "prova_B400.png";
    final Integer idPercorso = request.queryParam("idPercorso")
            .map(Integer::parseInt)
            .orElse(null);

    // 保存异步数据流,避免重复执行
    Mono<UserInfo> userInfoMono = super.parseUserInfo(request);
    // 通过flatMap链式调用获取MapFile的Mono,全程无阻塞
    Mono<MapFile> mapFileMono = userInfoMono.flatMap(userInfo -> 
            silverMountainService(userInfo).getMapFile(idPercorso)
    );

    // 合并所有需要的异步数据流:UserInfo、请求体、MapFile
    return Mono.zip(userInfoMono, request.bodyToMono(ObjectNode.class), mapFileMono)
            .flatMap(tuple -> {
                UserInfo userInfo = tuple.getT1();
                // ObjectNode dto = tuple.getT2(); // 后续需要请求体时可启用
                MapFile mapFile = tuple.getT3();

                // 响应式调用下载方法
                return silverMountainService(userInfo).downloadMappaPercorso(
                                StorageType.parse(mapFile.storageType),
                                mapFile.omniaClass,
                                fileName
                        )
                        .flatMap(fileResult -> ServerResponse.ok()
                                .headers(h -> h.setContentDisposition(
                                        Optional.ofNullable(fileName)
                                                .filter(StringUtils::isNotBlank)
                                                .map(filename -> ContentDisposition.attachment().filename(filename).build())
                                                .orElseGet(() -> ContentDisposition.inline().build())
                                ))
                                .contentType(MediaType.APPLICATION_OCTET_STREAM)
                                .body(BodyInserters.fromValue(fileResult.getContent()))
                        );
            })
            .onErrorResume(CustomHttpException.class, assEx -> {
                log.error("Error({}): {}", assEx.getErrorId(), assEx.getMessage());
                return ServerResponse.status(assEx.getHttpStatus()).bodyValue(String.format("ErrorID: %s", assEx.getErrorId()));
            })
            .onErrorResume(Exception.class, ex -> {
                var errId = UUID.randomUUID().toString();
                log.error(String.format("Error(%s) %s", errId, ex.getMessage()), ex);
                return ServerResponse.status(HttpStatus.INTERNAL_SERVER_ERROR).bodyValue(String.format("ErrorID: %s", errId));
            });
}

关键修改点说明

  1. 移除阻塞调用:将super.parseUserInfo(request).block()改为保存Mono<UserInfo>实例,通过flatMap链式获取Mono<MapFile>,全程保持响应式异步特性。
  2. 合并异步结果:用Mono.zip将三个独立的异步数据流合并为一个Tuple,在flatMap中一次性获取所有需要的数据,避免逐个阻塞获取。
  3. 避免重复执行:将super.parseUserInfo(request)的结果存为变量,解决原代码中两次调用导致的重复执行问题。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.16 01:34:55