Spring Reactor: Mono.zip fails on empty Mono

a 夏天 提交于 2021-01-21 03:51:05

问题


I am using Spring Reactor 3.1.0.M3 and have a use case where I need to merge Mono's from multiple sources. I found that if one of the Monos is an empty Mono, zip fails without an error.

Example:

Mono<String> m1 = Mono.just("A");
Mono<String> m2 = Mono.just("B");
Mono<String> m3 = Mono.empty();

Mono<String> combined = Mono.zip(strings -> {
    StringBuffer sb = new StringBuffer();
    for (Object string : strings) {
        sb.append((String) string);
    }
    return sb.toString();
}, m1, m2, m3);
System.out.println("Combined " + combined.block());

When m3 is added, the combinator is skipped in the response is null. When I remove m3, it all works as expected and "AB" is returned. Is there a way I could handle this by detecting the empty Mono? Also, is there a way to have the combinator method know the type of the object instead of having to cast?


回答1:


The zip operator doesn't behave like this. It would be in fact counter-intuitive: your code is expecting a Tuple of 3 elements and you're only getting two?!?

In this case, you're in control and only you can decide what's a good default value if none is provided (remember, null values are forbidden by the reactive streams specification).

Mono<String> m1 = Mono.just("A");
Mono<String> m2 = Mono.just("B");
Mono<String> m3 = Mono.empty().defaultIfEmpty("");

Mono<String> combined = Mono.when(m1, m2, m3).map(t -> {
    StringBuffer sb = new StringBuffer();
    sb.append(t.getT1());
    sb.append(t.getT2());
    sb.append(t.getT3());
    return sb.toString();
});

Edit

You seem to be confused by the nature of a Publisher type, see:

if one of the Monos is an empty Mono, zip fails without an error

and

So if I was to try and zip Mono's and for some reason one is empty, the zip would fail and I cannot seem to put in any code to safeguard against that

An empty Mono isn't a failure case: it's just that no value is emitted and it is completed successfully. You can verify that by changing the code sample:

    combined.subscribe(
            s -> System.out.println("element: " + s), // doesn't execute
            s -> System.out.println("error: " + s), // doesn't execute
            () -> { System.out.println("complete!"); // prints
    });

So depending on your requirements, you can:

  • apply a defaultIfEmpty operator on those 3 Mono instances, if there are convenient default values you can rely on
  • apply a defaultIfEmpty operator on the combined Mono, with a default value or even transform that into an error message with combined.switchIfEmpty(Mono.error(...))



回答2:


Mono.zip

An error or empty completion of any source will cause other sources to be cancelled and the resulting Mono to immediately error or complete, respectively.

When a Mono source completes without value, if you still want to continue the execution of other sources then Mono.zipDelayError is the best choice for you.




回答3:


In case of String it is quite easy to define a default value for the empty case which solves the issue nicely as described in Brian's answer. However, for other custom types it might be difficult to create an empty object for one reason or another. An alternative for these cases is to use Optional. This solution has some heavy boilerplate, though.

Mono<Optional<String>> m1 = Mono.just("A").map(Optional::of).defaultIfEmpty(Optional.empty());
Mono<Optional<String>> m2 = Mono.just("B").map(Optional::of).defaultIfEmpty(Optional.empty());
Mono<Optional<String>> m3 = Mono.<String>empty().map(Optional::of).defaultIfEmpty(Optional.empty());

Mono<String> combined = Mono.zip(strings -> {
    StringBuffer sb = new StringBuffer();
    for (Object string : strings) {
        ((Optional<String>) string).ifPresent(sb::append);
    }
    return sb.toString();
}, m1, m2, m3);
System.out.println("Combined " + combined.block());


来源:https://stackoverflow.com/questions/45727372/spring-reactor-mono-zip-fails-on-empty-mono

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!