• 作者:老汪软件技巧
  • 发表时间: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来创建自定义的线程池。