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

如何在处理Mono时主动抛出异常以触发重试?

问题分析

你当前代码的核心问题有两个:一是用doOnNext(副作用操作符)做校验抛异常,不符合Reactor的数据流设计规范;二是onErrorResume用法错误——它需要返回Mono类型的错误信号,而非直接抛出异常。另外,doOnNext里的异常虽会触发错误信号,但后续处理逻辑没正确承接,导致重试不生效。

可行解决方案

1. 用map替代doOnNext实现校验

map是转换类操作符,会直接参与数据流处理,抛出的异常会自动转为onError信号,能被下游重试机制捕获:

public Mono<Response> handleResponse() {
    return userService.getUser()
            .map(response -> {
                validate(response.getData());
                return response;
            });
}

private void validate(Data data) {
    // 校验逻辑示例
    if (data == null || data.getId() <= 0) {
        throw new RuntimeException("数据校验失败");
    }
}

这里无需额外的onErrorResume,map抛出的异常会直接进入错误流,触发重试。

2. 用handle操作符灵活控制错误信号

handle支持同时处理正常元素和错误场景,适合复杂校验逻辑:

public Mono<Response> handleResponse() {
    return userService.getUser()
            .handle((response, sink) -> {
                try {
                    validate(response.getData());
                    sink.next(response);
                } catch (RuntimeException e) {
                    sink.error(e);
                }
            });
}

通过sink.error(e)主动发送错误信号,确保下游能捕获并触发重试。

3. 配合@Retryable的正确姿势

如果你用Spring Retry的@Retryable注解,需要注意两点:

  • 必须在配置类上添加@EnableRetry开启重试功能;
  • 确保标注@Retryable的方法是被Spring代理的——也就是通过Spring容器调用该方法,不能用类内部的this直接调用,否则代理不生效,重试逻辑不会触发。

4. 除Mono自身重试外的替代方案

编程式Spring Retry

如果注解方式不满足需求,可直接用RetryTemplate实现编程式重试:

@Autowired
private RetryTemplate retryTemplate;

public Mono<Response> handleResponse() {
    return Mono.fromCallable(() -> 
            retryTemplate.execute(context -> 
                    userService.getUser()
                            .map(response -> {
                                validate(response.getData());
                                return response;
                            })
                            .block() // 仅在RetryTemplate内部同步调用,对外仍返回非阻塞Mono
            )
    );
}

自定义重试操作符封装

可以封装通用的重试逻辑,结合repeatWhen实现基于错误类型和次数的重试:

public <T> Mono<T> withCustomRetry(Mono<T> mono) {
    return mono.repeatWhen(repeatSignal -> repeatSignal
            .zipWith(Flux.range(1, 3)) // 最多重试3次
            .flatMap(tuple -> {
                Throwable error = tuple.getT1();
                int attempt = tuple.getT2();
                if (error instanceof RuntimeException && attempt <= 3) {
                    return Mono.delay(Duration.ofSeconds(1)); // 每次重试间隔1秒
                }
                return Mono.error(error);
            })
    );
}

// 使用示例
public Mono<Response> handleResponse() {
    return withCustomRetry(
            userService.getUser()
                    .map(response -> {
                        validate(response.getData());
                        return response;
                    })
    );
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.22 13:30:17