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

时间:2018-10-26 19:11:12

标签: java spring-boot java-8 reactive-programming reactor

在这里,我试图使用反应堆进行异步和非阻塞调用,对于每个请求,我可能不得不依次调用两个服务(在我的情况下,getAccountInfoFromAAAgetAccountInfoFromBBB

这是我的ItemRequest对象:

public class ItemRequest {
    private Account account;
    private Result firstServiceResult;
    private Result secondServiceResult;
    private PostingParameterCode postingParameterCode; //enum 
    //...
    //...
    //getters and setters
}

因此,我的请求输入将包含多个itemRequest,对于每个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
}

这是我的第一个服务呼叫界面:

public Mono<ItemRequest> getAccountDataFromAAA(ItemRequest itemRequest);

这是我的第二个服务呼叫接口:

public Mono<ItemRequest> getAccountDataFromBBB(ItemRequest itemRequest);

根据情况,此方法最多可以依次调用两个:

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.
    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);
}

//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)){ 
        secondCallResult = this.secondServiceCallImpl.getAccountDataFromBBB(itemRequest);
    }

    return firstCallResult.then(secondCallResult);  //attaching the
    //firstCallResult and secondCallResult to produce a single Mono

}

当不需要firstCallResult时,此方法工作正常。但是,当需要进行首次呼叫时,此条件检查将无法通过,因为我不会更新首次呼叫结果对象:

if(null != itemRequest.getFirstServiceResult() && 
    itemRequest.getFirstServiceResult().getStatus().equals(Result.Status.OK) && 
    itemRequest.getPostingParameterCode().equals(PostingParameterCode.MOBILECREDIT))) { ... } 
 //this condition check will not pass because first service call is not actually executing

如果我发表以下声明,两种情况都可以正常工作:

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())

但是无法找出将secondService与firstService链接以获得单声道结果​​并进行条件检查的方法。 条件检查很重要,因为我并不总是希望执行第二项服务。有什么方法可以将secondService与firstService链接以获取结果并进行条件检查吗?

很长的问题的道歉。任何建议/帮助将不胜感激。

3 个答案:

答案 0 :(得分:3)

在为这个问题提供悬赏分之后,我真的很兴奋,期待得到一些答案。 但是无论如何,我能够改善最初的解决方案并进行条件检查。

我做了以下事情: 我在两个服务调用中都将返回类型从Mono<ItemRequest>更改为Mono<Void>,因为我基本上是将数据更新为ItemRequest列表:

在此处处理并行调用(每个并行调用都有一个顺序调用):

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
    }

这是我的firstServiceCallsecondServiceCall界面更改:

public Mono<Void> getAccountDataFromAAA(ItemRequest itemRequest);

public Mono<Void> getAccountDataFromBBB(ItemRequest itemRequest);

然后我将secondServiceCallfirstServiceCall链接起来以获得单声道结果​​,并进行如下条件检查:

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();
        }
    }))

答案 1 :(得分:0)

以下是一些新闻:反应堆不是灵丹妙药! :)

每当需要呼叫响应以确定是否需要做其他事情时,这将永远无法完全并行化。例如。您总是可以做最后的建议。但是,这并不意味着使用Reactor不会给您带来任何好处!

您将获得的一些好处:

  • 您正在使用Netty而不是Servlet,这有助于避免锁定I / O操作。这样可以更好地分配资源,使您的系统更具弹性。
  • 您可以在等待响应时执行其他操作。如果您要处理的订单无关紧要,可以随时将其放在此处(例如审核,日志记录等)。

我希望这能回答您的问题:)

答案 2 :(得分:0)

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;
  }
  });
}

下一个简单的示例可以帮助您理解 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");
  });
}
相关问题