呼和浩特企业网站排名优化,怎样建个人网站 步骤,thinkphp做网站,百度网站邀您点评背景说明
在实际的软件开发中#xff0c;我们经常会遇到需要批量调用接口的场景。例如#xff0c;电商系统在生成商品详情页时#xff0c;需要同时调用多个服务接口来获取商品的基本信息、库存信息、价格信息、用户评价等。
传统的依次调用方式存在性能问题
面对上述场景…背景说明
在实际的软件开发中我们经常会遇到需要批量调用接口的场景。例如电商系统在生成商品详情页时需要同时调用多个服务接口来获取商品的基本信息、库存信息、价格信息、用户评价等。
传统的依次调用方式存在性能问题
面对上述场景传统的做法是依次调用这些接口等待每个接口返回结果后再进行下一步操作。
面对这种方式会导致整体性能低下因为每个接口调用都需要等待上一个接口调用完成假设x方法内部要调用a、b、c、d四个接口那么x方法执行的耗时a耗时b耗时c耗时d耗时这样消耗的时间会比较长。
采用批量调用的方式进行优化
可以注意到这些接口调用之间可能并没有严格的先后顺序完全可以并行执行我们可以采用CompletableFuture类来实现接口调用的并行执行。
CompletableFuture介绍
CompletableFuture 是 Java 8 引入的一个强大的异步编程工具它实现了 Future 和 CompletionStage 接口提供了丰富的方法来处理异步任务的完成、组合和异常处理。
在批量调用接口的场景中CompletableFuture 的主要原理如下
异步执行CompletableFuture.supplyAsync() 方法可以将一个任务提交到线程池中异步执行而不会阻塞当前线程。在上述代码中每个接口调用都被封装成一个 CompletableFuture 对象并通过 supplyAsync() 方法异步执行。
并行处理由于每个接口调用都是异步执行的它们可以在不同的线程中并行处理从而充分利用多核 CPU 的性能减少整体的执行时间。
组合操作CompletableFuture.allOf() 方法可以将多个 CompletableFuture 对象组合成一个新的 CompletableFuture 对象该对象在所有子任务都完成后才会完成。通过这种方式我们可以等待所有接口调用都完成后再进行后续的处理。
结果获取CompletableFuture.join() 方法用于获取异步任务的结果如果任务还未完成该方法会阻塞当前线程直到任务完成。在上述代码中我们使用 join() 方法获取每个接口调用的结果并将它们收集到一个列表中。
优化实践
首先我们模拟一个接口调用的服务类命名为InfoServiceFeignMock用于模拟调用接口的场景。
package org.example.Scene;/*** Author xu* Version 1.0* Description 模拟接口调用**/
public class InfoServiceFeignMock {/*** 模拟调用获取商品基本信息的接口* param productId 商品 ID* return 商品基本信息*/public String getProductBasicInfo(String productId) {try {// 模拟接口调用耗时例如网络延迟等Thread.sleep(200);} catch (InterruptedException e) {e.printStackTrace();}return Basic info for product productId;}/*** 模拟调用获取商品库存信息的接口* param productId 商品 ID* return 商品库存信息*/public String getProductInventoryInfo(String productId) {try {// 模拟接口调用耗时Thread.sleep(200);} catch (InterruptedException e) {e.printStackTrace();}return Inventory info for product productId;}/*** 模拟调用获取商品价格信息的接口* param productId 商品 ID* return 商品价格信息*/public String getProductPriceInfo(String productId) {try {// 模拟接口调用耗时Thread.sleep(200);} catch (InterruptedException e) {e.printStackTrace();}return Price info for product productId;}/*** 模拟调用获取商品用户评价信息的接口* param productId 商品 ID* return 商品用户评价信息*/public String getProductReviewInfo(String productId) {try {// 模拟接口调用耗时Thread.sleep(200);} catch (InterruptedException e) {e.printStackTrace();}return Review info for product productId;}}
接下来我们再新建一个类叫做SceneMock用来比对原始顺序调用和使用CompletableFuture批量调用情况下的耗时情况。
package org.example.Scene;import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.stream.Collectors;/*** Author xu* Version 1.0* Description 模拟接口调用的场景**/
public class SceneMock {/*** 程序的入口点* 本方法演示了两种处理产品ID的方法* param args 命令行参数本示例中未使用*/public static void main(String[] args) {// 定义一个产品ID用于后续的方法调用和处理String productId 12345;// 调用默认方法处理产品IDdefaultMethod(productId);// 调用改进方法处理产品IDbetterMethod(productId);}/*** 默认方法用于演示如何调用信息服务获取产品相关信息* 该方法将模拟通过Feign客户端调用远程服务来获取产品的基本信息、库存信息、价格信息和评论信息** param productId 产品ID用于查询产品信息*/public static void defaultMethod(String productId){// 创建模拟接口调用的实例InfoServiceFeignMock infoService new InfoServiceFeignMock();// 记录开始时间long startTime System.currentTimeMillis();// 初始化结果列表用于存储从各服务获取的信息ListString results new ArrayList();// 调用模拟的服务获取产品基本信息并添加到结果列表results.add(infoService.getProductBasicInfo(productId));// 调用模拟的服务获取产品库存信息并添加到结果列表results.add(infoService.getProductInventoryInfo(productId));// 调用模拟的服务获取产品价格信息并添加到结果列表results.add(infoService.getProductPriceInfo(productId));// 调用模拟的服务获取产品评论信息并添加到结果列表results.add(infoService.getProductReviewInfo(productId));// 记录结束时间long endTime System.currentTimeMillis();// 输出结果System.out.println(All results: results);// 输出总耗时System.out.println(defaultMethod time cost: (endTime - startTime) ms);}/*** 异步调用产品信息的方法* 该方法通过异步调用模拟获取产品的基本信息、库存信息、价格信息和评论信息* 使用 CompletableFuture 来并行处理多个异步任务并收集结果** param productId 产品ID用于查询产品信息*/public static void betterMethod(String productId){// 创建模拟接口调用的实例InfoServiceFeignMock infoService new InfoServiceFeignMock();// 记录开始时间long startTime System.currentTimeMillis();// 使用 CompletableFuture 异步调用各个接口CompletableFutureString basicInfoFuture CompletableFuture.supplyAsync(() -infoService.getProductBasicInfo(productId));CompletableFutureString inventoryInfoFuture CompletableFuture.supplyAsync(() -infoService.getProductInventoryInfo(productId));CompletableFutureString priceInfoFuture CompletableFuture.supplyAsync(() -infoService.getProductPriceInfo(productId));CompletableFutureString reviewInfoFuture CompletableFuture.supplyAsync(() -infoService.getProductReviewInfo(productId));// 将所有的 CompletableFuture 收集到一个列表中ListCompletableFutureString futures new ArrayList();futures.add(basicInfoFuture);futures.add(inventoryInfoFuture);futures.add(priceInfoFuture);futures.add(reviewInfoFuture);// 使用 allOf 方法组合所有的 CompletableFuture等待所有任务完成CompletableFutureVoid allFutures CompletableFuture.allOf(futures.toArray(new CompletableFuture[0]));// 当所有任务完成后将结果收集到一个列表中CompletableFutureListString allResults allFutures.thenApply(v -futures.stream().map(CompletableFuture::join).collect(Collectors.toList()));try {// 获取所有结果ListString results allResults.get();// 记录结束时间long endTime System.currentTimeMillis();// 输出结果System.out.println(All results: results);System.out.println(betterMethod time cost: (endTime - startTime) ms);} catch (Exception e) {// 处理异常e.printStackTrace();}}
}
我们接下来执行SceneMock类中的main方法查看执行结果。
All results: [Basic info for product 12345, Inventory info for product 12345, Price info for product 12345, Review info for product 12345]
defaultMethod time cost: 810 ms
All results: [Basic info for product 12345, Inventory info for product 12345, Price info for product 12345, Review info for product 12345]
betterMethod time cost: 253 ms可以看出使用CompletableFuture进行优化后消耗时间大幅度缩短。
扩展阅读
感兴趣的读者可以阅读下面这个链接看下美团技术团队是如何利用CompletableFuture优化外卖商家端API这个核心API的。
美团技术团队-外卖商家端API的异步化
总结
除了上述的实例实际上CompletableFuture还有更多种多样的用法比如说实现接口的多阶段批量调用等因此我们在实际使用中可以更加灵活地使用CompletableFuture进行优化。
我后续还会更新【性能优化专题系列】计划会涵盖前端、后端、网络、操作系统、数据库等一系列内容希望大家方便的话给我提供一些阅读上的感受和建议我会根据建议不断优化自己的写作方式写出更好的博客。