[英]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.