springboot整合Elasticsearch-Rest-Client

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>2.5.5</version><relativePath/> <!-- lookup parent from repository --></parent><groupId>com.atlinxi.gulimall</groupId><artifactId>gulimall-search</artifactId><version>0.0.1-SNAPSHOT</version><name>gulimall-search</name><description>elasticsearch检索服务</description><properties><java.version>1.8</java.version><elasticsearch.version>7.4.2</elasticsearch.version><spring-cloud.version>2020.0.4</spring-cloud.version></properties><dependencies><dependency><groupId>com.atlinxi.gulimall</groupId><artifactId>gulimall-common</artifactId><version>0.0.1-SNAPSHOT</version></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>org.elasticsearch.client</groupId><artifactId>elasticsearch-rest-high-level-client</artifactId><version>7.4.2</version></dependency><dependency><groupId>com.alibaba</groupId><artifactId>fastjson</artifactId><version>1.2.47</version></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency></dependencies><dependencyManagement><dependencies><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-dependencies</artifactId><version>${spring-cloud.version}</version><type>pom</type><scope>import</scope></dependency></dependencies></dependencyManagement><build><plugins><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId></plugin></plugins></build></project>
spring.cloud.nacos.discovery.server-addr=127.0.0.1:8848
spring.application.name=gulimall-search
package com.atlinxi.gulimall.search.config;import org.apache.http.HttpHost;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestClient;
import org.elasticsearch.client.RestHighLevelClient;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;/*** es配置类,给容器中注入一个RestHighLevelClient*/
@Configuration
public class GulimallElasticSearchConfig {// 后端访问es的时候,出于安全考虑,可以携带一个请求头// 现在暂时不用public static final RequestOptions COMMON_OPTIONS;static {RequestOptions.Builder builder = RequestOptions.DEFAULT.toBuilder();
//        builder.addHeader("Authorization", "Bearer " + TOKEN);
//        builder.setHttpAsyncResponseConsumerFactory(
//                new HttpAsyncResponseConsumerFactory
//                        .HeapBufferedResponseConsumerFactory(30 * 1024 * 1024 * 1024));COMMON_OPTIONS = builder.build();}@Beanpublic RestHighLevelClient esRestClient(){RestHighLevelClient client = new RestHighLevelClient(RestClient.builder(new HttpHost("192.168.56.10", 9200, "http")
//                        new HttpHost("localhost", 9201, "http")));return client;}}
package com.atlinxi.gulimall.search;import com.alibaba.fastjson.JSON;
import com.atlinxi.gulimall.search.config.GulimallElasticSearchConfig;
import lombok.Data;
import lombok.ToString;
import org.elasticsearch.action.index.IndexRequest;
import org.elasticsearch.action.index.IndexResponse;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.common.xcontent.XContentType;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.aggregations.AggregationBuilders;
import org.elasticsearch.search.aggregations.Aggregations;
import org.elasticsearch.search.aggregations.bucket.terms.Terms;
import org.elasticsearch.search.aggregations.bucket.terms.TermsAggregationBuilder;
import org.elasticsearch.search.aggregations.metrics.Avg;
import org.elasticsearch.search.aggregations.metrics.AvgAggregationBuilder;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;import java.io.IOException;@SpringBootTest
class GulimallSearchApplicationTests {@AutowiredRestHighLevelClient restHighLevelClient;@Testpublic void searchData()throws IOException{// 1. 创建检索请求SearchRequest searchRequest = new SearchRequest();// 1.1 指定索引searchRequest.indices("bank");// 指定DSL,检索条件SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();// 1.2 构造检索条件// 所有的函数名都对应原生es DSL语句
//        searchSourceBuilder.query();
//        searchSourceBuilder.from();
//        searchSourceBuilder.size();
//        searchSourceBuilder.aggregation();searchSourceBuilder.query(QueryBuilders.matchQuery("address","mill"));// 按照年龄的值分布进行聚合TermsAggregationBuilder ageAgg = AggregationBuilders.terms("ageAgg").field("age").size(10);searchSourceBuilder.aggregation(ageAgg);// 计算平均薪资AvgAggregationBuilder balanceAvg = AggregationBuilders.avg("balanceAvg").field("balance");searchSourceBuilder.aggregation(balanceAvg);//        System.out.println(searchSourceBuilder.toString());searchRequest.source(searchSourceBuilder);// 2. 执行检索SearchResponse searchResponse = restHighLevelClient.search(searchRequest, GulimallElasticSearchConfig.COMMON_OPTIONS);// 3. 分析结果 searchResponse
//        System.out.println(searchResponse.toString());// 3.1 获取所有查到的数据SearchHits hits = searchResponse.getHits();SearchHit[] searchHits = hits.getHits();for (SearchHit searchHit : searchHits) {//            searchHit.getIndex();String string = searchHit.getSourceAsString();Account account = JSON.parseObject(string, Account.class);
//            accountGulimallSearchApplicationTests.Account(account_number=970, balance=19648, firstname=Forbes, lastname=Wallace, age=28, gender=M, address=990 Mill Road, employer=Pheast, email=forbeswallace@pheast.com, city=Lopezo, state=AK)
//            accountGulimallSearchApplicationTests.Account(account_number=136, balance=45801, firstname=Winnie, lastname=Holland, age=38, gender=M, address=198 Mill Lane, employer=Neteria, email=winnieholland@neteria.com, city=Urie, state=IL)
//            accountGulimallSearchApplicationTests.Account(account_number=345, balance=9812, firstname=Parker, lastname=Hines, age=38, gender=M, address=715 Mill Avenue, employer=Baluba, email=parkerhines@baluba.com, city=Blackgum, state=KY)
//            accountGulimallSearchApplicationTests.Account(account_number=472, balance=25571, firstname=Lee, lastname=Long, age=32, gender=F, address=288 Mill Street, employer=Comverges, email=leelong@comverges.com, city=Movico, state=MT)System.out.println("account" + account);}// 3.2 获取这次检索到的分析信息Aggregations aggregations = searchResponse.getAggregations();
//        for (Aggregation aggregation : aggregations.asList()) {//            System.out.println(aggregation.getName());
//        }Terms ageAgg1 = aggregations.get("ageAgg");for (Terms.Bucket bucket : ageAgg1.getBuckets()) {String keyAsString = bucket.getKeyAsString();
//            年龄38==>2
//            年龄28==>1
//            年龄32==>1System.out.println("年龄" + keyAsString + "==>" + bucket.getDocCount());}Avg balanceAvg1 = aggregations.get("balanceAvg");// 平均薪资:25208.0System.out.println("平均薪资:" + balanceAvg1.getValue());}@ToString@Datastatic class Account{private int account_number;private int balance;private String firstname;private String lastname;private int age;private String gender;private String address;private String employer;private String email;private String city;private String state;}/*** 存储数据*/@Testvoid indexData() throws IOException {// 添加数据有多种方式,例如hashmap、直接将json粘在这儿IndexRequest request = new IndexRequest("users");request.id("1");
//        request.source("userName","zhangsan","age",12,"gender","男");//        String jsonString = "{" +
//                "\"user\":\"kimchy\"," +
//                "\"postDate\":\"2013-01-30\"," +
//                "\"message\":\"trying out Elasticsearch\"" +
//                "}";
//        request.source(jsonString, XContentType.JSON);User user = new User();user.setUserName("zs");user.setAge(12);user.setGender("man");String jsonString = JSON.toJSONString(user);request.source(jsonString, XContentType.JSON);// 执行保存/更新操作IndexResponse index = restHighLevelClient.index(request, GulimallElasticSearchConfig.COMMON_OPTIONS);// 提取有用的响应数据System.out.println(index);}@Dataclass User{private String userName;private String gender;private Integer age;}@Testvoid contextLoads() {System.out.println(restHighLevelClient);}}

sku在es中的存储模型分析

这样的结构比较容易检索,但是会产生冗余字段

多个sku是共享一个spu的,所以以下结构中多个sku的attrs是冗余的,

假设我们有100万个商品,平均属性有20个(2kb),那么冗余数据有2000m,也就是2g,那么整个商城的商品也就多了2g,加一个内存条就ok了

{skuId:1,skuTitle:华为XX,price:998,saleCount:99,attrs:[{尺寸:5寸},{cpu:高通},{分辨率:高清}]
}

这样的结构,attrs只存了一次,没有了冗余数据,也容易检索

但是这样会有一个极大的问题,

搜索小米,会出来粮食,手机,电器(很多sku都会包含小米),会检索出这些spu所有的规格属性,

假设小米的商品有10000个,涉及到4000个spu,4000个spu对应的所有属性,

esClient查询属性的时候,需要携带4000个spuId,因为是Long型数据,每一个都占8个字节,那么查询一次属性需要传递32kb的数据,

假设并发是1万,数据则是320m,如果面对百万并发,则是32G

最终面对百万并发,光传输4000个spuId,数据就高达32G,其他不说,光网络阻塞就非常长

sku索引{skuId:1,spuId:11,xxxxx
}attr索引{spuId:11,attrs:[{尺寸:5寸},{cpu:高通},{分辨率:高清}]
}

总之一句话,空间和时间总是不能二者兼得,第一种浪费空间节省时间,第二种浪费时间节省空间,最终我们选择的是时间

PUT product
{"mappings": {"properties": {"skuId": {"type": "long"},"spuId": {"type": "keyword"},"skuTitle": {"type": "text","analyzer": "ik_smart"},"skuPrice": {"type": "keyword"},"skuImg": {"type": "keyword","index": false,"doc_values": false},"saleCount": {"type": "long"},"hasStock": {"type": "boolean"},"hotScore": {"type": "long"},"brandId": {"type": "long"},"catalogId": {"type": "long"},"brandName": {"type": "keyword","index": false,"doc_values": false},"brandImg": {"type": "keyword","index": false,"doc_values": false},"catalogName": {"type": "keyword","index": false,"doc_values": false},"attrs": {"type": "nested","properties": {"attrId": {"type": "long"},"attrName": {"type": "keyword","index": false,"doc_values": false},"attrValue": {"type": "keyword"}}}}}
}

商品上架

spuInfoServiceImpl

/*** 商品上架* @param spuId*/@Transactional@Overridepublic void up(Long spuId) {// 1. 组装需要的数据// 1.1 查出当前spuId对应的所有sku信息List<SkuInfoEntity> skus = skuInfoService.getSkusBySpuId(spuId);List<Long> skuIdList = skus.stream().map(SkuInfoEntity::getSkuId).collect(Collectors.toList());// 4. 查询当前sku可以被检索的所有规格属性List<ProductAttrValueEntity> baseAttrs = attrValueService.baseAttrlistforspu(spuId);List<Long> attrIds = baseAttrs.stream().map(attr -> {return attr.getAttrId();}).collect(Collectors.toList());List<Long> searchAttrIds = attrService.selectSearchAttrs(attrIds);Set<Long> idSet = new HashSet<>(searchAttrIds);List<SkuEsModel.Attrs> attrsList = baseAttrs.stream().filter(item -> {return idSet.contains(item.getAttrId());}).map(item -> {SkuEsModel.Attrs attrs1 = new SkuEsModel.Attrs();BeanUtils.copyProperties(item, attrs1);return attrs1;}).collect(Collectors.toList());Map<Long, Boolean> stockMap = null;try {// 1. 发送远程调用,库存系统查询是否有库存R skusHasStock = wareFeignService.getSkusHasStock(skuIdList);// TypeReference 权限修饰符是 protected,所以我们要写成内部类的方式List<SkuHasStockVo> data = skusHasStock.getData("data",new TypeReference<List<SkuHasStockVo>>(){});stockMap = data.stream().collect(Collectors.toMap(SkuHasStockVo::getSkuId, item -> item.getHasStock()));}catch (Exception e){log.error("库存服务查询异常:",e);}// 1.2 封装每个sku的信息Map<Long, Boolean> finalstockMap = stockMap;List<SkuEsModel> upProducts = skus.stream().map(sku -> {SkuEsModel skuEsModel = new SkuEsModel();BeanUtils.copyProperties(sku, skuEsModel);// skuPrice,skuImg,hasStock,hotScore,brandName,brandImg,catalogName// Attrs attrId,attrName,attrValueskuEsModel.setSkuPrice(sku.getPrice());skuEsModel.setSkuImg(sku.getSkuDefaultImg());// 设置库存信息skuEsModel.setHasStock(finalstockMap == null ? true : finalstockMap.get(sku.getSkuId()));// todo 2. 热度评分 先默认设置0 但实际情况应该是后台可控的,比较复杂的操作skuEsModel.setHotScore(0L);// 3. 查询品牌和分类的名字信息BrandEntity brand = brandService.getById(skuEsModel.getBrandId());skuEsModel.setBrandName(brand.getName());skuEsModel.setBrandImg(brand.getLogo());CategoryEntity categoryEntity = categoryService.getById(skuEsModel.getCatalogId());skuEsModel.setCatalogName(categoryEntity.getName());// 设置检索属性skuEsModel.setAttrs(attrsList);return skuEsModel;}).collect(Collectors.toList());// todo 5. 将数据发送给es进行保存 gulimall-searchR r = searchFeignService.productStatusUp(upProducts);// 远程调用成功if (r.getCode()==0){// 修改当前spu状态baseMapper.updateSpuStatus(spuId, ProductConstant.StatusEnum.SPU_UP.getCode());}else {// todo 重复调用,接口幂等性,重试机制}}

spuInfoServiceImpl是总的流程,下面都是它调用到的函数

比较简单的函数

// SkuInfoServiceImpl
public List<SkuInfoEntity> getSkusBySpuId(Long spuId) {List<SkuInfoEntity> list = this.list(new QueryWrapper<SkuInfoEntity>().eq("spu_id",spuId));return list;}// ProductAttrValueServiceImpl
public List<ProductAttrValueEntity> baseAttrlistforspu(Long spuId) {List<ProductAttrValueEntity> entities = this.baseMapper.selectList(new QueryWrapper<ProductAttrValueEntity>().eq("spu_id", spuId));return entities;}// AttrServiceImpl
@Override
public List<Long> selectSearchAttrs(List<Long> attrIds) {return baseMapper.selectSearchAttrIds(attrIds);}
<select id="selectSearchAttrIds" resultType="java.lang.Long">select attr_id from pms_attr where attr_id in<foreach collection="attrIds" separator="," item="id" open="(" close=")">#{id}</foreach>and search_type = 1</select>// WareSkuServiceImpl
public List<SkuHasStockVo> getSkusHasStock(List<Long> skuIds) {List<SkuHasStockVo> collect = skuIds.stream().map(skuId -> {SkuHasStockVo vo = new SkuHasStockVo();// 查询当前sku的总库存量Long count = this.baseMapper.getSkuStock(skuId);vo.setSkuId(skuId);vo.setHasStock(count==null?false:count>0);return vo;}).collect(Collectors.toList());return collect;}// SpuInfoDao.xml
<update id="updateSpuStatus">update pms_spu_info set publish_status=#{code},update_time=NOW() where id = #{spuId}</update>

search模块

package com.atlinxi.gulimall.search.controller;import com.atlinxi.common.exception.BizCodeEnume;
import com.atlinxi.common.to.es.SkuEsModel;
import com.atlinxi.common.utils.R;
import com.atlinxi.gulimall.search.service.ProductSaveService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;import java.io.IOException;
import java.util.List;@Slf4j
@RestController
@RequestMapping("/search/save")
public class ElasticSaveController {@Autowiredprivate ProductSaveService productSaveService;/*** 上架商品*/@PostMapping("/product")public R productStatusUp(@RequestBody List<SkuEsModel> skuEsModels){boolean b = false;try {b = productSaveService.productStatusUp(skuEsModels);} catch (IOException e) {log.error("ElasticSaveController商品上架错误:{}",e);return R.error(BizCodeEnume.PRODUCT_UP_EXCEPTION.getCode(), BizCodeEnume.PRODUCT_UP_EXCEPTION.getMessage());}if (!b){return R.ok();}else {return R.error();}}
}package com.atlinxi.gulimall.search.service.impl;import com.alibaba.fastjson.JSON;
import com.atlinxi.common.to.es.SkuEsModel;
import com.atlinxi.gulimall.search.config.GulimallElasticSearchConfig;
import com.atlinxi.gulimall.search.constant.EsConstant;
import com.atlinxi.gulimall.search.service.ProductSaveService;
import lombok.extern.slf4j.Slf4j;
import org.elasticsearch.action.bulk.BulkRequest;
import org.elasticsearch.action.bulk.BulkResponse;
import org.elasticsearch.action.index.IndexRequest;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.common.xcontent.XContentType;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;import java.io.IOException;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;@Service
@Slf4j
public class ProductSaveServiceImpl implements ProductSaveService {@Autowiredprivate RestHighLevelClient restHighLevelClient;/**** @param skuEsModels* @return*/@Overridepublic boolean productStatusUp(List<SkuEsModel> skuEsModels) throws IOException {// 保存到es// 1. 建立索引 product 建立好映射关系(kibana操作)// 2. 给es中保存这些数据BulkRequest bulkRequest = new BulkRequest();for (SkuEsModel skuEsModel : skuEsModels) {IndexRequest indexRequest = new IndexRequest(EsConstant.Product_INDEX);indexRequest.id(skuEsModel.getSkuId().toString());String s = JSON.toJSONString(skuEsModel);indexRequest.source(s, XContentType.JSON);bulkRequest.add(indexRequest);}BulkResponse bulk = restHighLevelClient.bulk(bulkRequest, GulimallElasticSearchConfig.COMMON_OPTIONS);// todo 如果批量错误,处理错误boolean b = bulk.hasFailures();List<String> collect = Arrays.stream(bulk.getItems()).map(item -> {return item.getId();}).collect(Collectors.toList());log.info("商品上架成功:{}",collect);return b;}
}

封装数据的类

/*** Copyright (c) 2016-2019 人人开源 All rights reserved.** https://www.renren.io** 版权所有,侵权必究!*/package com.atlinxi.common.utils;import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.TypeReference;
import org.apache.http.HttpStatus;import java.util.HashMap;
import java.util.Map;/*** 返回数据** @author Mark sunlightcs@gmail.com*** 老师是用泛型的方式封装的data,* 在feign接口中返回泛型类时,由于java的泛型机制,在实例化之前无法得到具体的类型 ,* 因此,虽然服务提供方返回的是具体实例的数据,但是在客户端decode时,无法转化为具体的类。** 上面的话看不太懂,翻译成人话就是,feign在被远程调用返回结果的时候,泛型是null** 因为R继承了HashMap,我们写的所有私有属性都没用,只能存键值对,具体原因未知,** public class R<T> extends HashMap<String, Object> {*    private static final long serialVersionUID = 1L;**     private T data;**   public T getData() {*       return this.data;*        }***  public void setData(T data) {*      this.data = data;*    }*/
public class R extends HashMap<String, Object> {private static final long serialVersionUID = 1L;public R setData(Object data){put("data",data);return this;}//利用fastjson进行反序列化public <T> T getData(TypeReference<T> typeReference) {Object data = get("data");  //默认是mapString jsonString = JSON.toJSONString(data);T t = JSON.parseObject(jsonString, typeReference);return t;}//利用fastjson进行反序列化public <T> T getData(String key,TypeReference<T> typeReference) {Object data = get(key);   //默认是mapString jsonString = JSON.toJSONString(data);T t = JSON.parseObject(jsonString, typeReference);return t;}public R() {put("code", 0);put("msg", "success");}public static R error() {return error(HttpStatus.SC_INTERNAL_SERVER_ERROR, "未知异常,请联系管理员");}public static R error(String msg) {return error(HttpStatus.SC_INTERNAL_SERVER_ERROR, msg);}public static R error(int code, String msg) {R r = new R();r.put("code", code);r.put("msg", msg);return r;}public static R ok(String msg) {R r = new R();r.put("msg", msg);return r;}public static R ok(Map<String, Object> map) {R r = new R();r.putAll(map);return r;}public static R ok() {return new R();}public R put(String key, Object value) {super.put(key, value);return this;}public int getCode(){return (Integer) this.get("code");}
}

枚举

package com.atlinxi.common.constant;public class ProductConstant {public enum AttrEnum{ATTR_TYPE_BASE(1,"基本属性"),ATTR_TYPE_SALE(0,"销售属性");private int code;private String msg;AttrEnum(int code, String msg) {this.code = code;this.msg = msg;}public int getCode() {return code;}public void setCode(int code) {this.code = code;}public String getMsg() {return msg;}public void setMsg(String msg) {this.msg = msg;}}public enum StatusEnum{NEW_SPU(0,"新建"),SPU_UP(1,"商品上架"),SPU_DOWN(2,"商品下架");private int code;private String msg;StatusEnum(int code, String msg) {this.code = code;this.msg = msg;}public int getCode() {return code;}public void setCode(int code) {this.code = code;}public String getMsg() {return msg;}public void setMsg(String msg) {this.msg = msg;}}
}

谷粒商城十elasticsearch搜索服务及商品上架相关推荐

  1. 谷粒商城高级篇(36)——商品上架之上传数据到Elasticsearch

    商品上架之上传数据到Elasticsearch 功能需求分析 分析-怎么设计存储结构来保存数据 空间换时间 时间换空间 最终方案-存储结构 关于 nested 类型 商品上架功能实现 guimall- ...

  2. 谷粒商城十五商品详情CompletableFuture异步编排

    多线程异步任务的问题 例如a,b,c三个异步任务,不是随机运行就可以,它们还有一定的关系,c需要等待a的返回结果,b不需要等待谁的结果. 当异步任务产生一些关系和顺序之后,我们要编排好它们的关系进行调 ...

  3. 谷粒商城--整合Elasticsearch和商品的上架

    整合Elasticsearch和商品的上架 一.整合ES ES常用概念 索引,类型,文档是什么? 倒排索引 相关度分数score的计算 安装ES和Kibana 快速安装 ES kibana 初步检索_ ...

  4. 【谷粒商城】ElasticSearch、上架与检索

    笔记-基础篇-1(P1-P28):https://blog.csdn.net/hancoder/article/details/106922139 笔记-基础篇-2(P28-P100):https:/ ...

  5. 2023最新谷粒商城笔记之检索服务篇(全文总共13万字,超详细)

    检索服务 这里有关于检索的功能我们基于微服务架构应该抽出来单独作为一个服务运行,所以新建gulimall-search服务并注册到nacos中,以便网关服务可以监控到检索服务并将检索的请求转发给检索服 ...

  6. 谷粒商城笔记+踩坑(9)——上架商品spu到ES索引库

    导航: 谷粒商城笔记+踩坑汇总篇 目录 1.ES回顾 2.ES整合商品上架 2.1.分析 2.2.创建sku的es索引库 2.2.1.两种索引库设计方案分析 2.2.2.最终选用的索引库方案,nest ...

  7. 谷粒商城二十三秒杀服务

    秒杀是每一个电商系统中非常重要的模块,商家会不定期的发布一些低价商品,发布到秒杀系统中,秒杀系统的商品一般会放到首页展示,这样就可以引导用户购买商品. 秒杀的购买流程和普通的购买流程最大的特点就是瞬时 ...

  8. 谷粒商城十二性能压测及优化

    在分布式开发中,我们将每一一种服务都抽取成一个独立的模块,微服务模块在真正的上线之前,甚至是上线以后,我们都要进行压力测试,才能投入正常的使用. 压力测试是为了我们的系统在当前软硬件环境下,最大的负荷 ...

  9. 【谷粒商城高级篇】商城业务:商品检索

    谷粒商城笔记合集 分布式基础篇 分布式高级篇 高可用集群篇 ===简介&环境搭建=== ===Elasticsearch=== 项目简介与分布式概念(第一.二章) Elasticsearch: ...

最新文章

  1. 嵌入式系统自动使能alias
  2. Python 模块之 time datetime
  3. 防火墙(11)——防止爬虫过多访问(ping)我们的服务器
  4. spring cloud consul整合
  5. CentOS 7 安装 配置 Nginx + PHP
  6. Python中turtle模块画图
  7. 阶段3 3.SpringMVC·_06.异常处理及拦截器_6 SpringMVC拦截器之拦截器入门代码
  8. window11在注册表修改用户名后登陆不了账户
  9. 北京哪里有军品店?_爱问知识人
  10. 微信技巧:教你几招导出微信小视频
  11. Java SSLSocket的使用
  12. 问卷调查:vue element动态生成表单、表单校验以及表单提交
  13. 全网最全面工作流引擎Flowable完整教程之多实例会签
  14. 你所不知道的网站外链高级操作策略
  15. Redis在多线程高并发下出现数据错乱问题
  16. Java基础语法(一)——初识Java
  17. DELL服务器R230旧磁盘换新磁盘,新磁盘复制旧磁盘所以数据
  18. Edit Phone Number~苹果爸爸又又又又又又又又又又又又更新开发者中心的协议了
  19. face++与python实现人脸识别签到(考勤)功能
  20. 操作系统:最高相应比优先调度算法(HRRN)的实例

热门文章

  1. Python Iterator 学习小记
  2. Java基础03运算符
  3. 市场购物篮分析 Market Basket Analysis
  4. 详解vue路由传参的三种方式
  5. XY的模式识别学习笔记-最小平方误差准则分类 MSE
  6. Python优雅技巧
  7. 测试udp服务的端口是否可用
  8. 总有一张图片会撩拨起你初恋时的心弦(转载)
  9. vue-pdf使用+分页预览(踩坑 + 使用本地字体库)
  10. HTML详解(1.html简介)