java - 如何解决在 Spring Webflux 中将一个数据放入单声道到另一个单声道的问题?
问题描述
我想通过 webclient 对象从另一台服务器获取字符串数据,并将其放入另一个 Mono 对象。但在 webclient 中,只能在.subscribe()
.
因为responseBody.subscribe()
方法是异步的,所以方法test()
将在执行前返回result
空message
字段的对象responseBody.subscribe()
。
当然,我知道如果我返回responseBody
对象而不是result
对象,是没有问题的。但我想返回的不是responseBody
对象,而是result
具有非空字段的对象message
。
我想在responseBody
'ssubscribe()
完成后返回结果。如何更改我的代码?请帮我。
public Mono<ResultVO> test() {
Mono<ResultVO> result = Mono.just(new ResultVO());
WebClient client = webClientBuilder.baseUrl("http://XXXXXX").build();
Mono<String> responseBody = client.get().uri("/aaaa/bbbbb").retrieve().bodyToMono(String.class);
responseBody.subscribe( s -> {
result.subscribe(g -> g.setMessage(s));
});
return result;
}
...
@Data
public class ResultVO {
private long timestamp;
private String ip;
private String message;
...
}
我期待这样
{
"timestamp": 1566662695203,
"ip": "192.168.1.1",
"message": "c0db76f6-4eb5-4f84-be8d-018d53b453bb"
}
但结果数据是,
{
"timestamp": 1566662695203,
"ip": "192.168.1.1",
"message": ""
}
解决方案
不建议将这种逻辑放入 subscribe 方法中,这很容易导致“回调地狱”并最终导致代码无法维护。此外,我没有看到共享test
方法的调用者,但很可能其中一个 Monos 被订阅了两次,这也导致了相当混乱的行为。
相反,要组合 Monos,您可以使用 zip、zipWith、flatMap 和其他几个运算符。
一种使用 zipWith 方法的解决方案:
public Mono<ResultVO> test()
{
WebClient client = WebClient.builder().baseUrl("http://XXXXXX").build();
// dummy representation of another data source (db query, web service call...)
Mono<ResultVO> result = Mono.just(new ResultVO());
Mono<String> responseBody = client.get().uri("/aaaa/bbbbb").retrieve().bodyToMono(String.class);
return result.zipWith(responseBody,
(resultObj, body) -> new ResultVO(resultObj.getTimestamp(), resultObj.getIp(), body));
}
其他几点注意事项:
- 如果您通过响应式 WebFlux 应用程序的 REST 端点返回 JSON,那么您永远不需要手动订阅,Spring 会为您完成
- 避免使用可变对象(使用 setter 创建后修改的对象),而是创建新对象,这将使您的代码更容易推理并且不易出现并发问题
- 有用的阅读可用的 Reactor 操作符