project-reactor - 如何在 Reactor 中正确调用返回未来的方法
问题描述
为了防止 XY 问题,我将从头开始:
我有一个非阻塞 SOAP 客户端,我将其包装为返回类型Mono<T>
(默认情况下它接受回调。如果需要,我可以详细说明)。
现在我想做(给定 ID):
1. 通过 ID 获取代码
2. 对代码做一些事情
3. 之后,获取Foo
并Bar
创建FooBar
我写的是:
public class MyService {
private final MySoapClient soapClient;
public Mono<FooBarDto> doSomething(String id) {
return Mono.just(id)
.flatMap(soapClient::getCode) // returns Mono<String>
.flatMap(code ->
soapClient.doSomething(code) // returns Mono<Void>
.then(getFooBar(id, code))); // See this
}
private Mono<FooBarDto> getFooBar(String id, String code) {
return Mono.zip(
soapClient.getFoo(code), // returns Mono<Foo>
soapClient.getBar(code) // returns Mono<Bar>
).map(tuple2 -> toFooBarDto(id, tuple2));
}
private FooBarDto toFooBarDto(String id, Tuple2<Foo, Bar> tuple2) {
return FooBarDto.builder()/* set properties */.build();
}
}
现在的问题是,因为 SOAP 客户端的方法不是惰性的(在您调用它们的那一刻,它们开始了进程),所以then
这里的语义不起作用。意思是我想得到什么Foo
时候Bar
完成doSomething
。他们都是一起开始的。
我试图通过更改then
为来修复它flatMap
,但让它变得更糟。getFooBar
从来没有被叫过。(1.有人可以解释为什么吗?)。
所以我最终做的是再次包装 SOAP 调用以使它们变得懒惰:
public class MySoapClient {
private final AutoGeneratedSoapClient client;
Mono<Foo> getFoo(GetFooRequest request) {
return Mono.just(request).flatMap(this::doGetMsisdnByIccid);
}
private Mono<Foo> doGetFoo(GetFooRequest request) {
val handler = new AsyncHandler<GetFooRequest>();
client.getFoo(request, handler);
return Mono.fromFuture(handler.future);
}
private static class AsyncHandler<T> implements javax.xml.ws.AsyncHandler<T> {
private final CompletableFuture<T> future = new CompletableFuture<>();
@Override
public void handleResponse(Response<T> res) {
try {
future.complete(res.get());
} catch (Exception e) {
future.completeExceptionally(e);
}
}
}
}
有没有更好的方法呢?具体来说:
2. 使用CompeletableFuture
和回调。
3. 使 SOAP 客户端中的方法变得惰性。
解决方案
我试图通过更改它来修复它,然后更改为 flatMap,但让它变得更糟。getFooBar 从未被调用过。(1.有人可以解释为什么吗?)
我认为 aMono<Void>
总是完成空(或错误),因此永远不会调用后续的 flatMap 。
- 使用 CompletableFuture 和回调。
- 在 SOAP 客户端中使方法变得惰性。
要使呼叫变得懒惰,您可以执行以下操作之一:
1、您可以使用Mono.fromFuture
哪个接受供应商:
private Mono<Foo> doGetFoo(GetFooRequest request) {
return Mono.fromFuture(() -> {
val handler = new AsyncHandler<GetFooRequest>();
client.getFoo(request, handler);
return handler.future;
});
}
2,您可以使用Mono.defer
:
private Mono<Foo> doGetFoo(GetFooRequest request) {
return Mono.defer(() -> {
val handler = new AsyncHandler<GetFooRequest>();
client.getFoo(request, handler);
return Mono.fromFuture(handler.future);
});
}
3,您可以摆脱 CompletableFuture 并Mono.create
改用,如下所示:
private Mono<Foo> doGetFoo(GetFooRequest request) {
return Mono.create(sink -> {
AsyncHandler<Foo> handler = response ->
{
try
{
sink.success(response.get());
} catch (Exception e)
{
sink.error(e);
}
};
client.getFoo(request, handler);
});
}
如果您执行其中任何一项操作,那么使用then
方法将是安全的,并且它将按预期工作。
推荐阅读
- php - PHP 语法,是 $this->(stuff) ->(more stuff) 和 $this->(stuff); 一样 $this->(更多东西)?
- javascript - 不要使用 React-Select 清除选择时的输入
- java - spring cache - 如果更新逻辑失败,如何恢复到原始缓存?- @cacheable @cacheput @cacheevict
- javascript - javascript中通用模板文字函数的标记
- python - 如何从django中的子对象获取父对象
- javascript - 打字稿这个表达式是不可构造的。继承类结构错误
- c# - C# EnterpriseLibrary 获取输出参数时遇到问题
- c# - 安卓模拟器上的状态栏颜色没有变化吗?
- node.js - Express Handlebars 部分不做任何事情
- reactjs - Jumbotron 中的按钮链接到 React 导航栏中的按钮