谷粒商城-高级-56 -商城业务-商品详情-异步编排优化

一、顺序获取详情

商品详情获取代码:
com/atguigu/gulimall/product/service/impl/SkuInfoServiceImpl.java

@Override
    public SkuItemVo item(Long skuId) {

        SkuItemVo skuItemVo = new SkuItemVo();

        // 1、sku基本信息获取 pms_sku_info
        SkuInfoEntity info = getById(skuId);
        skuItemVo.setInfo(info);

        Long spuId = info.getSpuId();
        Long catalogId = info.getCatalogId();

        //2、sku的图片信息    pms_sku_images
        List<SkuImagesEntity> imagesEntities = skuImagesService.getImagesBySkuId(skuId);
        skuItemVo.setImages(imagesEntities);

        //3、获取spu的销售属性组合
        // 分析当前spu有多少个sku(pms_sku_info),然后再查sku有多少个属性组合(pms_sku_sale_attr_value)
        List<SkuItemSaleAttrVo> saleAttrVos = skuSaleAttrValueService.getSaleAttrBySpuId(spuId);
        skuItemVo.setSaleAttr(saleAttrVos);

        //4、获取spu的介绍    pms_spu_info_desc
        SpuInfoDescEntity spuInfoDescEntity = spuInfoDescService.getById(spuId);
        skuItemVo.setDesc(spuInfoDescEntity);

        //5、获取spu的规格参数信息
        List<SpuItemAttrGroupVo> attrGroupVos = attrGroupService.getAttrGroupWithAttrsBySpuId(spuId, catalogId);
                skuItemVo.setGroupAttrs(attrGroupVos);

        return skuItemVo;
    }

可以看到上边的商品详情查询逻辑是按照执行顺序查询的,1查完,2才能查,2查完,3才能查,这样比较耗时,我们可以使用异步的方式来查询,上边代码的1,2是没有关系的,3、4、5必须依赖1的结果,下边就用之前学的异步编排的方式来优化代码。

二、异步编排获取详情

1、线程池配置

要在项目中使用异步编排,需要先创建线程池配置类,然后直接使用Bean。

1、创建线程池配置类
com/atguigu/gulimall/product/config/MyThreadConfig.java

package com.atguigu.gulimall.product.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.util.concurrent.Executors;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

/**
 * @author: kaiyi
 * @create: 2020-09-05 18:59
 */
// @EnableConfigurationProperties(ThreadPoolConfigProperties.class)
// 我们的配置属性已经加入到容器中了(component),所以,不再需要开启上边的属性配置了
@Configuration
public class MyThreadConfig {

  @Bean
  public ThreadPoolExecutor threadPoolExecutor(ThreadPoolConfigProperties pool) {

    /*
    这里是写死的参数,需要从配置文件里边获取相关参数
    return new ThreadPoolExecutor(
        20,
        200,
        10,
        pool.getKeepAliveTime(),
        TimeUnit.SECONDS,
        new LinkedBlockingQueue<>(100000),
        Executors.defaultThreadFactory(),
        new ThreadPoolExecutor.AbortPolicy()
    );
     */

    return new ThreadPoolExecutor(
        pool.getCoreSize(),
        pool.getMaxSize(),
        pool.getKeepAliveTime(),
        TimeUnit.SECONDS,
        new LinkedBlockingQueue<>(100000),
        Executors.defaultThreadFactory(),
        new ThreadPoolExecutor.AbortPolicy()
    );

  }

}

2、创建线程池属性类
com/atguigu/gulimall/product/config/ThreadPoolConfigProperties.java

package com.atguigu.gulimall.product.config;

import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;

/**
 * @author: kaiyi
 * @create: 2020-09-05 19:07
 */
@ConfigurationProperties(prefix = "gulimall.thread")
@Component
@Data
public class ThreadPoolConfigProperties {

  private Integer coreSize;
  private Integer maxSize;
  private Integer keepAliveTime;

}

3、修改配置文件
添加线程池相关配置
gulimall-product/src/main/resources/application.properties

spring.cache.type=redis

#spring.cache.cache-names=
spring.cache.redis.time-to-live=3600000

#
#spring.cache.redis.key-prefix=CACHE_
spring.cache.redis.use-key-prefix=true

# 空值也存入防止缓存穿透
spring.cache.redis.cache-null-values=true

# 线程池配置
gulimall.thread.core-size=20
gulimall.thread.max-size=200
gulimall.thread.keep-alive-time=10

file

我们可以看到,IDEA编辑器会有自定义配置类的相关属性的提示,如果没有看到,则需要添加自定义配置文件依赖。

自定义配置依赖:
pom.xml

    <!-- 配置提示 -->
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-configuration-processor</artifactId>
      <optional>true</optional>
    </dependency>

2、代码优化

com/atguigu/gulimall/product/service/impl/SkuInfoServiceImpl.java

@Service("skuInfoService")
public class SkuInfoServiceImpl extends ServiceImpl<SkuInfoDao, SkuInfoEntity> implements SkuInfoService {

    @Resource
    private SkuImagesService skuImagesService;

    @Resource
    private SpuInfoDescService spuInfoDescService;

    @Resource
    private AttrGroupService attrGroupService;

    @Resource
    private SkuSaleAttrValueService skuSaleAttrValueService;

    @Autowired
    ThreadPoolExecutor threadPoolExecutor;  // 线程池

/**
* 异步编排获取
*/
  @Override
    public SkuItemVo item(Long skuId) throws ExecutionException, InterruptedException {

        SkuItemVo skuItemVo = new SkuItemVo();

        // 异步编排
        CompletableFuture<SkuInfoEntity> infoFuture = CompletableFuture.supplyAsync(() -> {
            // 1、sku基本信息获取 pms_sku_info
            SkuInfoEntity info = getById(skuId);
            skuItemVo.setInfo(info);

            // 需要将获取的值返回别,后边别人要用
            return info;

        }, threadPoolExecutor);

        // 接下来做,res表示上一步的返回值
        CompletableFuture<Void> saleAttrFuture = infoFuture.thenAcceptAsync((res) -> {

            //3、获取spu的销售属性组合
            // 分析当前spu有多少个sku(pms_sku_info),然后再查sku有多少个属性组合(pms_sku_sale_attr_value)
            List<SkuItemSaleAttrVo> saleAttrVos = skuSaleAttrValueService.getSaleAttrBySpuId(res.getSpuId());
            skuItemVo.setSaleAttr(saleAttrVos);

        }, threadPoolExecutor);

        CompletableFuture<Void> descFuture = infoFuture.thenAcceptAsync((res) -> {

            //4、获取spu的介绍    pms_spu_info_desc
            SpuInfoDescEntity spuInfoDescEntity = spuInfoDescService.getById(res.getSpuId());
            skuItemVo.setDesc(spuInfoDescEntity);

        }, threadPoolExecutor);

        CompletableFuture<Void> baseAttrFuture = infoFuture.thenAcceptAsync((res) -> {

            //5、获取spu的规格参数信息
            List<SpuItemAttrGroupVo> attrGroupVos = attrGroupService.getAttrGroupWithAttrsBySpuId(res.getSpuId(), res.getCatalogId());
            skuItemVo.setGroupAttrs(attrGroupVos);

        }, threadPoolExecutor);

        CompletableFuture<Void> imageFuture = CompletableFuture.runAsync(() -> {
            //2、sku的图片信息    pms_sku_images
            List<SkuImagesEntity> imagesEntities = skuImagesService.getImagesBySkuId(skuId);
            skuItemVo.setImages(imagesEntities);

        }, threadPoolExecutor);

        // 等到所有任务都完成,infoFuture 可以不用写
        // CompletableFuture.allOf(infoFuture, saleAttrFuture, descFuture, baseAttrFuture, imageFuture).get();
        CompletableFuture.allOf(saleAttrFuture, descFuture, baseAttrFuture, imageFuture).get();

        return skuItemVo;
    }

}

单元测试:

@Test
  public void itemTest() throws ExecutionException, InterruptedException {

    Long skuId = 1L;
    SkuItemVo itemVo = skuInfoService.item(skuId);

    System.out.println(itemVo);
  }

测试执行结果:

2020-09-05 20:12:08.508  INFO 23284 --- [           main] c.a.n.client.config.impl.ClientWorker    : [fixed-127.0.0.1_8848-748c5710-1166-498b-9e0e-528cb6c9d769] [subscribe] gulimall-product.properties+DEFAULT_GROUP+748c5710-1166-498b-9e0e-528cb6c9d769
2020-09-05 20:12:08.510  INFO 23284 --- [           main] c.a.nacos.client.config.impl.CacheData   : [fixed-127.0.0.1_8848-748c5710-1166-498b-9e0e-528cb6c9d769] [add-listener] ok, tenant=748c5710-1166-498b-9e0e-528cb6c9d769, dataId=gulimall-product.properties, group=DEFAULT_GROUP, cnt=1
2020-09-05 20:12:09.274  INFO 23284 --- [pool-2-thread-1] com.zaxxer.hikari.HikariDataSource       : HikariPool-1 - Starting...
2020-09-05 20:12:10.144  INFO 23284 --- [pool-2-thread-1] com.zaxxer.hikari.HikariDataSource       : HikariPool-1 - Start completed.
2020-09-05 20:12:10.165 DEBUG 23284 --- [pool-2-thread-2] c.a.g.p.dao.SkuImagesDao.selectList      : ==>  Preparing: SELECT id,img_sort,img_url,default_img,sku_id FROM pms_sku_images WHERE (sku_id = ?) 
2020-09-05 20:12:10.262 DEBUG 23284 --- [pool-2-thread-1] c.a.g.product.dao.SkuInfoDao.selectById  : ==>  Preparing: SELECT sku_id,sku_name,sku_title,catalog_id,sale_count,price,brand_id,sku_default_img,sku_subtitle,sku_desc,spu_id FROM pms_sku_info WHERE sku_id=? 
2020-09-05 20:12:10.279 DEBUG 23284 --- [pool-2-thread-2] c.a.g.p.dao.SkuImagesDao.selectList      : ==> Parameters: 1(Long)
2020-09-05 20:12:10.279 DEBUG 23284 --- [pool-2-thread-1] c.a.g.product.dao.SkuInfoDao.selectById  : ==> Parameters: 1(Long)
2020-09-05 20:12:10.334 DEBUG 23284 --- [pool-2-thread-2] c.a.g.p.dao.SkuImagesDao.selectList      : <==      Total: 1
2020-09-05 20:12:10.334 DEBUG 23284 --- [pool-2-thread-1] c.a.g.product.dao.SkuInfoDao.selectById  : <==      Total: 1
2020-09-05 20:12:10.387 DEBUG 23284 --- [pool-2-thread-4] c.a.g.p.dao.SpuInfoDescDao.selectById    : ==>  Preparing: SELECT spu_id,decript FROM pms_spu_info_desc WHERE spu_id=? 
2020-09-05 20:12:10.387 DEBUG 23284 --- [pool-2-thread-4] c.a.g.p.dao.SpuInfoDescDao.selectById    : ==> Parameters: 5(Long)
2020-09-05 20:12:10.393 DEBUG 23284 --- [pool-2-thread-3] c.a.g.p.d.A.getAttrGroupWithAttrsBySpuId : ==>  Preparing: SELECT product.spu_id, pag.attr_group_id, pag.attr_group_name, product.attr_id, product.attr_name, product.attr_value FROM pms_product_attr_value product LEFT JOIN pms_attr_attrgroup_relation paar ON product.attr_id = paar.attr_id LEFT JOIN pms_attr_group pag ON paar.attr_group_id = pag.attr_group_id WHERE product.spu_id = ? AND pag.catelog_id = ? 
2020-09-05 20:12:10.394 DEBUG 23284 --- [pool-2-thread-3] c.a.g.p.d.A.getAttrGroupWithAttrsBySpuId : ==> Parameters: 5(Long), 225(Long)
2020-09-05 20:12:10.394 DEBUG 23284 --- [pool-2-thread-4] c.a.g.p.dao.SpuInfoDescDao.selectById    : <==      Total: 1
2020-09-05 20:12:10.398 DEBUG 23284 --- [pool-2-thread-3] c.a.g.p.d.A.getAttrGroupWithAttrsBySpuId : <==      Total: 1
2020-09-05 20:12:10.400 DEBUG 23284 --- [pool-2-thread-5] c.a.g.p.d.S.getSaleAttrBySpuId           : ==>  Preparing: SELECT ssav.attr_id attr_id, ssav.attr_name attr_name, ssav.attr_value, group_concat( DISTINCT info.sku_id ) sku_ids FROM pms_sku_info info LEFT JOIN pms_sku_sale_attr_value ssav ON ssav.sku_id = info.sku_id WHERE info.spu_id = ? GROUP BY ssav.attr_id, ssav.attr_name, ssav.attr_value 
2020-09-05 20:12:10.400 DEBUG 23284 --- [pool-2-thread-5] c.a.g.p.d.S.getSaleAttrBySpuId           : ==> Parameters: 5(Long)
2020-09-05 20:12:10.405 DEBUG 23284 --- [pool-2-thread-5] c.a.g.p.d.S.getSaleAttrBySpuId           : <==      Total: 1
SkuItemVo(info=SkuInfoEntity(skuId=1, spuId=5, skuName=华为P60 8G, skuDesc=null, catalogId=225, brandId=4, skuDefaultImg=https://gulimall-corwien.oss-cn-heyuan.aliyuncs.com/2020-08-26/54995f55-c35c-4711-8447-c2482336ae1e_8bf441260bffa42f.jpg, skuTitle=华为P60 8G, skuSubtitle=华为国产旗舰, price=6999.0000, saleCount=0), hasStock=true, images=[SkuImagesEntity(id=1, skuId=1, imgUrl=https://gulimall-corwien.oss-cn-heyuan.aliyuncs.com/2020-08-26/54995f55-c35c-4711-8447-c2482336ae1e_8bf441260bffa42f.jpg, imgSort=null, defaultImg=1)], saleAttr=[SkuItemSaleAttrVo(attrId=3, attrName=内存, attrValues=[AttrValueWithSkuIdVo(attrValue=8G, skuIds=1)])], desc=SpuInfoDescEntity(spuId=5, decript=https://gulimall-corwien.oss-cn-heyuan.aliyuncs.com/2020-08-26/360b82a0-3f26-48dc-8112-0923fd4fd35c_0d40c24b264aa511.jpg), groupAttrs=[SpuItemAttrGroupVo(groupName=CPU星号, attrs=[Attr(attrId=null, attrName=CPU, attrValue=麒麟800)])])
2020-09-05 20:12:10.938  INFO 23284 --- [      Thread-10] com.zaxxer.hikari.HikariDataSource       : HikariPool-1 - Shutdown initiated...
2020-09-05 20:12:11.043  INFO 23284 --- [      Thread-10] com.zaxxer.hikari.HikariDataSource       : HikariPool-1 - Shutdown completed.

为者常成,行者常至