当前位置: 首页 > news >正文

谷粒商城高级篇

ElasticSearch的学习

在这里插入图片描述
ES中索引类似于mysql的库
类型类似于表
文档类似于记录
然后字段为属性,然后是以json的方式存储在内存中

docker 进行Es的安装

1.拉镜像

docker pull elasticsearch:7.4.2 存储和检索数据
docker pull kibana:7.4.2 可视化检索数据

2.设置配置文件的相关路径 用于后面挂载

mkdir-p /mydata/elasticsearch/config
mkdir-p /mydata/elasticsearch/data
echo “http.host: 0.0.0.0” >> /mydata/elasticsearch/config/elasticsearch.yml

3.docker启动容器

docker run–name elasticsearch-p 9200:9200-p 9300:9300 -e “discovery.type=single-node”
-e ES_JAVA_OPTS=“-Xms64m-Xmx512m” -v /mydata/elasticsearch/config/elasticsearch.yml:/usr/share/elasticsearch/config/elasticsearch.yml -v /mydata/elasticsearch/data:/usr/share/elasticsearch/data -v /mydata/elasticsearch/plugins:/usr/share/elasticsearch/plugins -d elasticsearch:7.4

-p 端口的设置 -e是环境变量的设置 -v是配置文件的挂载 -d表示后台运行

4.查看相应容器的日志

docker logs 容器id/容器名

ES的常规操作(以下过时了,ES还需学新版本)

接口的形式进行对ES的操作

查看节点情况

在这里插入图片描述

post和put的方式保存数据(说是类型的概念已经没有了)

在这里插入图片描述

查询文档

在这里插入图片描述
在具体的接口下根据Id查到信息后,修改时尽量要带上
?if_seq_no=* &if_primary_term= *
进行乐观锁判断,就你get查到的这两个字段是什么则带上,如果成功修改会自动加1

更新文档

在这里插入图片描述
除了之前post和put带上id的情况下可以直接更新
也可以路径带上_update(更新值与原来相同时不变,版本与序列号也不变,也可以更新的同时新增)

要用doc{
} 框起

删除文档

在这里插入图片描述

批量录入_bulk

当直接为 localhost:9200/_bulk,这种就是所有的索引都进行文档的批量操作
路径没有指定具体的索引
在这里插入图片描述
需要请求体时,则两个大括号为一组
index和create操作似乎都能创建索引,但肯定是有什么不同的
create就是更具体,内容更详细了
在这里插入图片描述

两种检索索引数据的方式

在这里插入图片描述
第一个get为url的请求方式
但通常用第二种 称为query DSL

queryDSL基本语法

基本查询

在这里插入图片描述

查找的索引返回部分字段

在这里插入图片描述

_source指定要查询的字段

match查询 指定具体的查询条件 (注意得分,分越高,相关性越高)

在这里插入图片描述
当查询的内容不想进行分词去查找时,则要指定为短语查找,当作整一句进行看
在这里插入图片描述

多字段查询

在这里插入图片描述
会分词查询

复合查询 bool

在这里插入图片描述

must为必须字段必须满足的情况,mustnot为比不满足,should是可以满足,满足时得分最高
filter 与 must 功能和用法相同,但不会影响分数

term(非文本时使用) 与 match(文本时使用)作用相同,但是精确查找

aggregations 类似sql的group和各种聚合函数

有很多种不同的聚合方法 也就是类似于sql的各种函数方法,要了解时需在官方文档查看

GET bank/_search
{"query":{"match": {"address": "mill"}},//使用聚合的关键字"aggs": {//聚合名 ageAgg"ageAgg": {//聚合类型 terms //age值进行分类,显示前10个分类"terms": {"field": "age","size": 10}},//第二个聚合 可在写一套聚合规则,且可在该聚合的基础下,再进行聚合,子聚合只能写一个"ageAgg2":{"aggAvg":{//工资的平均值"avg":{"filed": "balance"}}},//直接在aggs内部再写个aggs"aggs":{}}
}

mapping类型映射

创建索引时,创建的字段ES会自动分配字段类型,都有时候不符合我们的要求

则我们可以再创建索引的时候,就指定好类型

PUT /my_index
{"mappings":{"properties" :{"age":{"type":"integer"},"email":{"type":"keyword"},//类型为keyword的时候不会分词搜索"name":{"type":"text"}//会分词搜索}}
}
给索引添加新的映射字段
PUT /my_index/_mapping
{"mappings":{"properties" :{"employee-id":{"type":"integer","index": false //默认为true,设置为false时则不可由该字段进行检索}}}
}
修改索引的映射字段 不能修改已存在的字段,只能创建新索引,再把旧索引的数据迁移

在这里插入图片描述

在服务器上快速启动一个nginx服务器

首先直接通过docker拉取一个nginx服务器,没有镜像会自动先下载镜像

docker run -p 80:80 --name nginx -d nginx:1.10

我们所要的是拉取下来的nginx服务器的配置文件,找到自己想要保存配置的地址下,输入下面的命令,然后调整文件的保存目录

docker container cp nginx:/etc/nginx .
mv nginx conf
mkdir nginx
mv conf nginx

有了这套配置文件后,就docker正常运行nginx容器然后挂载

docker run -p 80:80 --name nginx
-v /mydata/nginx/html:/usr/share/nginx/html
-v /mydata/nginx/logs:/var/log/nginx
-v /mydata/nginx/conf:/etc/nginx
-d nginx:1.10

挂载 html页面的路径,然后日志的路径 ,配置的路径

创建新模块,用于elasticsearch

有很多种在springboot调用ela的方式,这边用的是

 <dependency><groupId>org.elasticsearch.client</groupId><artifactId>elasticsearch-rest-high-level-client</artifactId><version>7.4.2</version></dependency>

不能直接用,需要自己去写配置类

/*** 1、导入依赖* 2、编写配置,给容器中注入一个RestHighLevelClient* 3、*/
@Configuration
public class GulimallElasticSearchConfig {@Beanpublic RestHighLevelClient esRestClient(){RestClientBuilder builder = null;builder = RestClient.builder(new HttpHost("192.168.29.103",9200,"http"));RestHighLevelClient client = new RestHighLevelClient(builder);return client;}
}

设置同一的请求头

我们知道是通过接口去访问es,进行增删改查的,则对于各种请求我们想要设置,则需在配置类中调整,做为单实例

@Configuration
public class GulimallElasticSearchConfig {public static final RequestOptions COMMON_OPTIONS;static {RequestOptions.Builder builder = RequestOptions.DEFAULT.toBuilder();//对于builder进行各种的设置COMMON_OPTIONS = builder.build();}@Beanpublic RestHighLevelClient esRestClient(){RestClientBuilder builder = null;builder = RestClient.builder(new HttpHost("192.168.29.103",9200,"http"));RestHighLevelClient client = new RestHighLevelClient(builder);return client;}
}

索引的保存和更新,只要指定好了索引id

 @Testpublic void indexData() throws IOException {//设置索引名IndexRequest indexRequest = new IndexRequest("users");//设置idindexRequest.id("1");//键值对的方式往索引加值//indexRequest.source("userName","zhangsan","age",18);//第二种,以json对象的方式加入值,也是我们常用的User user = new User();user.setUserName("lihao");user.setAge(18);user.setGender("男");String jsonString = JSON.toJSONString(user);//第二个参数要设置为json类型indexRequest.source(jsonString, XContentType.JSON);//我们创建好的索引,我们配置类设置好的请求头IndexResponse index = client.index(indexRequest, GulimallElasticSearchConfig.COMMON_OPTIONS);System.out.println(index);}

保存 查询 删除的api方式需要查看es文档

复杂查询

一个简单的查询过程
   @Testpublic void searchData() throws IOException{//1.创建检索请求SearchRequest searchRequest = new SearchRequest();//指定索引searchRequest.indices("bank");//指定DSL,检索条件//通过SearchSourceBuilder sourceBuilder 封装的条件SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();//1.1构造检索条件 es控制台能操作的,代码也都是可以操作
//        sourceBuilder.query();
//        sourceBuilder.from();
//        sourceBuilder.size();
//        sourceBuilder.aggregation();//又进行QueryBuilders的条件封装sourceBuilder.query(QueryBuilders.matchQuery("address","mill"));System.out.println(searchRequest.toString());searchRequest.source(sourceBuilder);//2.执行检索SearchResponse searchResponse = client.search(searchRequest, GulimallElasticSearchConfig.COMMON_OPTIONS);//3.分析结果System.out.println(searchResponse.toString());}

加入聚合后

  @Testpublic void searchData() throws IOException{//1.创建检索请求SearchRequest searchRequest = new SearchRequest();//指定索引searchRequest.indices("bank");//指定DSL,检索条件//通过SearchSourceBuilder sourceBuilder 封装的条件SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();//1.1构造检索条件 es控制台能操作的,代码也都是可以操作
//        sourceBuilder.query();
//        sourceBuilder.from();
//        sourceBuilder.size();
//        sourceBuilder.aggregation();//又进行QueryBuilders的条件封装sourceBuilder.query(QueryBuilders.matchQuery("address","mill"));//1.2 按照年龄的值分布进行聚合TermsAggregationBuilder ageAgg = AggregationBuilders.terms("ageAgg").field("age").size(10);sourceBuilder.aggregation(ageAgg);//1.3 利用聚合 计算平均薪资AvgAggregationBuilder balanceAvg = AggregationBuilders.avg("balanceAvg").field("balance");sourceBuilder.aggregation(balanceAvg);System.out.println(searchRequest.toString());searchRequest.source(sourceBuilder);//2.执行检索SearchResponse searchResponse = client.search(searchRequest, GulimallElasticSearchConfig.COMMON_OPTIONS);//3.分析结果System.out.println(searchResponse.toString());//正常我们拿到值,肯定是要存入bean中的,也可以先存在内存中,拿一个map存一存//Map map = JSON.parseObject(searchResponse.toString(),Map.class);}//3.1获取所有查到的数据//外面一层的hits,有很多信息,但保存的数据还再更里面一层hitsSearchHits hits = searchResponse.getHits();//真正的数据hits,以keyvalue的形似SearchHit[] searchHits = hits.getHits();for(SearchHit hit : searchHits){/*** 各种除了数据外的信息* _index:"bank"索引名*       "_type":"account"类型,现在类型取消了,统一为doc*       "_id":"345",*       "_score":5.4022得分,也就是符合程度*       "_source"这就是我们的数据了*///   hit.getIndex();hit.getType();hit.getId();//拿到json形式,然后解析后可以封装到javaBean中String sourceAsString = hit.getSourceAsString();//Account accout = JSON.parseObject(String,Account.class);//3.2获取这次检索到分析信息,就是聚合后的数据,统计分析过的数据Aggregations aggregations = searchResponse.getAggregations();//可以通过foreach每个进行访问,也可以通过聚合名进行具体访问//我知道该聚合的类型为Terms,所以直接写了,这些的父类都是AggregationTerms ageAgg1 = aggregations.get("ageAgg");for(Terms.Bucket bucket : ageAgg1.getBuckets()){//拿到统计的信息String keyAsString = bucket.getKeyAsString();}//这是平均值的聚合Avg balanceAvg1 = aggregations.get("balanceAvg");

ES使用

对于大数据,多筛选情况下,用es比较好
存储该商城所需要的筛选条件

mappings(映射)是用来定义索引中字段的数据类型、分词方式及其他属性的核心配置。它相当于传统数据库中的"表结构定义"。
当然可以直接创建索引的时候同时创建字段和值,不用先mapping

PUT  product{
"mappings":{
"properties":{"skuId":{"type":"long"},
"spuId":{"type":"keyword"},
//标题设置为了text,全文检索进行分词,为模糊查找
//类型为keyword时是不可以分词的,为精确查找
"skuTitle":{"type":"text",
//该字段指定了分词器
"analyzer":"ik_smart"},
"skuPrice":{"type":"keyword"},
//关于图片字段都是为了可以快速在内存查到显示在前段,不用进行聚合和检索,所以设置了false
"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标注,但数组只是普通的值则不用设置nested,会自动扁平化存储
"attrs":{"type":"nested",
"properties":{"attrId":{"type":"long"},
"attrName":{"type":"keyword",
"index":false,
"doc_values":false},
"attrValue":{"type":"keyword"}}}}}}

实际操作

创建一个跟ES索引属性对应的Bean对象

spu信息的es内存保存(现在es创建好了索引,定义好了字段(setting和mapping),相当于在mysql创建好了表和字段)

@Slf4j
@Service
public class ProductSaveServiceImpl implements ProductSaveService {@AutowiredRestHighLevelClient restHighLevelClient;@Overridepublic boolean productStatusUp(List<SkuEsModel> skuEsModels) throws IOException {//把sku信息保存到es,可供快速检索//1、给es中建立索引。 product 建立好映射关系//2.给es中保存这些数据//由于是list,最好不用index方法单独保存数据,采用bulk批量存储参数所需BulkRequest bulkRequest,RequestOptions optionsBulkRequest bulkRequest = new BulkRequest();for(SkuEsModel model : skuEsModels){//1、构造保存请求IndexRequest indexRequest = new IndexRequest(EsConstant.PRODUCT_INDEX);indexRequest.id(model.getSkuId().toString());String s = JSON.toJSONString(model);indexRequest.source(s, XContentType.JSON);bulkRequest.add(indexRequest);}BulkResponse bulk = restHighLevelClient.bulk(bulkRequest, GulimallElasticSearchConfig.COMMON_OPTIONS);//检查批量保存是否有出错boolean b = bulk.hasFailures();List<String> collect = Arrays.stream(bulk.getItems()).map(item -> {return item.getId();}).collect(Collectors.toList());log.error("商品上架错误:{}",collect);return b;}
}

为什么需要预先创建索引?
索引相当于数据库的表:Elasticsearch 的索引(Index)类似于关系型数据库中的表,它定义了数据的存储结构(Mapping)和配置(Settings)。
如果索引不存在,默认会自动创建:
Elasticsearch 在写入数据时,如果目标索引不存在,默认会自动创建,并使用动态映射(Dynamic Mapping)推断字段类型。
但自动创建的索引可能不符合业务需求,比如某些字段应该设为 keyword 而不是 text,或者需要特殊的分词器(Analyzer)。

ES存储时,遇到一个类型转换的问题

我们想在返回类R中进行存储data然后返回,但R基础于hashMap,导致data类型都应该是keyvalue的map,但我们要的是list<所需的类>

public class R extends HashMap<String, Object> {private static final long serialVersionUID = 1L;//alibaba fastjson的转换方式   复杂类型转换时用TypeReferencepublic <T>T getData(TypeReference<T> tTypeReference){Object o = get("data");String s = JSON.toJSONString(o);T t = JSON.parseObject(s, tTypeReference);return t;}public R setData(Object data){put("data",data);return this;}

应用时

//TypeReference是抽象类,则需要以匿名内部类的形式进行类的构建TypeReference<List<SkuHasStockVo>> listTypeReference = new TypeReference<List<SkuHasStockVo>>(){};//getdata传入该类型stockMap = skusHasStock.getData(listTypeReference).stream().collect(Collectors.toMap(SkuHasStockVo::getSkuId, item -> item.getHasStock()));

NGINX配置

分布式缓存

异步和线程池

线程池

在这里插入图片描述
创建线程的service继承于Executor,注意有的方法参数就是Executor,这也就是叫我们指定传线程池

正常使用一般都是使用线程池,省去了创建和销毁线程的时间

在这里插入图片描述

创建线程池的7个参数

在这里插入图片描述
在这里插入图片描述

常见四种线程池

在这里插入图片描述

异步编排 CompletableFuture

有时候在面对多线程并行时,有的线程需要其他线程拿到得值作为参数才能继续进行,所以这里我们将用CompletableFuture,实现于Future。

Future类主要是可以有指定得返回值,和进行线程先后顺序执行的编排

CompletableFuture(基础使用)

在这里插入图片描述

当lambda表达式,一个有返回值一个没有返回值

public class ThreadTest {public static ExecutorService executor = Executors.newFixedThreadPool(10);public static void main(String[] args) throws ExecutionException, InterruptedException {System.out.println("main...start...");//无返回类型CompletableFuture.runAsync(()->{System.out.println("当前线程:"+Thread.currentThread().getId());},executor);//有返回类型CompletableFuture<Long> future = CompletableFuture.supplyAsync(()->{System.out.println("当前线程:"+Thread.currentThread().getId());return Thread.currentThread().getId();},executor);Long aLong = future.get();}
}
基础使用的延申(计算完成时回调方法)

在这里插入图片描述

当得到结果后,可以进行链式编程直接再对结果进行操作,同样有异步和同步的两种选择,并且也可以对异常后的返回可以进行指定

   //有返回类型CompletableFuture<Long> future = CompletableFuture.supplyAsync(()->{System.out.println("当前线程:"+Thread.currentThread().getId());return Thread.currentThread().getId();},executor).whenComplete((res,excption)->{System.out.println("异步任务成功完成,结果是:"+res+",异常是:"+excption);}).exceptionally(throwable -> {//指定异常后,我们可以进行的值返回return 10L;});
handle方法(和complete一样只是可以直接指定返回值)

在这里插入图片描述

线程串行化 链式执行?

在这里插入图片描述

thenRun 拿不到上一步的执行结果
  CompletableFuture<Void> future = CompletableFuture.supplyAsync(()->{System.out.println("当前线程:"+Thread.currentThread().getId());return Thread.currentThread().getId();},executor).thenRunAsync(()->{System.out.println("任务开始执行");},executor);
thenAccept 可以拿到上一步的结果进行处理,但无返回值
  CompletableFuture<Void> future = CompletableFuture.supplyAsync(()->{System.out.println("当前线程:"+Thread.currentThread().getId());return Thread.currentThread().getId();},executor).thenAccept ((res)->{System.out.println("任务开始执行"+res);},executor);
thenApplyAsync 可以拿到上一步的结果,且自己也可以进行返回值,这是要注意给future的泛型不要设置为空了
 CompletableFuture<String> future = CompletableFuture.supplyAsync(()->{System.out.println("当前线程:"+Thread.currentThread().getId());return Thread.currentThread().getId();},executor).thenApplyAsync ((res)->{System.out.println("任务开始执行"+res);return "成功了"+res;},executor);String a = future.get();
两任务组合(同时完成后,进行第三个任务的触发)

在这里插入图片描述

runAfterBothAsync 执行两个任务 拿不到上次执行的结果也没有返回值
CompletableFuture<Long> future1 = CompletableFuture.supplyAsync(()->{System.out.println("当前线程:"+Thread.currentThread().getId());System.out.println("任务1开始");System.out.println("任务1结束");return Thread.currentThread().getId();},executor);CompletableFuture<String> future2 = CompletableFuture.supplyAsync(()->{System.out.println("任务2开始");System.out.println("任务2结束");return "hello";},executor);future1.runAfterBothAsync(future2,()->{System.out.println("任务3开始..");},executor);
thenAcceptBothAsync 可以拿到两个线程的返回值进行新的操作
CompletableFuture<Long> future1 = CompletableFuture.supplyAsync(()->{System.out.println("当前线程:"+Thread.currentThread().getId());System.out.println("任务1开始");System.out.println("任务1结束");return Thread.currentThread().getId();},executor);CompletableFuture<String> future2 = CompletableFuture.supplyAsync(()->{System.out.println("任务2开始");System.out.println("任务2结束");return "hello";},executor);future1.thenAcceptBothAsync (future2,(f1,f2)->{System.out.println("任务3开始.."+f1,+f2);},executor);
thenCombineAsync 可以拿到上次的返回值并且自己可以进行值返回
CompletableFuture<Long> future1 = CompletableFuture.supplyAsync(()->{System.out.println("当前线程:"+Thread.currentThread().getId());System.out.println("任务1开始");System.out.println("任务1结束");return Thread.currentThread().getId();},executor);CompletableFuture<String> future2 = CompletableFuture.supplyAsync(()->{System.out.println("任务2开始");System.out.println("任务2结束");return "hello";},executor);
//有返回值了,就要用future进行接受CompletableFuture<String> future3 =  future1.thenCombineAsync (future2,(f1,f2)->{System.out.println("任务3开始.."+f1,+f2);return "返回了"+f1+f2;},executor);
http://www.dtcms.com/a/272861.html

相关文章:

  • 用GNU Radio生成Frank信号
  • Redisson 的分布式锁
  • 动态物体滤除算法
  • 全连接神经网络
  • AI教学设计助手:生成好教案的Prompt技术实战(二)
  • Java中实现线程安全的几种方式
  • 我做了一个在线工具导航网站!
  • Apache
  • 一招解决Win11桌面右键刷新BUG问题!
  • 高通跃龙IoT-IQ系列芯片深度解析:定位、特性与应用全景
  • 智能音视频-搭建可视化智能体
  • 机器学10——集成学习
  • 北京-4年功能测试2年空窗-报培训班学测开-第四十七天
  • 汽车功能安全-软件集成和验证(Software Integration Verification)【目的、验证输入、集成验证要求】9
  • Synopsys 逻辑综合之 MultiBit Flip-Flop 与 ICG
  • 【TCP/IP】9. 域名系统(DNS)
  • Agent自动化与代码智能
  • 【更新至2023年】1998-2023年各地级市第一产业占GDP比重数据(全市)
  • 防爬虫君子协定 Robots.txt 文件
  • jetson agx orin 刷机、cuda、pytorch配置指南【亲测有效】
  • 【AI】人工智能领域关键术语全解析
  • [C#] 使用TextBox换行失败的原因与解决方案:换用RichTextBox的实战经验
  • AI 智能体:开启自动化协作新时代
  • The 2023 ICPC Asia Hangzhou Regional Contest(G. Snake Move(最短路))
  • GoView 低代码数据可视化
  • Git保姆级入门实战:从安装配置到常用命令与常见错误解决
  • Shader面试题100道之(61-80)
  • 动态规划疑惑总结
  • Oracle大表数据清理优化与注意事项详解
  • 毫米波雷达守护银发安全:七彩喜跌倒检测仪重构居家养老防线