CompletableFuture asynchronous orchestration

1. CompletableFuture asynchronous orchestration

1.1 Why do you need asynchronous orchestration

  Problem: The logic of querying the product details page is very complicated, and the acquisition of data requires remote calls, which will inevitably take more time.

At present, the product details page in my business contains the following 7 methods:

Get the basic details and picture list of sku

Get real-time prices

Get three levels of classification

Get sales attribute and selected state

Get product switching data

Get Poster Information

Get platform information

  The above query process is OpenFeignimplemented with service calls. Assuming that each remote call takes 1s, it will take 7s to complete the execution, which is unacceptable to users.

  So if there are multiple threads performing these 7 steps at the same time, will the time be shorter?


1.2 Introduction to CompletableFuture

  Futureis Java 5an added class used to describe the result of an asynchronous computation. You can use isDonethe method to check whether the calculation is complete, or use the get to block the calling thread until the calculation is completed and return the result, you can also use the cancelmethod to stop the execution of the task.

  In Java 8, a new class with about 50 methods has been added: CompletableFuture, which provides a very powerful Futureextension function, which can help us simplify the complexity of asynchronous programming, and provides the ability of functional programming, which can be used in the way of callback Process calculation results, and provide conversion and combination CompletableFuturemethods.

  CompletableFutureThe class implements Futurethe interface, so you can still getget the result by method blocking or polling as before, but this method is not recommended.

  CompletableFutureBoth the implementation class and the implementation class FutureTaskbelonging to the interface can obtain the execution result of the thread.Future

image-20230419214958726

1.3 Create an asynchronous object

  CompletableFutureFour static methods are provided to create an asynchronous operation.

image-20230419215150849

  A method that does not specify an Executor will use ForkJoinPool.commonPool()as its thread pool to execute asynchronous code.

  • runAsyncMethod does not support return value.

  • supplyAsyncReturn values ​​can be supported.

  whenCompleteIt can handle normal or abnormal calculation results and exceptionallyhandle abnormal situations. BiConsumer<? super T,? super Throwable>business can be defined


whenCompletewhenCompleteAsyncThe difference between and :

  whenComplete: It is the thread that executes the current task to execute the task that continues to be whenCompleteexecuted .

  whenCompleteAsync: It is to execute and continue to submit whenCompleteAsyncthis task to the thread pool for execution.

  The method does not Asyncend with the Actionsame thread, but Asyncmay be executed by other threads (if the same thread pool is used, it may also be selected by the same thread for execution)


Code demo:

public class CompletableFutureDemo {
    
    
    public static void main(String[] args) throws ExecutionException, InterruptedException {
    
    
        //创建一个没有返回值的异步对象
        CompletableFuture<Void> future = CompletableFuture.runAsync(() -> {
    
    
            System.out.println("没有返回值结果");
        });
        System.out.println(future.get());

        //创建一个有返回值的异步对象
        CompletableFuture<Integer> completableFuture = CompletableFuture.supplyAsync(new Supplier<Integer>() {
    
    
            @Override
            public Integer get() {
    
    
                int a=1/0;
                return 404;
            }
        }).whenComplete(new BiConsumer<Integer, Throwable>() {
    
    
            /**
             *whenComplete 和异步对象使用用一个线程
             * @param integer   异步对象执行后的返回值结果
             * @param throwable 异常对象
             */
            @Override
            public void accept(Integer integer, Throwable throwable) {
    
    
                System.out.println("whenComplete:"+integer);
                System.out.println("whenComplete:"+throwable);
            }
        }).exceptionally(new Function<Throwable, Integer>() {
    
    
            /**
             * 只处理异常的回调
             * @param throwable
             * @return
             */
            @Override
            public Integer apply(Throwable throwable) {
    
    
                return null;
            }
        }).whenCompleteAsync(new BiConsumer<Integer, Throwable>() {
    
    
            /**
             * whenCompleteAsync跟异步对象有可能不适用同一个线程,由线程池重新分配
             * @param integer
             * @param throwable
             */
            @Override
            public void accept(Integer integer, Throwable throwable) {
    
    

            }
        });
    }

}

image-20230419220803592

1.4 Thread serialization and parallelization methods

  thenApplyMethod: When a thread depends on another thread, get the result returned by the previous task and return the return value of the current task.

image-20230419225245838

  thenAcceptMethod: Consume the processing result. Receive the processing result of the task, and consume the processing, and return no result.

insert image description here

  thenRunMethod: As long as the above task is completed, it will start to execute thenRun, but after the task is processed, the follow-up operation thenRunwill

insert image description here

AsyncExecution is asynchronous   by default. The so-called asynchronous here refers to not executing in the current thread.

Function<? super T,? extends U> 
T:上一个任务返回结果的类型 
U:当前任务的返回值类型

  Code demo:

public class CompletableFutureDemo {
    
    
    public static void main(String[] args) {
    
    
        ThreadPoolExecutor threadPoolExecutor =
                new ThreadPoolExecutor(
                        50,
                        500,
                        30,
                        TimeUnit.SECONDS,
                        new ArrayBlockingQueue<>(10000)
                );

        //创建一个异步任务对象A
        CompletableFuture<Object> futureA = CompletableFuture.supplyAsync(new Supplier<Object>() {
    
    
            @Override
            public Object get() {
    
    
                return "404";
            }
        },threadPoolExecutor);
        //创建一个B
        futureA.thenAcceptAsync(new Consumer<Object>() {
    
    
            @SneakyThrows
            @Override
            public void accept(Object o) {
    
    
                    Thread.sleep(500);
                    System.out.println("我是B");
            }
        },threadPoolExecutor);
        //创建一个C
        futureA.thenAcceptAsync(new Consumer<Object>() {
    
    
            @Override
            public void accept(Object o) {
    
    
                System.out.println("我是C");
            }
        },threadPoolExecutor);
    }
}

image-20230419221321514

  Here is a test to see if it is parallelization. Let B sleep for a while, and you can see that output C first and then output B, indicating that it is parallelization.

  Because if it is serialized, then even if B sleeps for a while, then C will always wait, and the output order is B, C

1.5 Multitasking combination

public static CompletableFuture<Void> allOf(CompletableFuture<?>... cfs);
public static CompletableFuture<Object> anyOf(CompletableFuture<?>... cfs);

  allOf: Wait for all tasks to complete.

  anyOf: As long as there is one task completed.

1.6 Optimize product details page (business code)

1.6.1 Code before optimization

@Service
@SuppressWarnings("all")
public class ItemServiceImpl implements ItemService {
    
    
    
    @Autowired
    private ProductFeignClient productFeignClient;

    //获取商品详情数据
    @Override
    public HashMap<String, Object> getItem(Long skuId) {
    
    
        HashMap<String, Object> resultMap=new HashMap<>();

        //获取sku的基本详情和图片列表
        SkuInfo skuInfo = productFeignClient.getSkuInfo(skuId);
        //获取实时价格
        BigDecimal skuPrice = productFeignClient.getSkuPrice(skuId);

        //判断
        if(skuInfo!=null){
    
    
            //获取三级分类
            BaseCategoryView categoryView = productFeignClient.getCategoryView(skuInfo.getCategory3Id());
            //获取销售属性和选中状态
            List<SpuSaleAttr> spuSaleAttrListCheckBySku = productFeignClient.getSpuSaleAttrListCheckBySku(skuId, skuInfo.getSpuId());
            //获取商品切换数据
            Map skuValueIdsMap = productFeignClient.getSkuValueIdsMap(skuInfo.getSpuId());
            //获取海报信息
            List<SpuPoster> spuPosterBySpuId = productFeignClient.findSpuPosterBySpuId(skuInfo.getSpuId());

            resultMap.put("categoryView",categoryView);
            resultMap.put("spuSaleAttrList",spuSaleAttrListCheckBySku);
            resultMap.put("valuesSkuJson", JSON.toJSONString(skuValueIdsMap));
            resultMap.put("spuPosterList",spuPosterBySpuId);
        }
        //获取平台信息
        List<BaseAttrInfo> attrList = productFeignClient.getAttrList(skuId);
        //处理数据符合要求 List  Obj  key attrName value attrValue
        List<Map<String, String>> spuAttrList = attrList.stream().map(baseAttrInfo -> {
    
    
            Map<String, String> map = new HashMap<>();
            map.put("attrName", baseAttrInfo.getAttrName());
            map.put("attrValue", baseAttrInfo.getAttrValueList().get(0).getValueName());
            return map;
        }).collect(Collectors.toList());

        //存储数据
        resultMap.put("skuInfo",skuInfo);
        resultMap.put("price",skuPrice);
        resultMap.put("skuAttrList",spuAttrList);
        return resultMap;
    }
}

1.6.2 Asynchronous orchestration using CompletableFuture

Configure the thread pool:

@Configuration
public class ThreadPoolConfig {
    
    
    /**
     * 核心线程数
     * 最大线程数
     * 空闲存活时间
     * 时间单位
     * 阻塞队列
     * 默认:
     *  线程工厂
     *  拒绝策略
     * @return
     */
    @Bean
    public ThreadPoolExecutor threadPoolExecutor(){
    
    

        return new ThreadPoolExecutor(
                50,
                500,
                30,
                TimeUnit.SECONDS,
                new ArrayBlockingQueue<>(10000)
        );
    }
}

Implement class transformation:

@Service
@SuppressWarnings("all")
public class ItemServiceImpl implements ItemService {
    
    

    @Autowired
    private ProductFeignClient productFeignClient;

    @Autowired
    private ThreadPoolExecutor executor;

    //获取商品详情数据
    @Override
    public HashMap<String, Object> getItem(Long skuId) {
    
    
        HashMap<String, Object> resultMap=new HashMap<>();

        CompletableFuture<SkuInfo> skuInfoCompletableFuture = CompletableFuture.supplyAsync(new Supplier<SkuInfo>() {
    
    
            @Override
            public SkuInfo get() {
    
    
                //获取sku的基本详情和图片列表
                SkuInfo skuInfo = productFeignClient.getSkuInfo(skuId);
                resultMap.put("skuInfo", skuInfo);
                return skuInfo;
            }
        }, executor);

        CompletableFuture<Void> skuPriceCompletableFuture = CompletableFuture.runAsync(new Runnable() {
    
    
            @Override
            public void run() {
    
    
                //获取实时价格
                BigDecimal skuPrice = productFeignClient.getSkuPrice(skuId);
                resultMap.put("price", skuPrice);
            }
        }, executor);

        //判断
        CompletableFuture<Void> categoryViewCompletableFuture = skuInfoCompletableFuture.thenAcceptAsync(new Consumer<SkuInfo>() {
    
    
            @Override
            public void accept(SkuInfo skuInfo) {
    
    
                //获取三级分类
                BaseCategoryView categoryView = productFeignClient.getCategoryView(skuInfo.getCategory3Id());
                resultMap.put("categoryView",categoryView);
            }
        }, executor);

        CompletableFuture<Void> spuSaleAttrListCheckBySkuCompletableFuture = skuInfoCompletableFuture.thenAcceptAsync(new Consumer<SkuInfo>() {
    
    
            @Override
            public void accept(SkuInfo skuInfo) {
    
    
                //获取销售属性和选中状态
                List<SpuSaleAttr> spuSaleAttrListCheckBySku = productFeignClient.getSpuSaleAttrListCheckBySku(skuId, skuInfo.getSpuId());
                resultMap.put("spuSaleAttrList",spuSaleAttrListCheckBySku);
            }
        }, executor);
        CompletableFuture<Void> skuValueIdsMapCompletableFuture = skuInfoCompletableFuture.thenAcceptAsync(new Consumer<SkuInfo>() {
    
    
            @Override
            public void accept(SkuInfo skuInfo) {
    
    
                //获取商品切换数据
                Map skuValueIdsMap = productFeignClient.getSkuValueIdsMap(skuInfo.getSpuId());
                resultMap.put("valuesSkuJson", JSON.toJSONString(skuValueIdsMap));
            }
        }, executor);

        CompletableFuture<Void> findSpuPosterBySpuIdCompletableFuture = skuInfoCompletableFuture.thenAcceptAsync(new Consumer<SkuInfo>() {
    
    
            @Override
            public void accept(SkuInfo skuInfo) {
    
    
                //获取海报信息
                List<SpuPoster> spuPosterBySpuId = productFeignClient.findSpuPosterBySpuId(skuInfo.getSpuId());
                resultMap.put("spuPosterList",spuPosterBySpuId);
            }
        }, executor);


        CompletableFuture<Void> attrListCompletableFuture = CompletableFuture.runAsync(new Runnable() {
    
    
            @Override
            public void run() {
    
    
                //获取平台信息
                List<BaseAttrInfo> attrList = productFeignClient.getAttrList(skuId);
                //处理数据符合要求 List  Obj  key attrName value attrValue
                List<Map<String, String>> spuAttrList = attrList.stream().map(baseAttrInfo -> {
    
    
                    Map<String, String> map = new HashMap<>();
                    map.put("attrName", baseAttrInfo.getAttrName());
                    map.put("attrValue", baseAttrInfo.getAttrValueList().get(0).getValueName());
                    return map;
                }).collect(Collectors.toList());

                //存储数据
                resultMap.put("skuAttrList", spuAttrList);
            }
        }, executor);

        //多任务组合 -- 所有的异步任务执行完成才是完成
        CompletableFuture.allOf(
                skuInfoCompletableFuture,
                skuPriceCompletableFuture,
                categoryViewCompletableFuture,
                spuSaleAttrListCheckBySkuCompletableFuture,
                skuValueIdsMapCompletableFuture,
                findSpuPosterBySpuIdCompletableFuture,
                attrListCompletableFuture
        ).join();
        return resultMap;
    }
}

  Determine which API to call based on whether there is a return value, and then check whether there are dependencies. Several of them depend on SkuInfo, so use skuInfoCompletableFuture to create.

   We need to wait for each task to be executed before returning, so finally use allOfthe method to combine multiple tasks.

1.6.3 Test whether the function is normal

  This kind of asynchronous effect is actually better tested in a high-concurrency environment. We can verify whether the function is normal here.

  Visit the product detail page:

image-20230419224246988

  View data in Redis

image-20230419224307865

  It can be seen that 6 keys are cached. Since our price is a real-time price, the database is always checked. Do not use the cache.

Guess you like

Origin blog.csdn.net/qq_43753724/article/details/130255436