首页 > 其他分享 >谷粒商城项目篇10_分布式高级篇_ES首页检索功能、异步、商品详情

谷粒商城项目篇10_分布式高级篇_ES首页检索功能、异步、商品详情

时间:2023-06-28 10:32:15浏览次数:37  
标签:10 searchParamVo String 异步 private 线程 attrs new ES


目录

  1. 首页检索功能
  2. 异步
  3. 商品详情

一、首页检索功能

1.环境准备

配置本地域名解析

谷粒商城项目篇10_分布式高级篇_ES首页检索功能、异步、商品详情_ES检索

配置nginx将请求转给网关

http {
    include       mime.types;
    default_type  application/octet-stream;


    sendfile        on;
 
    keepalive_timeout  65;

  
	
	# 配置上游服务器(网关)
	upstream gulishop{
		server 192.168.0.100:88;
	}

    server {
        listen       80;
        server_name  *.gulishop.cn gulishop.cn;

        #charset koi8-r;

        #access_log  logs/host.access.log  main;

        location / {
            # root   html;
            # index  index.html index.htm;
			proxy_set_header Host $host;
			// 监听80的/路径下所有请求都转给网关
			proxy_pass http://gulishop;
        }

     

        error_page   500 502 503 504  /50x.html;
        location = /50x.html {
            root   html;
        }

       
    }

配置网关转发规则,实现首页和搜索页之间的跳转

# 4. 首页
        - id: guli-shop_host_route
          uri: lb://guli-shop-product
          predicates:
            - Host=gulishop.cn

        # 5. 首页搜索
        - id: search_router
          uri: lb://guli-shop-search
          predicates:
            - Host=search.gulishop.cn

编search微服务的controller处理检索请求

package henu.soft.xiaosi.search.web;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;

@Controller
public class SearchController {

    @GetMapping("/list.html")
    public String listPage(){

        // 处理检索属性

        return "list";
    }
}

2.确认检索属性、返回结果属性,编写vo实体

1.检索条件分析
  • 全文检索:skuTitle —> keyword
  • 排序:saleCount(销量)、hotScore(热度分)、skuPrice(价格)
  • 过滤:hasStock、skuPrice区间、brandId、catalog3Id、attrs
  • 聚合:attrs
2.完整查询参数

keyword=小米&sort=saleCount_desc/asc&hasStock=0/1&skuPrice=400_1900&brandId=1&catalog3Id=1&at trs=1_3G:4G:5G&attrs=2_骁龙845&attrs=4_高清屏

3.实体Vo
@Data
public class SearchParam {

    /**
     * 页面传递过来的全文匹配关键字
     */
    private String keyword;

    /**
     * 品牌id,可以多选
     */
    private List<Long> brandId;

    /**
     * 三级分类id
     */
    private Long catalog3Id;

    /**
     * 排序条件:sort=price/salecount/hotscore_desc/asc
     */
    private String sort;

    /**
     * 是否显示有货
     */
    private Integer hasStock;

    /**
     * 价格区间查询
     */
    private String skuPrice;

    /**
     * 按照属性进行筛选
     */
    private List<String> attrs;

    /**
     * 页码
     */
    private Integer pageNum = 1;

    /**
     * 原生的所有查询条件
     */
    private String _queryString;


}

检索结果

@Data
public class SearchResultVo {

    /**
     * 查询到的所有商品信息
     */
    private List<SkuEsModel> products;


    /**
     * 当前页码
     */
    private Integer pageNum;

    /**
     * 总记录数
     */
    private Long total;

    /**
     * 总页码
     */
    private Integer totalPages;

    private List<Integer> pageNavs;

    /**
     * 当前查询到的结果,所有涉及到的品牌
     */
    private List<BrandVo> brands;

    /**
     * 当前查询到的结果,所有涉及到的所有属性
     */
    private List<AttrVo> attrs;

    /**
     * 当前查询到的结果,所有涉及到的所有分类
     */
    private List<CatalogVo> catalogs;


    //===========================以上是返回给页面的所有信息============================//


    /* 面包屑导航数据 */
    private List<NavVo> navs;

    @Data
    public static class NavVo {
        private String navName;
        private String navValue;
        private String link;
    }


    @Data
    @AllArgsConstructor
    public static class BrandVo {

        private Long brandId;

        private String brandName;

        private String brandImg;
    }


    @Data
    @AllArgsConstructor
    public static class AttrVo {

        private Long attrId;

        private String attrName;

        private List<String> attrValue;
    }


    @Data
    @AllArgsConstructor
    public static class CatalogVo {

        private Long catalogId;

        private String catalogName;
    }
}
4.编写DSL
# 重新设置mapping
PUT gulishop_product
{
  "mappings": {
    "properties": {
      "skuId": {
        "type": "long"
      },
      "spuId": {
        "type": "keyword"
      },
      "skuTitle": {
        "type": "text",
        "analyzer": "ik_smart"
      },
      "skuPrice": {
        "type": "keyword"
      },
      "skuImg": {
        "type": "keyword"
      },
      "saleCount": {
        "type": "long"
      },
      "hasStock": {
        "type": "boolean"
      },
      "hotScore": {
        "type": "long"
      },
      "brandId": {
        "type": "long"
      },
      "catalogId": {
        "type": "long"
      },
      "brandName": {
        "type": "keyword"
      },
      "brandImg": {
        "type": "keyword"
      },
      "catalogName": {
        "type": "keyword"
      },
      "attrs": {
        "type": "nested",
        "properties": {
          "attrId": {
            "type": "long"
          },
          "attrName": {
            "type": "keyword"
          },
          "attrValue": {
            "type": "keyword"
          }
        }
      }
    }
  }
}




GET gulishop_product/_search



# 迁移数据
POST _reindex
{
  "source": {
    "index": "product"
  },
  "dest": {
    "index":"gulishop_product"
  }
}


# 查询
GET gulishop_product/_search
{
  "query": {
    "bool": {
      "must": [
        {
          "match": {
            "skuTitle": "华为"
          }
        }
      ],
      "filter": [
        {
            "term": {
              "catalogId": "225"
            }
        },
        {
            "terms": {
            "brandId": [
              "2"
            ]
          }
        },
        {
          "term": {
            "hasStock": "false"
          }
        },
        {
          "range": {
            "skuPrice": {
              "gte": 1000,
              "lte": 7000
            }
          }
        },
        {
          "nested": {
            "path": "attrs",
            "query": {
              "bool": {
                "must": [
                  {
                    "term": {
                      "attrs.attrId": {
                        "value": "6"
                      }
                    }
                  }
                ]
              }
            }
          }
        }
      ]
    }
  },
  "sort": [
    {
      "skuPrice": {
        "order": "desc"
      }
    }
  ],
  "from": 0,
  "size": 5,
  "highlight": {
    "fields": {"skuTitle": {}},
    "pre_tags": "<b style='color:red'>", 
    "post_tags": "</b>"
  },
  "aggs": {
    "brandAgg": {
      "terms": {
        "field": "brandId",
        "size": 10
      },
      "aggs": {
        "brandNameAgg": {
          "terms": {
            "field": "brandName",
            "size": 10
          }
        },
      
        "brandImgAgg": {
          "terms": {
            "field": "brandImg",
            "size": 10
          }
        }
        
      }
    },
    "catalogAgg":{
      "terms": {
        "field": "catalogId",
        "size": 10
      },
      "aggs": {
        "catalogNameAgg": {
          "terms": {
            "field": "catalogName",
            "size": 10
          }
        }
      }
    },
    "attrs":{
      "nested": {
        "path": "attrs"
      },
      "aggs": {
        "attrIdAgg": {
          "terms": {
            "field": "attrs.attrId",
            "size": 10
          },
          "aggs": {
            "attrNameAgg": {
              "terms": {
                "field": "attrs.attrName",
                "size": 10
              }
            }
          }
        }
      }
    }
  }
}

谷粒商城项目篇10_分布式高级篇_ES首页检索功能、异步、商品详情_ES检索_02

谷粒商城项目篇10_分布式高级篇_ES首页检索功能、异步、商品详情_List_03

5.编写代码

逻辑

  • 准备检索请求,调用封装方法
  • 执行检索请求
  • 分析响应数据,封装换成需要的格式
@Qualifier("client")
    @Autowired
    RestHighLevelClient restHighLevelClient;

    /**
     * 根据条件检索对应商品信息
     */
    @Override
    public SearchResultVo searchProductByParam(SearchParamVo searchParamVo) {
        // 需要的返回数据
        SearchResultVo resultVo = null;

        // 准备检索请求,调用封装方法
        SearchRequest searchRequest = helperBuildSearchRequest(searchParamVo);


        try {
            // 执行检索请求
            SearchResponse response = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
			 // 分析响应数据,封装换成需要的格式
            resultVo = helperBuilderResult(response);
           
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
        }


        return resultVo;
    }

获取searchRequest查询条件

/**
     * 1. 封装方法,获取searchRequest
     *
     * @return
     */
    private SearchRequest helperBuildSearchRequest(SearchParamVo searchParamVo) {

        // 构建DSL语句
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();

        /**
         * 查询:模糊匹配、过滤(按照属性、分类、品牌、价格区间、库存)
         */

        /**
         * {
         *      query :{
         *          bool: {
         *              must:{},
         *              filter{}
         *          }
         *      },
         *      sort:[],
         *      from:0,
         *      size:1,
         *      highlight:{},
         *      aggs:{}
         * }
         */

        //1. 构建bool query
        BoolQueryBuilder boolQueryBuilder = new BoolQueryBuilder();
        //1.1 bool must
        if (!StringUtils.isEmpty(searchParamVo.getKeyword())) {
            boolQueryBuilder.must(QueryBuilders.matchQuery("skuTitle", searchParamVo.getKeyword()));
        }

        //1.2 bool filter
        //1.2.1 catalog
        if (searchParamVo.getCatalog3Id() != null) {
            boolQueryBuilder.filter(QueryBuilders.termQuery("catalogId", searchParamVo.getCatalog3Id()));
        }
        //1.2.2 brand
        if (searchParamVo.getBrandId() != null && searchParamVo.getBrandId().size() > 0) {
            boolQueryBuilder.filter(QueryBuilders.termsQuery("brandId", searchParamVo.getBrandId()));
        }
        //1.2.3 hasStock
        if (searchParamVo.getHasStock() != null) {
            boolQueryBuilder.filter(QueryBuilders.termQuery("hasStock", searchParamVo.getHasStock() == 1));
        }
        //1.2.4 priceRange
        RangeQueryBuilder rangeQueryBuilder = QueryBuilders.rangeQuery("skuPrice");
        if (!StringUtils.isEmpty(searchParamVo.getSkuPrice())) {
            String[] prices = searchParamVo.getSkuPrice().split("_");
            if (prices.length == 1) {
                if (searchParamVo.getSkuPrice().startsWith("_")) {
                    rangeQueryBuilder.lte(Integer.parseInt(prices[0]));
                } else {
                    rangeQueryBuilder.gte(Integer.parseInt(prices[0]));
                }
            } else if (prices.length == 2) {
                //_6000会截取成["","6000"]
                if (!prices[0].isEmpty()) {
                    rangeQueryBuilder.gte(Integer.parseInt(prices[0]));
                }
                rangeQueryBuilder.lte(Integer.parseInt(prices[1]));
            }
            boolQueryBuilder.filter(rangeQueryBuilder);
        }
        //1.2.5 attrs-nested
        //attrs=1_5寸:8寸&2_16G:8G
        List<String> attrs = searchParamVo.getAttrs();
        BoolQueryBuilder queryBuilder = new BoolQueryBuilder();
        if (attrs != null && attrs.size() > 0) {
            attrs.forEach(attr -> {
                String[] attrSplit = attr.split("_");
                queryBuilder.must(QueryBuilders.termQuery("attrs.attrId", attrSplit[0]));
                String[] attrValues = attrSplit[1].split(":");
                queryBuilder.must(QueryBuilders.termsQuery("attrs.attrValue", attrValues));
            });
        }
        NestedQueryBuilder nestedQueryBuilder = QueryBuilders.nestedQuery("attrs", queryBuilder, ScoreMode.None);
        boolQueryBuilder.filter(nestedQueryBuilder);
        //1. bool query构建完成
        searchSourceBuilder.query(boolQueryBuilder);

        //2. sort  eg:sort=saleCount_desc/asc
        if (!StringUtils.isEmpty(searchParamVo.getSort())) {
            String[] sortSplit = searchParamVo.getSort().split("_");
            searchSourceBuilder.sort(sortSplit[0], sortSplit[1].equalsIgnoreCase("asc") ? SortOrder.ASC : SortOrder.DESC);
        }

        //3. 分页
        searchSourceBuilder.from((searchParamVo.getPageNum() - 1) * EsConstant.PRODUCT_PAGESIZE);
        searchSourceBuilder.size(EsConstant.PRODUCT_PAGESIZE);

        //4. 高亮highlight
        if (!StringUtils.isEmpty(searchParamVo.getKeyword())) {
            HighlightBuilder highlightBuilder = new HighlightBuilder();
            highlightBuilder.field("skuTitle");
            highlightBuilder.preTags("<b style='color:red'>");
            highlightBuilder.postTags("</b>");
            searchSourceBuilder.highlighter(highlightBuilder);
        }

        //5. 聚合
        //5.1 按照brand聚合
        TermsAggregationBuilder brandAgg = AggregationBuilders.terms("brandAgg").field("brandId");
        TermsAggregationBuilder brandNameAgg = AggregationBuilders.terms("brandNameAgg").field("brandName");
        TermsAggregationBuilder brandImgAgg = AggregationBuilders.terms("brandImgAgg").field("brandImg");
        brandAgg.subAggregation(brandNameAgg);
        brandAgg.subAggregation(brandImgAgg);
        searchSourceBuilder.aggregation(brandAgg);

        //5.2 按照catalog聚合
        TermsAggregationBuilder catalogAgg = AggregationBuilders.terms("catalogAgg").field("catalogId");
        TermsAggregationBuilder catalogNameAgg = AggregationBuilders.terms("catalogNameAgg").field("catalogName");
        catalogAgg.subAggregation(catalogNameAgg);
        searchSourceBuilder.aggregation(catalogAgg);

        //5.3 按照attrs聚合
        NestedAggregationBuilder nestedAggregationBuilder = new NestedAggregationBuilder("attrs", "attrs");
        //按照attrId聚合
        TermsAggregationBuilder attrIdAgg = AggregationBuilders.terms("attrIdAgg").field("attrs.attrId");
        //按照attrId聚合之后再按照attrName和attrValue聚合
        TermsAggregationBuilder attrNameAgg = AggregationBuilders.terms("attrNameAgg").field("attrs.attrName");
        TermsAggregationBuilder attrValueAgg = AggregationBuilders.terms("attrValueAgg").field("attrs.attrValue");
        attrIdAgg.subAggregation(attrNameAgg);
        attrIdAgg.subAggregation(attrValueAgg);

        nestedAggregationBuilder.subAggregation(attrIdAgg);
        searchSourceBuilder.aggregation(nestedAggregationBuilder);


        System.out.println(searchSourceBuilder.toString());

        SearchRequest request = new SearchRequest(new String[]{EsConstant.PRODUCT_INDEX}, searchSourceBuilder);
        return request;


    }

封装查询结果 SearchResultVo 返回给前端页面

/**
     * 2. 封装方法,将查询结果封装到resultVo
     *
     * @param
     * @return
     */
    private SearchResultVo helperBuilderResult(SearchResponse searchResponse, SearchParamVo searchParamVo) {

        SearchResultVo result = new SearchResultVo();
        SearchHits hits = searchResponse.getHits();

        //1. 封装查询到的商品信息
        if (hits.getHits() != null && hits.getHits().length > 0) {
            List<SkuEsModel> skuEsModels = new ArrayList<>();
            for (SearchHit hit : hits) {
                String sourceAsString = hit.getSourceAsString();
                SkuEsModel skuEsModel = JSON.parseObject(sourceAsString, SkuEsModel.class);
                //设置高亮属性
                if (!StringUtils.isEmpty(searchParamVo.getKeyword())) {
                    HighlightField skuTitle = hit.getHighlightFields().get("skuTitle");
                    String highLight = skuTitle.getFragments()[0].string();
                    skuEsModel.setSkuTitle(highLight);
                }
                skuEsModels.add(skuEsModel);
            }
            result.setProducts(skuEsModels);
        }

        //2. 封装分页信息
        //2.1 当前页码
        result.setPageNum(searchParamVo.getPageNum());
        //2.2 总记录数
        long total = hits.getTotalHits().value;
        result.setTotal(total);
        //2.3 总页码
        Integer totalPages = (int) total % EsConstant.PRODUCT_PAGESIZE == 0 ?
                (int) total / EsConstant.PRODUCT_PAGESIZE : (int) total / EsConstant.PRODUCT_PAGESIZE + 1;
        result.setTotalPages(totalPages);
        List<Integer> pageNavs = new ArrayList<>();
        for (int i = 1; i <= totalPages; i++) {
            pageNavs.add(i);
        }
        result.setPageNavs(pageNavs);

        //3. 查询结果涉及到的品牌
        List<SearchResultVo.BrandVo> brandVos = new ArrayList<>();
        Aggregations aggregations = searchResponse.getAggregations();
        //ParsedLongTerms用于接收terms聚合的结果,并且可以把key转化为Long类型的数据
        ParsedLongTerms brandAgg = aggregations.get("brandAgg");
        for (Terms.Bucket bucket : brandAgg.getBuckets()) {
            //3.1 得到品牌id
            Long brandId = bucket.getKeyAsNumber().longValue();

            Aggregations subBrandAggs = bucket.getAggregations();
            //3.2 得到品牌图片
            ParsedStringTerms brandImgAgg = subBrandAggs.get("brandImgAgg");
            String brandImg = brandImgAgg.getBuckets().get(0).getKeyAsString();
            //3.3 得到品牌名字
            Terms brandNameAgg = subBrandAggs.get("brandNameAgg");
            String brandName = brandNameAgg.getBuckets().get(0).getKeyAsString();
            SearchResultVo.BrandVo brandVo = new SearchResultVo.BrandVo(brandId, brandName, brandImg);
            brandVos.add(brandVo);
        }
        result.setBrands(brandVos);

        //4. 查询涉及到的所有分类
        List<SearchResultVo.CatalogVo> catalogVos = new ArrayList<>();
        ParsedLongTerms catalogAgg = aggregations.get("catalogAgg");
        for (Terms.Bucket bucket : catalogAgg.getBuckets()) {
            //4.1 获取分类id
            Long catalogId = bucket.getKeyAsNumber().longValue();
            Aggregations subcatalogAggs = bucket.getAggregations();
            //4.2 获取分类名
            ParsedStringTerms catalogNameAgg = subcatalogAggs.get("catalogNameAgg");
            String catalogName = catalogNameAgg.getBuckets().get(0).getKeyAsString();
            SearchResultVo.CatalogVo catalogVo = new SearchResultVo.CatalogVo(catalogId, catalogName);
            catalogVos.add(catalogVo);
        }
        result.setCatalogs(catalogVos);

        //5 查询涉及到的所有属性
        List<SearchResultVo.AttrVo> attrVos = new ArrayList<>();
        //ParsedNested用于接收内置属性的聚合
        ParsedNested parsedNested = aggregations.get("attrs");
        ParsedLongTerms attrIdAgg = parsedNested.getAggregations().get("attrIdAgg");
        for (Terms.Bucket bucket : attrIdAgg.getBuckets()) {
            //5.1 查询属性id
            Long attrId = bucket.getKeyAsNumber().longValue();

            Aggregations subAttrAgg = bucket.getAggregations();
            //5.2 查询属性名
            ParsedStringTerms attrNameAgg = subAttrAgg.get("attrNameAgg");
            String attrName = attrNameAgg.getBuckets().get(0).getKeyAsString();
            //5.3 查询属性值
            ParsedStringTerms attrValueAgg = subAttrAgg.get("attrValueAgg");
            List<String> attrValues = new ArrayList<>();
            for (Terms.Bucket attrValueAggBucket : attrValueAgg.getBuckets()) {
                String attrValue = attrValueAggBucket.getKeyAsString();
                attrValues.add(attrValue);
                List<SearchResultVo.NavVo> navVos = new ArrayList<>();
            }
            SearchResultVo.AttrVo attrVo = new SearchResultVo.AttrVo(attrId, attrName, attrValues);
            attrVos.add(attrVo);
        }
        result.setAttrs(attrVos);

        // 6. 构建面包屑导航
        List<String> attrs = searchParamVo.getAttrs();
        if (attrs != null && attrs.size() > 0) {
            List<SearchResultVo.NavVo> navVos = attrs.stream().map(attr -> {
                String[] split = attr.split("_");
                SearchResultVo.NavVo navVo = new SearchResultVo.NavVo();
                //6.1 设置属性值
                navVo.setNavValue(split[1]);
                //6.2 查询并设置属性名
                try {
                    R r = productFeignService.info(Long.parseLong(split[0]));
                    if (r.getCode() == 0) {
                        AttrResponseVo attrResponseVo = JSON.parseObject(JSON.toJSONString(r.get("attr")), new TypeReference<AttrResponseVo>() {
                        });
                        navVo.setNavName(attrResponseVo.getAttrName());
                    }
                } catch (Exception e) {
                    log.error("远程调用商品服务查询属性失败", e);
                }
                //6.3 设置面包屑跳转链接
                String queryString = searchParamVo.get_queryString();
                String replace = queryString.replace("&attrs=" + attr, "").replace("attrs=" + attr + "&", "").replace("attrs=" + attr, "");
                navVo.setLink("http://search.gulishop.com/search.html" + (replace.isEmpty() ? "" : "?" + replace));
                return navVo;
            }).collect(Collectors.toList());
            result.setNavs(navVos);
        }
        return result;

    }

二、CompletableFuture异步编排

概念

  • 线程、JUC知识见往期博客:JUC
  • 多个任务可以交给线程池来异步执行,但是多个待执行的任务之间部分有逻辑先后的顺序,因此需要异步编排
  • CompletableFuture< T > 实现 Future< T > 接口,可以通过他完成异步编排,类似前端的Promise,可以一直then(…)
  • Future 是 Java 5 添加的类,用来描述一个异步计算的结果。你可以使用isDone方法检查计
    算是否完成,或者使用get阻塞住调用线程,直到计算完成返回结果,你也可以使用cancel 方法停止任务的执行。
  • 虽然Future以及相关使用方法提供了异步执行任务的能力,但是对于结果的获取却是很不
    方便,只能通过阻塞或者轮询的方式得到任务的结果。阻塞的方式显然和我们的异步编程的
    初衷相违背,轮询的方式又会耗费无谓的 CPU 资源,而且也不能及时地得到计算结果,为
    什么不能用观察者设计模式当计算结果完成及时通知监听者呢?
  • 很多语言,比如 Node.js,采用回调的方式实现异步编程。Java 的一些框架,比如 Netty,自
    己扩展了 Java 的 Future接口,提供了addListener等多个扩展方法;Google guava 也提供了
    通用的扩展 Future;Scala 也提供了简单易用且功能强大的 Future/Promise 异步编程模式。
    作为正统的 Java 类库,是不是应该做点什么,加强一下自身库的功能呢?
  • 在 Java 8 中, 新增加了一个包含 50 个方法左右的类: CompletableFuture,提供了非常强大的
    Future 的扩展功能,可以帮助我们简化异步编程的复杂性,提供了函数式编程的能力,可以
    通过回调的方式处理计算结果,并且提供了转换和组合 CompletableFuture 的方法。
    CompletableFuture 类实现了 Future 接口,所以你还是可以像以前一样通过get方法阻塞或
    者轮询的方式获得结果,但是这种方式不推荐使用。
    CompletableFuture 和 FutureTask 同属于 Future 接口的实现类,都可以获取线程的执行结果
  • 使用Callable < T > + FutureTask< T > 创建线程时,后者也是Future< T > 的子实现接口

谷粒商城项目篇10_分布式高级篇_ES首页检索功能、异步、商品详情_分布式高级_04

使用

  • CompletableFuture 提供了四个静态方法来创建一个异步操作
  • runXxxx 都是没有返回结果的,supplyXxx 都是可以获取返回结果的
  • 可以传入自定义的线程池,否则就用默认的线程池

谷粒商城项目篇10_分布式高级篇_ES首页检索功能、异步、商品详情_ES检索_05

1.whenComplete完成时回调

  • 可以在异步任务的下面写业务代码,也可以放到成功回调里面,因为 CompletableFuture.runAsync()CompletableFuture.supplyAsync() 执行完之后还会返回completableFuture接着调方法
  • 谷粒商城项目篇10_分布式高级篇_ES首页检索功能、异步、商品详情_ES检索_06

  • whenComplete 可以处理正常和异常的计算结果,exceptionally 处理异常情况。
  • whenComplete 和 whenCompleteAsync 的区别:
  • whenComplete:是执行当前任务的线程执行继续执行 whenComplete 的任务。
  • whenCompleteAsync:是执行把 whenCompleteAsync 这个任务继续提交给线程池来进行执行。
  • 方法不以 Async 结尾,意味着 Action 使用相同的线程执行,而 Async 可能会使用其他线程执行(如果是使用相同的线程池,也可能会被同一个线程选中执行)

举栗

谷粒商城项目篇10_分布式高级篇_ES首页检索功能、异步、商品详情_谷粒商城_07

public static void main(String[] args) throws ExecutionException, InterruptedException {

	CompletableFuture future = CompletableFuture.supplyAsync(new Supplier<Object>() {
		@Override
		public Object get() {
			System.out.println(Thread.currentThread().getName() + "completableFuture");
			int i = 10 / 0;
			return 1024;
		}
	}).whenComplete(new BiConsumer<Object, Throwable>() {
		@Override
		public void accept(Object o, Throwable throwable) {
			System.out.println("-------o=" + o.toString());
			System.out.println("-------throwable=" + throwable);
		}
	}).exceptionally(new Function<Throwable, Object>() {
		@Override
		public Object apply(Throwable throwable) {
			System.out.println("throwable=" + throwable);
			return 6666;
		}
	});
	System.out.println(future.get());
}

注意前面的只能感知异常,如果无异常不能对原结果在进行处理

谷粒商城项目篇10_分布式高级篇_ES首页检索功能、异步、商品详情_ES检索_08

2.thenApply线程串化方法

概念

  • 简单理解就是将线程顺序串起来,一个线程可能依赖另外一个线程的结果
  • 当前线程执行完接着可以执行另外一个任务,当前线程的结果可被消费,消费之后结果可以接着向下传递
  • 谷粒商城项目篇10_分布式高级篇_ES首页检索功能、异步、商品详情_谷粒商城_09

  • thenApply 方法:当一个线程依赖另一个线程时,获取上一个任务返回的结果,并返回当前
    任务的返回值。
  • thenAccept 方法:消费处理结果。接收任务的处理结果,并消费处理,无返回结果。
  • thenRun 方法:只要上面的任务执行完成,就开始执行 thenRun,只是处理完任务后,执行 thenRun 的后续操作。
  • 带有 Async 默认是异步执行的。同之前。

以上都要前置任务成功完成。Function<? super T,? extends U>

  • T:上一个任务返回结果的类型
  • U:当前任务的返回值类型

3.两任务组合 - 都要完成

两个任务必须都完成,触发该任务。

  • thenCombine:组合两个 future,获取两个 future 的返回结果,并返回当前任务的返回值
  • thenAcceptBoth:组合两个 future,获取两个 future 任务的返回结果,然后处理任务,没有返回值。
  • runAfterBoth:组合两个 future,不需要获取 future 的结果,只需两个 future 处理完任务后,处理该任务

4.两任务组合 - 一个完成

当两个任务中,任意一个 future 任务完成的时候,执行任务。

  • applyToEither:两个任务有一个执行完成,获取它的返回值,处理任务并有新的返回值。
  • acceptEither:两个任务有一个执行完成,获取它的返回值,处理任务,没有新的返回值。
  • runAfterEither:两个任务有一个执行完成,不需要获取 future 的结果,处理任务,也没有返
    回值

5.多任务组合

  • allOf:等待所有任务完成
  • anyOf:只要有一个任务完成

三、商品详情

点击商品进入详情页,设置网关路由

谷粒商城项目篇10_分布式高级篇_ES首页检索功能、异步、商品详情_ES检索_10


复制前端静态页面,修改页面,编写后端查询代码

谷粒商城项目篇10_分布式高级篇_ES首页检索功能、异步、商品详情_分布式高级_11

1.完成异步编排

自定义线程池

ThreadPoolConfigProperties 绑定properties配置文件,后续可以在配置文件设置线程池参数

package henu.soft.xiaosi.product.config;

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


@ConfigurationProperties(prefix = "gulishop.thread")
@Component
@Data
public class ThreadPoolConfigProperties {
    private int corePoolSize;
    private int maxPoolSize;
    private long keepAliveTime;

}

MyThreadPoolConfig 自定义线程池

package henu.soft.xiaosi.product.config;


import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.util.concurrent.LinkedBlockingDeque;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

@Configuration
//@EnableConfigurationProperties(ThreadPoolConfigProperties.class)
public class MyThreadPoolConfig {
    @Bean
    public ThreadPoolExecutor threadPoolExecutor(ThreadPoolConfigProperties properties) {
        return new ThreadPoolExecutor(
                properties.getCorePoolSize(),
                properties.getMaxPoolSize(),
                properties.getKeepAliveTime(),
                TimeUnit.SECONDS,
                new LinkedBlockingDeque<>(10000),
                new ThreadPoolExecutor.AbortPolicy()
        );
    }
}
# 自定义线程池
gulishop.thread.corePoolSize=10
gulishop.thread.maxPoolSize=20
gulishop.thread.keepAliveTime=300

使用

@Override
    public SkuItemVo item(Long skuId) {
        SkuItemVo skuItemVo = new SkuItemVo();
        CompletableFuture<SkuInfoEntity> infoFuture = CompletableFuture.supplyAsync(() -> {
            //1、sku基本信息的获取  pms_sku_info
            SkuInfoEntity skuInfoEntity = this.getById(skuId);
            skuItemVo.setInfo(skuInfoEntity);
            return skuInfoEntity;
        }, executor);

        //2、sku的图片信息    pms_sku_images
        CompletableFuture<Void> imageFuture = CompletableFuture.runAsync(() -> {
            List<SkuImagesEntity> skuImagesEntities = skuImagesService.list(new QueryWrapper<SkuImagesEntity>().eq("sku_id", skuId));
            skuItemVo.setImages(skuImagesEntities);
        }, executor);


        //3、获取spu的销售属性组合-> 依赖1 获取spuId
        CompletableFuture<Void> saleFuture = infoFuture.thenAcceptAsync((info) -> {
            List<SkuItemSaleAttrVo> saleAttrVos = skuSaleAttrValueService.listSaleAttrs(info.getSpuId());
            skuItemVo.setSaleAttr(saleAttrVos);
        }, executor);


        //4、获取spu的介绍-> 依赖1 获取spuId
        CompletableFuture<Void> descFuture = infoFuture.thenAcceptAsync((info) -> {
            SpuInfoDescEntity byId = spuInfoDescService.getById(info.getSpuId());
            skuItemVo.setDesc(byId);
        }, executor);


        //5、获取spu的规格参数信息-> 依赖1 获取spuId catalogId
        CompletableFuture<Void> attrFuture = infoFuture.thenAcceptAsync((info) -> {
            List<SpuItemAttrGroupVo> spuItemAttrGroupVos=productAttrValueService.getProductGroupAttrsBySpuId(info.getSpuId(), info.getCatalogId());
            skuItemVo.setGroupAttrs(spuItemAttrGroupVos);
        }, executor);

//        //6、秒杀商品的优惠信息
//        CompletableFuture<Void> seckFuture = CompletableFuture.runAsync(() -> {
//            R r = seckillFeignService.getSeckillSkuInfo(skuId);
//            if (r.getCode() == 0) {
//                SeckillSkuVo seckillSkuVo = r.getData(new TypeReference<SeckillSkuVo>() {
//                });
//                long current = System.currentTimeMillis();
//                //如果返回结果不为空且活动未过期,设置秒杀信息
//                if (seckillSkuVo != null&¤t<seckillSkuVo.getEndTime()) {
//                    skuItemVo.setSeckillSkuVo(seckillSkuVo);
//                }
//            }
//        }, executor);

        //等待所有任务执行完成
        try {
            CompletableFuture.allOf(imageFuture, saleFuture, descFuture, attrFuture).get();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }

        return skuItemVo;
    }


    /**
     * 商品详情页
     *
     * @param skuId
     * @return
     */
/*
    @Override
    public SkuItemVo item(Long skuId) {
        SkuItemVo skuItemVo = new SkuItemVo();
        //1、sku基本信息的获取  pms_sku_info
        SkuInfoEntity skuInfoEntity = this.getById(skuId);
        skuItemVo.setInfo(skuInfoEntity);
        Long spuId = skuInfoEntity.getSpuId();
        Long catalogId = skuInfoEntity.getCatalogId();


        //2、sku的图片信息    pms_sku_images
        List<SkuImagesEntity> skuImagesEntities = skuImagesService.list(new QueryWrapper<SkuImagesEntity>().eq("sku_id", skuId));
        skuItemVo.setImages(skuImagesEntities);

        //3、获取spu的销售属性组合-> 依赖1 获取spuId
        List<SkuItemSaleAttrVo> saleAttrVos = skuSaleAttrValueService.listSaleAttrs(spuId);
        skuItemVo.setSaleAttr(saleAttrVos);

        //4、获取spu的介绍-> 依赖1 获取spuId
        SpuInfoDescEntity byId = spuInfoDescService.getById(spuId);
        skuItemVo.setDesc(byId);

        //5、获取spu的规格参数信息-> 依赖1 获取spuId catalogId
        List<SpuItemAttrGroupVo> spuItemAttrGroupVos = productAttrValueService.getProductGroupAttrsBySpuId(spuId, catalogId);
        skuItemVo.setGroupAttrs(spuItemAttrGroupVos);
        //TODO 6、秒杀商品的优惠信息

        return skuItemVo;
    }
    */


标签:10,searchParamVo,String,异步,private,线程,attrs,new,ES
From: https://blog.51cto.com/u_15790456/6569645

相关文章

  • typescript中 == 和 === 的区别
    在TypeScript中,==和===是用于比较两个值是否相等的操作符。 ==是相等比较操作符,它在比较值时进行隐式类型转换。它会尝试将两个操作数转换为相同类型,然后再进行比较。这种隐式类型转换可能会导致一些意想不到的结果。例如:console.log(1=="1");//trueconsole.log(t......
  • AtCoder Beginner Contest 306(ABC 306) E~F补题
    E题目大意给定数字$k$,规定数组$A$的函数$f(A)$:$A$数组前$k$大数字的和如$A=[1,3,7,~4]$,$k=2$,则$f(A)=7+4=11$现在给定最大数组长度$n$,有$q$组操作,每次将第$x$个数字修改为$y$,输出每次操作后的$f(A)$其中$0<k\leqn\leq5\times10^5,~q\leq5\times......
  • (2023.6.28)ls1028相关
    1.核心板升级为V1.3版,解决MDIO总线不稳定问题?现象是什么2.PHY芯片由AR8031和QCA8075更换为YT8521和YT86143.因核心板加密芯片A1006L原厂设计BUG,在高温和低温环境下可能会出现加密芯片不能正常工作,加密失败导致核心板不能启动。更换核心板加密为AL10074.外部的两个phy的md......
  • DevExpress GroupControl面板收缩展开
    1#regionGroupControl面板缩进展开绑定2privatevoidBindGroupControl(DevExpress.XtraEditors.GroupControlgroupControl)3{4DevExpress.XtraEditors.ButtonsPanelControl.ButtonImageOptionsbuttonImageOptions1=newDevExpres......
  • DevExpress WPF Scheduler组件,快速构建性能优异的调度管理器!(上)
    无论您在WPF项目中是需要Outlook样式的调度程序,还是需要时间表或议程视图来向最终用户展示信息,DevExpressWPF Scheduler都提供了数十个选项,如集成的日程对话框等,因此用户可以快速构建下一个伟大的调度管理器。DevExpressWPF拥有120+个控件和库,将帮助您交付满足甚至超出企业需......
  • 读取Devexpress内部的图标
    引用DevExpress.Images.vXX.1.dll文件,代码获取图标方法:ImageCollectionimageCollection=newImageCollection();imageCollection.Images.Add(DevExpress.Images.ImageResourceCache.Default.GetImage("images/actions/add_16x16.png"));DevExpress.Images.ImageResourc......
  • RHEL 10 不包含 X.org 显示服务器
    导读红帽企业 Linux 发行版RHEL10将不再包含X.orgServer。官方文档称,X.org 显示服务器已被弃用,并将在以后的主RHEL发行版本(从RHEL10开始)中删除。目前的RHEL9则仍包含X.org显示服务器,并会提供10年的支持,持续到2032年。红帽没有解释弃用X.org的原......
  • kubernetes探针及应用场景
    kubernetes提供了哪几种探针?分别有什么功能?应用场景有哪些?LivenessProbe:容器存活性检查,用于判断容器是否健康。功能:如果LivenessProbe探针探测到容器不健康,则kubelet将删除该容器,并根据容器的重启策略做相应的处理。 如果一个容器不包含LivenessProbe探针,那么kubele......
  • chrome.runtime.onMessage.addListener sendResponse
    Ifmultiplepagesarelisteningfor onMessage events,onlythefirsttocall sendResponse() foraparticulareventwillsucceedinsendingtheresponse.Allotherresponsestothateventwillbeignored.如果多个页面正在监听onMessage事件,则只有第一个为特定......
  • 【笔记】Inception-v4, Inception-ResNet and the Impact of Residual Connections on
    论文:https://arxiv.org/abs/1602.07261代码:https://github.com/gu-yan/mlAlgorithms/blob/master/inception_impl/Inception_resnet_v2_temsorflow.py先贴出模型:InceptionV4:https://raw.githubusercontent.com/titu1994/Inception-v4/master/Architectures/Inception-v4.pngInecpt......