Java 反应器中基于条件检查的异步顺序调用

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

在这里,我尝试使用reactor进行异步和非阻塞调用,对于每个请求,我可能必须按顺序调用两个服务(在下面的示例中,
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");
  });
}