Home > OS >  Scatter & Gather using Spring Webclient
Scatter & Gather using Spring Webclient

Time:10-27

I am new to reactive programming concepts and trying to build one service that sends requests to a two backend service in parallel and combine those results. Those two backend service has a different response structure and i have created a mapper method to convert all that into a common Response structure.

This is what i have right now and it is working when both the services return results.

public Mono<List<Response>> getRecords(String input){

List<Response> response = new ArrayList<>();

Mono<FirstApiResponse> gResp = this.firstWebClient.get().uri(uriBuilder -> uriBuilder
            .path("/")
            .queryParam("q", input)
            .build()).retrieve()
            .bodyToMono(FirstApiResponse.class).log()
            .timeout(Duration.ofSeconds(50L));

Mono<SecondApiResponse> iResp = this.secondWebClient.get().uri(uriBuilder -> uriBuilder
        .path("/search")
        .queryParam("term", input)
        .build()).retrieve()
        .bodyToMono(SecondApiResponse.class).log().timeout(Duration.ofSeconds(50L));


return Mono.zip(firstResp,secResp).map(objects ->{
    if(firstResp != null)
    response.addAll(Mapper.convert(objects.getT1()));
    if(secResp != null);
    response.addAll(Mapper.convert(objects.getT2()));
    return response;
});

}

public  List<Response> convert(FirstApiResponse resp){
    ////
    Mapping to Response object 
    ////

    return response;
}

public  List<Response> convert(SecondApiResponse resp){
     ////
    Mapping to Response object 
    ////

    return response;
}

I don't know if this is the right way to do it. Moreover, i want to make it in such a way that if there is any errors from any of this service, then it should still return the results from the other service. Right now it throws the exception and I am not able to figure out how to handle it properly

How to handle these errors in a proper way ?

CodePudding user response:

This is a pretty valid scenario and there are many ways to handle it. One crude way would be to use onErrorReturn a new Model which you can handle. It could be either an empty response or a wrapper around your model whichever seems fit for your scenario.

Mono<Wrapper<FirstApiResponse>> gResp = this.firstWebClient.get().uri(uriBuilder -> uriBuilder
     .path("/")
     .queryParam("q", input)
     .build()).retrieve()
     .bodyToMono(FirstApiResponse.class).log()
     .map( response -> new Wrapper().withResponse(response))
     .timeout(Duration.ofSeconds(50L))
     .doOnError(throwable -> logger.error("Failed", throwable))
      .onErrorReturn(new Wrapper().withError( YourDefaultErrorReponse(...));

Mono<SecondApiResponse> iResp = this.secondWebClient.get().uri(uriBuilder -> uriBuilder
    .path("/search")
    .queryParam("term", input)
    .build())
    .retrieve()      
    .bodyToMono(SecondApiResponse.class).log()
    .map( response -> new Wrapper().withResponse(response))
    .timeout(Duration.ofSeconds(50L))
    ..doOnError(throwable -> logger.error("Failed", throwable))
    .onErrorReturn(new Wrapper().withError( YourDefaultErrorReponse(...))

Again there are ways to return a default response. A simple one would be to use something like a wrapper

public final class Wrapper<T> {
  private T response ;
  private Error error;
      
  public Wrapper<T> withResponse ( T response ){
     this.response = response;
     return this;
  }
  public Wrapper<T> withError( Error error) {
     this.error = error;
     return this;
  }

  public Boolean hasError(){
    return error != null ;
  }
      
  public T getResponse(){
   return response;
  }
}
  • Related