SpringBoot项目中异步调用接口方式知多少?


SpringBoot项目中异步调用接口方式知多少?

文章插图
环境:springboot2.5.12
经常会遇到在项目中调用第三方接口的情景,你是如何调用的呢?同步?异步?
场景:
假设下单业务流程如下步骤:
1、查询用户信息 。
2、查询库存信息 。
3、查询活动信息(折扣) 。
1、同步顺序调用public boolean createOrder() {long start = System.currentTimeMillis() ;String userResult = restTemplate.getForObject("http://localhost:8080/users/{1}", String.class, new Object[] {1}) ;String storageResult = restTemplate.getForObject("http://localhost:8080/storage/{1}", String.class, new Object[] {1}) ;String discountResult = restTemplate.getForObject("http://localhost:8080/discount/{1}", String.class, new Object[] {1}) ;// 这里合并请求结果处理System.out.println(Arrays.toString(new String[] {userResult, storageResult, discountResult})) ;System.out.println("传统方式耗时:" + (System.currentTimeMillis() - start) + "毫秒") ;return true ;}@GetMApping("/create")public Object create() {return os.createOrder() ;}调用结果:
SpringBoot项目中异步调用接口方式知多少?

文章插图
图片
接口一个一个调用,非常耗时 。
2、多线程(Callable+Future)public boolean createOrder2() {long start = System.currentTimeMillis() ;Callable<String> userCallable = () -> {return restTemplate.getForObject("http://localhost:8080/users/{1}", String.class, new Object[] {1}) ;} ;Callable<String> storageCallable = () -> {return restTemplate.getForObject("http://localhost:8080/storage/{1}", String.class, new Object[] {1}) ;} ;Callable<String> discountCallable = () -> {return restTemplate.getForObject("http://localhost:8080/discount/{1}", String.class, new Object[] {1}) ;} ;FutureTask<String> userTask = new FutureTask<>(userCallable) ;FutureTask<String> storageTask = new FutureTask<>(storageCallable) ;FutureTask<String> discountTask = new FutureTask<>(discountCallable) ;new Thread(userTask).start() ;new Thread(storageTask).start() ;new Thread(discountTask).start() ;try {String userResult = userTask.get() ;String storageResult = storageTask.get() ;String discountResult = discountTask.get() ;// 这里合并请求结果处理System.out.println(Arrays.toString(new String[] {userResult, storageResult, discountResult})) ;} catch (InterruptedException | ExecutionException e) {e.printStackTrace();}System.out.println("多线程方式耗时:" + (System.currentTimeMillis() - start) + "毫秒") ;return true ;}调用结果:
SpringBoot项目中异步调用接口方式知多少?

文章插图
图片
这次耗时少了,性能明显提升了 。但在项目中我们一般是禁止直接创建线程的,如果这是个高并发的接口,那么我们的程序很可能出现OOM的错误 。
3、线程池(Callable+Future)防止内存溢出风险ThreadPoolExecutor pool = new ThreadPoolExecutor(5, 5, 60, TimeUnit.SECONDS, new ArrayBlockingQueue<Runnable>(1000)) ;public boolean createOrder3() {long start = System.currentTimeMillis() ;List<Future<String>> results = new ArrayList<>(3) ;results.add(pool.submit(() -> {return restTemplate.getForObject("http://localhost:8080/users/{1}", String.class, new Object[] {1}) ;})) ;results.add(pool.submit(() -> {return restTemplate.getForObject("http://localhost:8080/storage/{1}", String.class, new Object[] {1}) ;})) ;results.add(pool.submit(() -> {return restTemplate.getForObject("http://localhost:8080/discount/{1}", String.class, new Object[] {1}) ;})) ;for (int i = 0, size = results.size(); i < size; i++) {try {System.out.println(results.get(i).get()) ;} catch (InterruptedException | ExecutionException e) {e.printStackTrace();}}System.out.println("线程池方式耗时:" + (System.currentTimeMillis() - start) + "毫秒") ;return true ;}调用结果:
SpringBoot项目中异步调用接口方式知多少?

文章插图
图片
耗时和上一个基本一致,通过Future的方式有一个问题就是只能一个一个的取值,只有当前的返回数据了后才会继续往下执行 。如果有其它的任务执行完,那没有轮到它也必须等待 。
4、CompletionService(异步任务与使用已完成任务的结果分离),submit提交任务,take获取已经完成的任务,不用按照submit的顺序获取结果 。public boolean createOrder4() {long start = System.currentTimeMillis() ;CompletionService<String> cs = new ExecutorCompletionService<>(pool) ;cs.submit(() -> {return restTemplate.getForObject("http://localhost:8080/users/{1}", String.class, new Object[] {1}) ;}) ;cs.submit(() -> {return restTemplate.getForObject("http://localhost:8080/storage/{1}", String.class, new Object[] {1}) ;}) ;cs.submit(() -> {return restTemplate.getForObject("http://localhost:8080/discount/{1}", String.class, new Object[] {1}) ;}) ;for (int i = 2 ; i >=0; i--) {try {System.out.println(cs.take().get()) ;} catch (InterruptedException | ExecutionException e) {e.printStackTrace();}}System.out.println("CompletionService方式耗时:" + (System.currentTimeMillis() - start) + "毫秒") ;return true ;}


推荐阅读