Java 反应器中基于条件检查的异步顺序调用
在这里,我尝试使用reactor进行异步和非阻塞调用,对于每个请求,我可能必须按顺序调用两个服务(在下面的示例中,Java 反应器中基于条件检查的异步顺序调用,java,spring-boot,java-8,reactive-programming,reactor,Java,Spring Boot,Java 8,Reactive Programming,Reactor,在这里,我尝试使用reactor进行异步和非阻塞调用,对于每个请求,我可能必须按顺序调用两个服务(在下面的示例中,getAccountInfoFromAAA和getAccountInfoFromBBB) 这是我的ItemRequest对象: public class ItemRequest { private Account account; private Result firstServiceResult; private Result secondServiceRe
getAccountInfoFromAAA
和getAccountInfoFromBBB
)
这是我的ItemRequest
对象:
public class ItemRequest {
private Account account;
private Result firstServiceResult;
private Result secondServiceResult;
private PostingParameterCode postingParameterCode; //enum
//...
//...
//getters and setters
}
因此,我的请求输入将包含多个itemRequest
s,对于每个itemRequest
,我将执行异步调用,如下所示:
public void getAccountData(List<ItemRequest> itemRequests) {
ImmutableList<ItemRequest> list = ImmutableList.copyOf(itemRequests);
Flux.fromIterable(list).flatMap(this::callBothSors).blockLast();
}
public Mono<ItemRequest> callBothSors(ItemRequest itemRequest) {
return getAccountDataService.getAccountDataFromAAAandBBB(itemRequest);
//here, it will enter into a sequential call for each itemRequest
}
如果我提出以下声明,这两种情况都很好:
if(isFirstServiceCallRequired(itemRequest)){
firstCallResult = this.firstServiceCallImpl.getAccountDataFromAAA(itemRequest);
firstCallResult.block(); //adding this case will work on both cases
}
但是,我不认为我会通过这种方式获得反应堆利益。
我想有这样的逻辑:
Mono<ItemRequest> result = firstService.call(...)
.doOnNext(/*do something */)
.then( ... secondService.call())
Mono result=firstService.call(…)
.doOnNext(/*做点什么*/)
.然后(…secondService.call())
但无法找到将secondService与firstService链接以获得mono结果并进行这些条件检查的方法。
条件检查很重要,因为我并不总是想执行第二个服务。有没有办法将secondService与firstService链接起来以获得结果并进行这些条件检查
为这个冗长的问题道歉。如有任何建议/帮助,将不胜感激 以下是一些新闻:反应堆不是银弹!:) 每当您需要调用的响应来确定是否需要执行其他操作时,这将永远无法完全并行化。你总是可以做你最后的建议。然而,这并不意味着使用反应堆不会给你带来任何好处 您获得的一些好处:
- 您正在使用Netty-under-the-hood而不是Servlet,这有助于避免锁定I/O操作。这可以更好地分配资源,使您的系统更具弹性
- 您可以在等待响应时执行其他操作。如果您要做的事情与顺序无关,您可以随时将它们放在那里(例如审计、日志记录等)
我希望这能回答你的问题:)在给出了这个问题的悬赏分数后,我真的很兴奋,期待着一些答案。 但无论如何,我能够改进我的初始解决方案,并进行这些条件检查 我做了以下工作: 我在两次服务调用中都将返回类型从
Mono
更改为Mono
,因为我基本上将数据更新为ItemRequest
列表:
在此处处理并行调用(每个并行调用都有一个顺序调用):
我将
secondServiceCall
与firstServiceCall
链接起来,以获得mono结果,并进行以下条件检查:
public Mono<Void> getAccountDataFromAAAandBBB(ItemRequest itemRequest){
Mono<Void> callSequence = Mono.empty();
if(isFirstServiceCallRequired(itemRequest)){
callSequence = this.firstServiceCallImpl.getAccountDataFromAAA(itemRequest);
} else {
//Account key is already present, so just update the result status which I need later.
Result result = new Result();
result.setStatus(Result.Status.OK);
result.setMessageText("First call not required as account info is set for item request");
itemRequest.setFirstServiceResult(result);
}
return callSequence.thenEmpty(Mono.defer(() -> {
//note: Mono.defer ==>> Create a Mono provider that will supply a target Mono to subscribe to
//for each subscriber downstream.
//only if the firstServiceCall result is successful & other condition check successful,
// I am calling secondServiceCall:
if(shouldCallSecondService(itemRequest)){
return this.secondServiceCallImpl.getAccountDataFromAAAandBBB(itemRequest);
} else {
return Mono.empty();
}
}))
公共Mono GetAccountDataFromaaAndBBB(ItemRequest ItemRequest){
Mono callSequence=Mono.empty();
如果(isFirstServiceCallRequired(itemRequest)){
callSequence=this.firstServiceCallImpl.getAccountDataFromAAA(itemRequest);
}否则{
//帐户密钥已存在,因此只需更新我稍后需要的结果状态。
结果=新结果();
result.setStatus(result.Status.OK);
result.setMessageText(“第一次调用不需要,因为为项目请求设置了帐户信息”);
itemRequest.setFirstServiceResult(结果);
}
返回callSequence.thenEmpty(Mono.defer(()->{
//注意:Mono.defer==>>创建一个Mono提供程序,该提供程序将提供要订阅的目标Mono
//对于每个下游用户。
//仅当firstServiceCall结果成功且其他条件检查成功时,
//我正在呼叫第二服务呼叫:
if(shouldCallSecondService(itemRequest)){
返回此.secondServiceCallImpl.GetAccountDataFromaaAndBBB(itemRequest);
}否则{
返回Mono.empty();
}
}))
公共Mono GetAccountDataFromaaAndBBB(ItemRequest ItemRequest){
Mono firstCallResult=Mono.empty();
Mono secondCallResult=Mono.empty();
如果(isFirstServiceCallRequired(itemRequest)){
firstCallResult=this.firstServiceCallImpl.getAccountDataFromAAA(itemRequest);
//基本上,firstService调用将更新accountKey信息和
//还将结果状态设置为确定所需的OK
//是否拨打第二个服务电话。
}否则{
/*帐户密钥已经存在,所以只需更新我需要的结果状态
稍后*/
firstCallResult=Mono.defer(()->{
结果=新结果();
result.setStatus(result.Status.OK);
result.setMessageText(“第一次调用不需要,因为为项目请求设置了帐户信息”);
itemRequest.setFirstServiceResult(结果);
返回Mono.just(itemRequest);
});
}
返回firstCallResult.flatMap(itReq->{
//现在,在调用第二个服务之前,我需要检查以下内容:
if(null!=itemRequest.getFirstServiceResult()&&
itemRequest.getFirstServiceResult().getStatus().equals(Result.Status.OK)&&
itemRequest.getPostingParameterCode().equals(PostingParameterCode.MOBILECREDIT)){
return secondCallResult=this.secondServiceCallImpl.getAccountDataFromBBB(itemRequest);
}否则{
返回itReq;
}
});
}
下一个简单示例可以帮助您理解flatMap:
public static void main(String[] args) {
callExternalServiceA.flatMap(response -> {
if(response.equals("200")){
return Mono.just(response);
} else {
return callExtertnalServiceB();
}
}).block();
}
public static Mono<String> callExtertnalServiceA() {
return Mono.defer(() -> {
System.out.println("Call external service A");
return Mono.just("400");
});
}
public static Mono<String> callExtertnalServiceB() {
return Mono.defer(() -> {
System.out.println("Call external service B");
return Mono.just("200");
});
}
publicstaticvoidmain(字符串[]args){
CallExternalService.flatMap(响应->{
如果(响应等于(“200”)){
返回Mono.just(响应);
}否则{
返回callExtertnalServiceB();
}
}).block();
}
公共静态Mono CallerTextnalServiceA(){
返回Mono.defer(()->{
System.out.println(“调用外部服务A”);
返回Mono.just(“400”);
});
}
公共静态Mono CallerTextnalServiceB(){
返回Mono.defer(()->{
System.out.println(“呼叫外部服务B”);
返回Mono.just(“200”);
});
}
感谢Sofo提供您的意见,并强调了其优点。:)
if(isFirstServiceCallRequired(itemRequest)){
firstCallResult = this.firstServiceCallImpl.getAccountDataFromAAA(itemRequest);
firstCallResult.block(); //adding this case will work on both cases
}
Mono<ItemRequest> result = firstService.call(...)
.doOnNext(/*do something */)
.then( ... secondService.call())
public void getAccountData(List<ItemRequest> itemRequests) {
ImmutableList<ItemRequest> list = ImmutableList.copyOf(itemRequests);
Flux.fromIterable(list).flatMap(this::callBothSors).blockLast();
}
public Mono<Void> callBothSors(ItemRequest itemRequest) {
return getAccountDataService.getAccountDataFromAAAandBBB(itemRequest);
//here, it will enter into a sequential call for each itemRequest
}
public Mono<Void> getAccountDataFromAAA(ItemRequest itemRequest);
public Mono<Void> getAccountDataFromBBB(ItemRequest itemRequest);
public Mono<Void> getAccountDataFromAAAandBBB(ItemRequest itemRequest){
Mono<Void> callSequence = Mono.empty();
if(isFirstServiceCallRequired(itemRequest)){
callSequence = this.firstServiceCallImpl.getAccountDataFromAAA(itemRequest);
} else {
//Account key is already present, so just update the result status which I need later.
Result result = new Result();
result.setStatus(Result.Status.OK);
result.setMessageText("First call not required as account info is set for item request");
itemRequest.setFirstServiceResult(result);
}
return callSequence.thenEmpty(Mono.defer(() -> {
//note: Mono.defer ==>> Create a Mono provider that will supply a target Mono to subscribe to
//for each subscriber downstream.
//only if the firstServiceCall result is successful & other condition check successful,
// I am calling secondServiceCall:
if(shouldCallSecondService(itemRequest)){
return this.secondServiceCallImpl.getAccountDataFromAAAandBBB(itemRequest);
} else {
return Mono.empty();
}
}))
public Mono<ItemRequest> getAccountDataFromAAAandBBB(ItemRequest itemRequest) {
Mono<ItemRequest> firstCallResult = Mono.empty();
Mono<ItemRequest> secondCallResult = Mono.empty();
if (isFirstServiceCallRequired(itemRequest)) {
firstCallResult = this.firstServiceCallImpl.getAccountDataFromAAA(itemRequest);
//basically, firstService call will update the accountKey information and
//will also set the result status to OK which is required to decide
//whether to make secondService call.
} else {
/*Account key is already present, so just update the result status which I need
later.*/
firstCallResult = Mono.defer(() -> {
Result result = new Result();
result.setStatus(Result.Status.OK);
result.setMessageText("First call not required as account info is set for item request");
itemRequest.setFirstServiceResult(result);
return Mono.just(itemRequest);
});
}
return firstCallResult.flatMap(itReq -> {
//Now, before calling the second service, I need to check the following:
if (null != itemRequest.getFirstServiceResult() &&
itemRequest.getFirstServiceResult().getStatus().equals(Result.Status.OK) &&
itemRequest.getPostingParameterCode().equals(PostingParameterCode.MOBILECREDIT)) {
return secondCallResult = this.secondServiceCallImpl.getAccountDataFromBBB(itemRequest);
} else {
return itReq;
}
});
}
public static void main(String[] args) {
callExternalServiceA.flatMap(response -> {
if(response.equals("200")){
return Mono.just(response);
} else {
return callExtertnalServiceB();
}
}).block();
}
public static Mono<String> callExtertnalServiceA() {
return Mono.defer(() -> {
System.out.println("Call external service A");
return Mono.just("400");
});
}
public static Mono<String> callExtertnalServiceB() {
return Mono.defer(() -> {
System.out.println("Call external service B");
return Mono.just("200");
});
}