多个REST调用在Spring Boot Web应用程序中超时

时间:2016-12-15 06:45:28

标签: multithreading spring-boot resttemplate

我创建了一个Spring Boot(1.4.2)REST应用程序。其中一个@RestController方法需要调用第三方API REST操作(RestOp1),该操作返回100-250条记录。对于RestOp1返回的每条记录,在同一方法中,必须调用同一第三方API(RestOp2)的另一个REST操作。我的第一次尝试涉及使用基于大小为100的固定线程池的Controller类级别ExecutorService,以及返回与RestOp2的响应相对应的Callable:

// 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.
}

至少可以说上述代码的表现非常糟糕。 RestOp1调用(仅调用一次)的响应时间约为2.5秒,而RestOp2调用(每个记录调用)的响应时间约为1.5秒。但代码执行时间在20-30秒之间,而不是预期的5-6秒范围!我在这里遗漏了一些基本的东西吗?

2 个答案:

答案 0 :(得分:1)

您呼叫的服务是否足够快以便每秒处理那么多请求?

有一个名为AsyncRestService的异步版RestService可用。你为什么不用它?

我可能会这样:

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

答案 1 :(得分:1)

您的问题涉及两个部分:

  1. 异步进行多个API回调
  2. 处理超时(后备)
  3. 这两个部分都是相关的,因为您可以处理每次通话的超时。

    您可以考虑使用Spring Cloud(基于Spring引导)并使用一些基于OSS Netflix堆栈的开箱即用解决方案。

    第一个(超时)应该是基于假装客户端的断路器hystrix

    第二个(多个请求)这是一个体系结构问题,使用本机Executors并不是一个好主意,因为它不会扩展并且具有巨大的维护成本。您可以通过Spring Asynchrounous Methods转发,您将获得更好的结果并完全符合弹簧要求。

    希望这会有所帮助。