- 作者:老汪软件技巧
- 发表时间:2024-09-20 10:01
- 浏览量:
发票信息的数据组合主要思想
把发票的每一块数据分别进行异步查询,最终阻塞等待完成所有模块的查询后,进行数据的最终组装。
使用注意
一定要使用get()方法来阻塞所有异步操作,否则数据会直接返回null。因为CompletableFuture是异步执行的,所以thenRun里面的代码在方法返回时可能还没有执行,导致goodsOrderInvoiceNewVO的属性没有被正确设置。
代码展示同步写法的代码
public GoodsOrderInvoiceNewVO getInvoiceByOrderCode(String orderCode) {
GoodsOrderInvoiceNewVO goodsOrderInvoiceNewVO = new GoodsOrderInvoiceNewVO();
// 1.直接调用方法查询发票的基本信息
GoodsOrderInvoiceNewVO invoiceInfo = goodsOrderInvoiceMapper.getInvoiceByOrderCode(orderCode);
// 2.直接调用方法获取发票资料
List invoiceHistoryList = getInvoiceHistoryByOrderCode(orderCode);
// 3.直接调用方法获取操作信息
List invoiceLifecycleList = getInvoiceLifecycleByOrderCode(orderCode);
// 4.组合数据
if (invoiceInfo != null) {
BeanUtils.copyProperties(invoiceInfo, goodsOrderInvoiceNewVO);
}
goodsOrderInvoiceNewVO.setOrderInvoiceHistoryDTOList(invoiceHistoryList);
goodsOrderInvoiceNewVO.setOrderInvoiceLifecycleList(invoiceLifecycleList);
return goodsOrderInvoiceNewVO;
}
异步写法的代码
/**
* 根据订单编号获取发票信息(异步写法)
*
* @param orderCode
* @return
*/
@Override
public GoodsOrderInvoiceNewVO getInvoiceByOrderCodeAsync(String orderCode) {
GoodsOrderInvoiceNewVO goodsOrderInvoiceNewVO = new GoodsOrderInvoiceNewVO();
// 打算采用异步组合式写法 CompletableFuture
CompletableFuture future1 = CompletableFuture.supplyAsync(() -> {
//1.异步 先查询发票的基本信息(开票资料、订单资料)
return goodsOrderInvoiceMapper.getInvoiceByOrderCode(orderCode);
});
// 2.异步调用获取发票资料
CompletableFuture> future2 = CompletableFuture.supplyAsync(
() -> getInvoiceHistoryByOrderCode(orderCode));
// 3.异步调用获取操作信息
CompletableFuture> future3 = CompletableFuture.supplyAsync(
() -> getInvoiceLifecycleByOrderCode(orderCode));
//4. 组合数据返回
CompletableFuture allFutures = CompletableFuture.allOf(future1, future2, future3);
allFutures.thenRun(() -> {
try {
GoodsOrderInvoiceNewVO goodsOrderInvoiceNewVOTemp = future1.get();
List invoiceHistoryDTOList = future2.get();
List invoiceLifecycleList = future3.get();
BeanUtils.copyProperties(goodsOrderInvoiceNewVOTemp, goodsOrderInvoiceNewVO);
goodsOrderInvoiceNewVO.setOrderInvoiceHistoryDTOList(invoiceHistoryDTOList);
goodsOrderInvoiceNewVO.setOrderInvoiceLifecycleList(invoiceLifecycleList);
} catch (InterruptedException | ExecutionException e) {
log.error("组合用户发票信息异常");
throw new RuntimeException(e);
}
});
// 等待所有的future完成
try {
allFutures.get();
} catch (InterruptedException | ExecutionException e) {
log.error("等待异步任务完成时发生异常", e);
throw new RuntimeException(e);
}
return goodsOrderInvoiceNewVO;
}
效率提升
同步情况下rt在(70+,110+)的范围内,异步的情况下RT在(50,70+)之间,平均提升效率40%以上
组装商品详情页数据主要思想
将串行化处理转变化并行化处理,提升数据的加载性能
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
public class ProductDetailLoader {
0.假设的类,需要你根据实际情况定义
class ProductDetail {
// 产品详情字段
}
class InventoryStatus {
// 库存状态字段
}
class UserReview {
// 用户评论字段
}
1.声明所有的子调用方法
// 假设的方法,这些通常会在服务层中实现
CompletableFuture getProductDetails(String productId) {
// 异步获取产品详细信息
return CompletableFuture.supplyAsync(() -> {
// 模拟远程服务调用
return new ProductDetail(productId, "Product Description", "Product Category");
});
}
CompletableFuture getInventoryStatus(String productId) {
// 异步获取库存状态
return CompletableFuture.supplyAsync(() -> {
// 模拟远程服务调用
return new InventoryStatus(productId, true); // 假设库存可用
});
}
CompletableFuture<Double> getPrice(String productId) {
// 异步获取价格
return CompletableFuture.supplyAsync(() -> {
// 模拟远程服务调用
return 99.99; // 假设价格
});
}
CompletableFuture> getUserReviews(String productId) {
// 异步获取用户评论
return CompletableFuture.supplyAsync(() -> {
// 模拟远程服务调用
return Arrays.asList(new UserReview(productId, "Review 1", 5),
new UserReview(productId, "Review 2", 4));
});
}
2.组合所有的子调用方法
// 加载产品详情页面的方法
public void loadProductDetailsPage(String productId) throws ExecutionException, InterruptedException {
CompletableFuture productDetailsFuture = getProductDetails(productId);
CompletableFuture inventoryStatusFuture = getInventoryStatus(productId);
CompletableFuture<Double> priceFuture = getPrice(productId);
CompletableFuture> reviewsFuture = getUserReviews(productId);
// 等待所有 CompletableFuture 完成
CompletableFuture<Void> allFutures = CompletableFuture.allOf(
productDetailsFuture,
inventoryStatusFuture,
priceFuture,
reviewsFuture
);
// 当所有的 CompletableFuture 完成后执行
allFutures.thenRun(() -> {
try {
// 获取并使用结果
ProductDetail productDetails = productDetailsFuture.get();
InventoryStatus inventoryStatus = inventoryStatusFuture.get();
Double price = priceFuture.get();
List reviews = reviewsFuture.get();
3. 在这里,你可以组合并显示这些信息
// 例如,发送给前端或处理其他业务逻辑
displayProductPage(productDetails, inventoryStatus, price, reviews);
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
}
}).get(); // 阻塞等待所有任务完成
}
// 假设的显示方法
void displayProductPage(ProductDetail productDetails, InventoryStatus inventoryStatus, Double price, List reviews) {
// 实现展示逻辑
System.out.println("Product Details: " + productDetails);
System.out.println("Inventory Status: " + inventoryStatus);
System.out.println("Price: " + price);
System.out.println("User Reviews: " + reviews);
}
// 示例使用
public static void main(String[] args) throws ExecutionException, InterruptedException {
new ProductDetailLoader().loadProductDetailsPage("product123");
}
}
使用注意异常处理
如果不做特殊的处理,在CompletableFuture 的调用链中出现的错误,会一直保留到链路调用结束才会最后抛出异常,导致整个调用都无法继续执行thenAccept, thenApply的后续执行方法;且也导致中间链路被多余调用,浪费了资源。
可以使用exceptionally,来在调用的链路中间提前处理异常。示例如下:
import java.util.concurrent.CompletableFuture;
public class CompletableFutureExceptionHandlingIntermediate {
public static void main(String[] args) {
CompletableFuture future = CompletableFuture.supplyAsync(() -> {
// 模拟一个抛出异常的操作
throw new RuntimeException("任务失败了");
});
CompletableFuture handledFuture = future.exceptionally(ex -> {
System.err.println("任务发生异常: " + ex.getMessage());
return 0; // 处理异常并返回默认值
});
handledFuture.thenAccept(result -> {
System.out.println("处理异常后的结果:" + result);
});
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
在上面的示例中,exceptionally 方法捕获了前一个阶段中的异常,并返回了一个默认值,然后继续执行后续操作。
线程池的使用
completableFuture在执行时,容易出现长时间的阻塞情况,会出现对线程资源的占用,所以要谨慎选择使用其装配的线程池,尽量不要使用默认的线程池,forkJoinPool,而是通过executer来创建自定义的线程池。