Search code examples
multithreadingspring-bootresttemplate

Multiple REST calls timing out in Spring Boot web application


I created a Spring Boot (1.4.2) REST application. One of the @RestController methods needs to invoke a 3rd party API REST operation (RestOp1) which returns, say between 100-250 records. For each of those records returned by RestOp1, within the same method, another REST operation of the same 3rd party API (RestOp2) must be invoked. My first attempt involved using a Controller class level ExecutorService based on a Fixed Thread Pool of size 100, and a Callable returning a record corresponding to the response of RestOp2:

// Executor thread pool - declared and initialized at class level
ExecutorService executor = Executors.newFixedThreadPool(100);

// Get records from RestOp1
ResponseEntity<RestOp1ResObj[]> restOp1ResObjList
  = this.restTemplate.exchange(url1, HttpMethod.GET, httpEntity, RestOp1ResObj[].class);
RestOp1ResObj[] records = restOp1ResObjList.getBody();

// Instantiate a list of futures (to call RestOp2 for each record)
List<Future<RestOp2ResObj>> futureList = new ArrayList<>();

// Iterate through the array of records and call RestOp2 in a concurrent manner, using Callables.
for (int count=0; count<records.length; count++) {

  Future<RestOp2ResObj> future = this.executorService.submit(new Callable<RestOp2ResObj>() {

    @Override
    public RestOp2ResObj call() throws Exception {
      return this.restTemplate.exchange(url2, HttpMethod.GET, httpEntity, RestOp2Obj.class);
    }
  };

  futureList.add(future);
});

// Iterate list of futures and fetch response from RestOp2 for each
// record. Build a final response and send back to the client.
for (int count=0; count<futureList.size(); count++) {

  RestOp2ResObj response = futureList.get(count).get();
  // use above response to build a final response for all the records.
}

The performance of the above code is abysmal to say the least. The response time for a RestOp1 call (invoked only once) is around 2.5 seconds and that for a RestOp2 call (invoked for each record) is about 1.5 seconds. But the code execution time is between 20-30 seconds, as opposed to an expected range of 5-6 seconds! Am I missing something fundamental here?


Solution

  • Is the service you are calling fast enough to handle that many requests per second?

    There is an async version of RestService is available called AsyncRestService. Why are you not using that?

    I would probably go like this:

        AsyncRestTemplate asyncRestTemplate = new AsyncRestTemplate(new ConcurrentTaskExecutor(Executors.newFixedThreadPool(100)));
    
        asyncRestTemplate.exchange("http://www.example.com/myurl", HttpMethod.GET, new HttpEntity<>("message"), String.class)
                .addCallback(new ListenableFutureCallback<ResponseEntity<String>>() {
                    @Override
                    public void onSuccess(ResponseEntity<String> result) {
                        //TODO: Add real response handling
    
                        System.out.println(result);
                    }
    
                    @Override
                    public void onFailure(Throwable ex) {
                        //TODO: Add real logging solution
    
                        ex.printStackTrace();
                    }
                });