环境:springboot2.3.9.RELEASE
经常会遇到在项目中调用第三方接口的情景,你是如何调用的呢?同步?异步?
场景:
假设下单业务流程如下步骤:
1、查询用户信息。
2、查询库存信息。
3、查询活动信息(折扣)。
1.同步顺序调用
publicbooleancreateOrder(){
longstart=System.currentTimeMillis();
StringuserResult=restTemplate.getForObject("http://localhost:8080/users/{1}",String.class,newObject[]{1});
StringstorageResult=restTemplate.getForObject("http://localhost:8080/storage/{1}",String.class,newObject[]{1});
StringdiscountResult=restTemplate.getForObject("http://localhost:8080/discount/{1}",String.class,newObject[]{1});
//这里合并请求结果处理
System.out.println(Arrays.toString(newString[]{userResult,storageResult,discountResult}));
System.out.println("传统方式耗时:"+(System.currentTimeMillis()-start)+"毫秒");
returntrue;
}
@GetMapping("/create")
publicObjectcreate(){
returnos.createOrder();
}
调用结果:
接口一个一个调用,非常耗时。
2.多线程(Callable+Future)
publicbooleancreateOrder2(){
longstart=System.currentTimeMillis();
Callable<String>userCallable=()->{
returnrestTemplate.getForObject("http://localhost:8080/users/{1}",String.class,newObject[]{1});
};
Callable<String>storageCallable=()->{
returnrestTemplate.getForObject("http://localhost:8080/storage/{1}",String.class,newObject[]{1});
};
Callable<String>discountCallable=()->{
returnrestTemplate.getForObject("http://localhost:8080/discount/{1}",String.class,newObject[]{1});
};
FutureTask<String>userTask=newFutureTask<>(userCallable);
FutureTask<String>storageTask=newFutureTask<>(storageCallable);
FutureTask<String>discountTask=newFutureTask<>(discountCallable);
newThread(userTask).start();
newThread(storageTask).start();
newThread(discountTask).start();
try{
StringuserResult=userTask.get();
StringstorageResult=storageTask.get();
StringdiscountResult=discountTask.get();
//这里合并请求结果处理
System.out.println(Arrays.toString(newString[]{userResult,storageResult,discountResult}));
}catch(InterruptedException|ExecutionExceptione){
e.printStackTrace();
}
System.out.println("多线程方式耗时:"+(System.currentTimeMillis()-start)+"毫秒");
returntrue;
}
调用结果:
这次耗时少了,性能明显提升了。但在项目中我们一般是禁止直接创建线程的,如果这是个高并发的接口,那么我们的程序很可能出现OOM的错误。
3.线程池(Callable+Future)防止内存溢出风险
ThreadPoolExecutorpool=newThreadPoolExecutor(5,5,60,TimeUnit.SECONDS,newArrayBlockingQueue<Runnable>(1000));
publicbooleancreateOrder3(){
longstart=System.currentTimeMillis();
List<Future<String>>results=newArrayList<>(3);
results.add(pool.submit(()->{
returnrestTemplate.getForObject("http://localhost:8080/users/{1}",String.class,newObject[]{1});
}));
results.add(pool.submit(()->{
returnrestTemplate.getForObject("http://localhost:8080/storage/{1}",String.class,newObject[]{1});
}));
results.add(pool.submit(()->{
returnrestTemplate.getForObject("http://localhost:8080/discount/{1}",String.class,newObject[]{1});
}));
for(inti=0,size=results.size();i<size;i++){
try{
System.out.println(results.get(i).get());
}catch(InterruptedException|ExecutionExceptione){
e.printStackTrace();
}
}
System.out.println("线程池方式耗时:"+(System.currentTimeMillis()-start)+"毫秒");
returntrue;
}
调用结果:
耗时和上一个基本一致,通过Future的方式有一个问题就是只能一个一个的取值,只有当前的返回数据了后才会继续往下执行。如果有其它的任务执行完,那没有轮到它也必须等待。
4.CompletionService(异步任务与使用已完成任务的结果分离),submit提交任务,take获取已经完成的任务,不用按照submit的顺序获取结果。
publicbooleancreateOrder4(){
longstart=System.currentTimeMillis();
CompletionService<String>cs=newExecutorCompletionService<>(pool);
cs.submit(()->{
returnrestTemplate.getForObject("http://localhost:8080/users/{1}",String.class,newObject[]{1});
});
cs.submit(()->{
returnrestTemplate.getForObject("http://localhost:8080/storage/{1}",String.class,newObject[]{1});
});
cs.submit(()->{
returnrestTemplate.getForObject("http://localhost:8080/discount/{1}",String.class,newObject[]{1});
});
for(inti=2;i>=0;i–){
try{
System.out.println(cs.take().get());
}catch(InterruptedException|ExecutionExceptione){
e.printStackTrace();
}
}
System.out.println("CompletionService方式耗时:"+(System.currentTimeMillis()-start)+"毫秒");
returntrue;
}
调用结果:
通过CompletionService方式不管任务添加的顺序是什么,只要通过take方法就能获取执行完的结果,如果没有任务执行完,take方法会阻塞。
5.CompletableFuture(异步任务编排),JDK1.8
publicbooleancreateOrder5(){
longstart=System.currentTimeMillis();
CompletableFuture<String>userFuture=CompletableFuture.supplyAsync(()->{
returnrestTemplate.getForObject("http://localhost:8080/users/{1}",String.class,newObject[]{1});
});
CompletableFuture<String>storageFuture=CompletableFuture.supplyAsync(()->{
returnrestTemplate.getForObject("http://localhost:8080/storage/{1}",String.class,newObject[]{1});
});
CompletableFuture<String>discountFuture=CompletableFuture.supplyAsync(()->{
returnrestTemplate.getForObject("http://localhost:8080/discount/{1}",String.class,newObject[]{1});
});
CompletableFuture<List<String>>result=CompletableFuture
.allOf(userFuture,storageFuture,discountFuture)
.thenApply((Void)->{
List<String>datas=newArrayList<>();
try{
datas.add(userFuture.get());
datas.add(storageFuture.get());
datas.add(discountFuture.get());
}catch(InterruptedException|ExecutionExceptione){
e.printStackTrace();
}
returndatas;
}).exceptionally(e->{
e.printStackTrace();
returnnull;
});
try{
System.out.println(result.get());
}catch(InterruptedException|ExecutionExceptione1){
e1.printStackTrace();
}
System.out.println("CompletableFuture方式耗时:"+(System.currentTimeMillis()-start)+"毫秒");
returntrue;
}
调用结果:
CompletableFuture提供了非常强大的异步编程方法,可同步,可异步,可编排任务执行,异步通过回调的方式执行。该对象很多的一些方法与前端JavaScript中的Promise对象有点相像。
原文地址:https://www.toutiao.com/i6940533670621217316/