如何在Spring Flux错误处理程序中访问参考源对象?

时间:2019-08-14 13:46:28

标签: java spring spring-webflux

我有一个要同时执行的List<Mono>阻止Web请求的列表。

以下通常可以正常工作,但是:发生异常时,我想在分配给导致异常的请求的Map中收集所有错误。

List<Request> bodies;
List<Mono<Response>> monos = requests.stream().map(bodies -> prepareMono(body)).collect(Collectors.toList());

Map<Request, Throwable> errors = new HashMap<>();

List<Response> results = Flux.merge(monos).collectList().onErrorContinue((ex, value) -> {
            //TODO how can I access the <Request> here who caused the exception
            errors.put(<request>, ex);
        }).block();

private Mono<Response> prepareMono(Object body) {
    return webClient.post().syncBody(body).retrieve().bodyToMono(Response.class);
}

问题:如何访问在错误处理程序中发送的请求正文?

2 个答案:

答案 0 :(得分:0)

首先,您的所有请求不会“同时”执行。

让我们讨论一下您的代码。

List<Request> bodies;
List<Mono<Response>> monos = requests.stream().map(bodies -> prepareMono(body)).collect(Collectors.toList());

您有一个List<Requests>尚未声明的地方。为什么这是清单?列表是必不可少的,它应该是Flux

您创建n个将要执行n个请求的Monos。为什么叫这个prepareMono我不知道,没有准备一个单声道。

List<Response> results = Flux.merge(monos).collectList().onErrorContinue((ex, value) -> {
        //TODO how can I access the <Request> here who caused the exception
        errors.put(<request>, ex);
    }).block();

因此,在这里您将“很快就叫休息电话”列表合并到Flux中。 API对Flux#merge

说了些什么
  

将Iterable中包含的Publisher序列中的数据合并到   交错的合并序列。

它从List<Mono<T>>创建通量而不考虑顺序。因此,它们将被一一发出,但可能无法保持原始顺序。它仍然是一个可迭代的序列

然后您执行Flux#collectList和api状态

  

在此序列完成时,将此通量发出的所有元素收集到一个列表中,该列表由生成的Mono发出。

因此,当通量序列完成时,您将返回到包含响应的Mono<List<T>>,然后在该Mono上发出一个错误时要捕获错误使用onErrorContinue

最重要的是,您想block一切。

您正在从命令式世界过渡到反应性世界,然后又回到命令式世界,在List,Flux和Mono之间跳回第四。

我们需要整理一些基础知识。

单声道 一件事。它可以包含n个事物的列表。那是一回事。

助焊剂 许多东西,可以是一个东西,也可以是列表中的许多东西。可以有很多列表,因为列表是一件事。

列表 是一种Flux,因此请不要使用单音列表,而要使用流量。

现在,我们了解了这些基础知识。我们可以看到,在您的代码中,Flux.merge(monos)将返回一个Flux<Response>(单声道响应列表),因此助焊剂将依次依次发出 并进行一次休息调用,返回响应并发出响应。

但是您要做的是collectList。收集列表必须等到此序列完成。必须拨打所有电话,并且一切都必须顺利进行,因为您想要Mono<List<Response>>

要获得完整的响应列表,所有响应都必须成功。

然后您对Mono进行错误处理,以查看其是否无法发出该列表。

您需要处理每个rest调用中的错误,这些错误要从通量中顺序进行。

Flux.merge(monos)
    .onErrorContinue((throwable, request) -> {
        // error handling
    })
    .collectList();

但是您使事情变得太复杂了,这就是我编写代码的方式。

final List<Response> responseList = Flux.fromIterable(requestList)
            .parallel(2)
            .runOn(Schedulers.parallel())
            .doOnNext(this::fooService.get)
            .onErrorContinue((throwable, o) -> {
                // add to map here
            }).doFinally(signalType -> {
                // check if map is empty if not throw exception
            }).collectList()
              .block();

最后但并非最不重要的一点是,我不知道我必须告诉人们多少次。

默认情况下,Webflux不是并发的,因为Mono和Flux在顺序链中发射时会执行,除非您将并行流量与异步使用者结合使用或在平面图中执行所有操作

如果要并行执行,则需要使用Parallel Fluxes。而且,如果您希望并发,则需要结合使用@Async注释方法和并行执行。或带有Monos的弹性调度程序。

答案 1 :(得分:0)

您可以尝试以下操作:

Flux.fromIterable(requests)
    .flatMap(request -> execute(request))
    .doOnNext(result -> {
        if (result.throwable != null)
        {
            Request request = result.request;
            Throwable throwable = result.throwable;
            // do something
        }
    });

private Mono<Result> execute(Request request)
{
    return WEB_CLIENT.post().syncBody(request).retrieve()
                     .bodyToMono(Response.class)
                     .map(response -> new Result(request, response, null))
                     .onErrorResume(e -> Mono.just(new Result(request, null, e)));
}

private class Result
{
    private Request request;
    private Response response;
    private Throwable throwable;

    private Result(Request request, Response response, Throwable throwable)
    {
        this.request = request;
        this.response = response;
        this.throwable = throwable;
    }
}

但是,在响应式链之外的地图中收集请求错误是非常难看的。