Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to use Spring WebClient to make multiple calls simultaneously?

I want to execute 3 calls simultaneously and process the results once they're all done.

I know this can be achieved using AsyncRestTemplate as it is mentioned here How to use AsyncRestTemplate to make multiple calls simultaneously?

However, AsyncRestTemplate is deprecated in favor of WebClient. I have to use Spring MVC in the project but interested if I can use a WebClient just to execute simultaneous calls. Can someone advise how this should be done properly with WebClient?

like image 435
ddzz Avatar asked May 06 '18 19:05

ddzz


Video Answer


4 Answers

Assuming a WebClient wrapper (like in reference doc):

@Service
public class MyService {

    private final WebClient webClient;

    public MyService(WebClient.Builder webClientBuilder) {
        this.webClient = webClientBuilder.baseUrl("http://example.org").build();
    }

    public Mono<Details> someRestCall(String name) {
        return this.webClient.get().url("/{name}/details", name)
                        .retrieve().bodyToMono(Details.class);
    }

}

..., you could invoke it asynchronously via:

// ... 
  @Autowired
  MyService myService
  // ...

   Mono<Details> foo = myService.someRestCall("foo");
   Mono<Details> bar = myService.someRestCall("bar");
   Mono<Details> baz = myService.someRestCall("baz");
   
   // ..and use the results (thx to: [2] & [3]!):

   // Subscribes sequentially:

   // System.out.println("=== Flux.concat(foo, bar, baz) ===");
   // Flux.concat(foo, bar, baz).subscribe(System.out::print);
    
   // System.out.println("\n=== combine the value of foo then bar then baz ===");
   // foo.concatWith(bar).concatWith(baz).subscribe(System.out::print);
  
   // ----------------------------------------------------------------------
   // Subscribe eagerly (& simultaneously):
   System.out.println("\n=== Flux.merge(foo, bar, baz) ===");
   Flux.merge(foo, bar, baz).subscribe(System.out::print);
  • Mono javadoc

  • Flux javadoc

  • Spring WebClient reference doc

  • Spring Boot WebClient reference doc

  • Projectreactor reference doc

  • Which (reactive) operator to use!

Thanks, Welcome & Kind Regards,

like image 173
xerx593 Avatar answered Sep 23 '22 08:09

xerx593


You can make HTTP calls concurrently using simple RestTemplate and ExecutorService:

RestTemplate restTemplate = new RestTemplate();
ExecutorService executorService = Executors.newCachedThreadPool();

Future<String> firstCallFuture = executorService.submit(() -> restTemplate.getForObject("http://first-call-example.com", String.class));
Future<String> secondCallFuture = executorService.submit(() -> restTemplate.getForObject("http://second-call-example.com", String.class));

String firstResponse = firstCallFuture.get();
String secondResponse = secondCallFuture.get();

executorService.shutdown();

Or

Future<String> firstCallFuture = CompletableFuture.supplyAsync(() -> restTemplate.getForObject("http://first-call-example.com", String.class));
Future<String> secondCallFuture = CompletableFuture.supplyAsync(() -> restTemplate.getForObject("http://second-call-example.com", String.class));

String firstResponse = firstCallFuture.get();
String secondResponse = secondCallFuture.get();
like image 22
Justinas Jakavonis Avatar answered Sep 22 '22 08:09

Justinas Jakavonis


You can use Spring reactive client WebClient to send parallel requests. In this example,

public Mono<UserInfo> getUserInfo(User user) {
        Mono<UserInfo> userInfoMono = getUserInfo(user.getId());
        Mono<OrgInfo> organizationInfoMono = getOrgInfo(user.getOrgId());

        return Mono.zip(userInfoMono, organizationInfoMono).map(tuple -> {
            UserInfo userInfo = tuple.getT1();
            userInfo.setOrganization(tuple.getT2());
            return userInfo;
        });
    }

Here:

  • getUserInfo makes an HTTP call to get user info from another service and returns Mono
  • getOrgInfo method makes HTTP call to get organization info from another service and returns Mono
  • Mono.zip() waits all the results from all monos and merges into a new mono and returns it.

Then, call getOrgUserInfo().block() to get the final result.

like image 26
nsv Avatar answered Sep 23 '22 08:09

nsv


Another way:

public Mono<Boolean> areVersionsOK(){
        final Mono<Boolean> isPCFVersionOK = getPCFInfo2();
        final Mono<Boolean> isBlueMixVersionOK = getBluemixInfo2();

        return isPCFVersionOK.mergeWith(isBlueMixVersionOK)
            .filter(aBoolean -> {
                return aBoolean;
            })
            .collectList().map(booleans -> {
                return booleans.size() == 2;
        });

    }
like image 31
jabrena Avatar answered Sep 24 '22 08:09

jabrena