繁体   English   中英

如何同时进行API调用

[英]How to make API calls concurrently

我有大约40万个员工ID。一次可以传递1个员工ID。

如何使用线程并行调用PI,以便提高性能。 需要一些指针。

saveInDatabase()方法将对象保存在数据库表中。我已将该方法标记为“同步”。

private void callApi(List<Long> employeeList, HttpEntity<String> requestEntity) {
        Long employeeId;
        for (Long i : employeeList) {

            employeeId = i;// url

            String url = "http://dummy.restapiexample.com/api/v1/employee/" + employeeId;

            ResponseEntity<String> responseEntity = restTemplate.exchange(url, HttpMethod.GET, requestEntity,
                    String.class);

            saveInDatabase(responseEntity);

        }
    }

由于级别较低,因此直接使用Thread API容易出错。
使用parallelStream()可能很有趣,但这也可能是一个问题,因为处理后的流可能会消耗您的应用程序可用的所有CPU内核。
这意味着您的应用程序的其他HTTP客户端请求可以在最近得到响应。
还要注意, parallelStream()使用的线程数是JVM实现的详细信息,并不属于public API的一部分
允许指定池中可用线程数的ExecutorService API看起来是更好/更可靠的替代方案。

Spring Boot提供了包装它的内置功能
您可以提取要执行的单个任务,例如:

@Async
public Future<ResponseEntity<String>> getEmployee(long employeeId,  HttpEntity<String> requestEntity) {

       String url = "http://dummy.restapiexample.com/api/v1/employee/" + employeeId;
       ResponseEntity<String> responseEntity = restTemplate.exchange(url, HttpMethod.GET, requestEntity,
                String.class);
       return new AsyncResult<ResponseEntity<String>>(responseEntity);
   }

现在调用它:

private void callApi(List<Long> employeeList, HttpEntity<String> requestEntity) {

        // Do async calls and store futures in a List
        List<Future<ResponseEntity<String>>> futures = new ArrayList<>();
        for (Long id : employeeList) {
            futures.add(getEmployee(id, requestEntity));    
        }

        // Then process list of future 
        for (Future<ResponseEntity<String>> future : futures) 
            try{ 
                saveInDatabase(future.get());
               }
               catch(Exception e){ 
                  //handle the exception
              }

         }    
    }

附带说明一下,将saveInDatabase()操作放入循环中不是正确的方法。
而是要批处理数据库插入,因为您有许多工作要做。 就像是 :

private void callApi(List<Long> employeeList, HttpEntity<String> requestEntity) {

  List<ResponseEntity<String>> responseEntities = 
        employeeList.stream()
                    .map(id -> getEmploye(id))
                    .map(future -> {
                          try{return future.get();}
                            catch(Exception e){ 
                                //handle the exception
                                }
                             }
                     )
                    .collect(toList()); 
   saveInDatabase(responseEntities);            
}

为了使@Asynch功能正常工作,您必须在应用程序的@Configuration类上添加@EnableAsync
(可选)您可以使用适合您需要的池/队列配置来定义Executor bean。 注意:如果您没有定义Executor bean,Spring将创建一个SimpleAsyncTaskExecutor并使用它(它按任务创建一个Thread ,并且不要重用它们)。

例如 :

@SpringBootApplication
@EnableAsync
public class Application {

    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }

    @Bean
    public Executor taskExecutor() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        executor.setCorePoolSize(2);
        executor.setMaxPoolSize(2);
        executor.setQueueCapacity(500);
        executor.setThreadNamePrefix("GithubLookup-");
        executor.initialize();
        return executor;
    }


}

您可以使用parallelStream()并发进行API调用

List<ResponseEntity<String>> result = employeeList.parallelStream()
            .map(id->restTemplate.exchange("http://dummy.restapiexample.com/api/v1/employee/"+id, HttpMethod.GET, requestEntity, String.class))
            .collect(Collectors.toList());

result.forEach(entity->saveInDatabase(entity));

但是请注意, parallelStream()也可能会耗尽您的应用程序可用的CPU内核。 如果应用程序不仅执行此任务,而且旨在满足其他请求,则可能是一个问题。

因此,如@davidxxx所建议,使用saveAll进行批量插入

saveAll(result)

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM