谷粒商城----ES篇
一、product-es准备
P128
ES在内存中,所以在检索中优于mysql。ES也支持集群,数据分片存储。
需求:
- 上架的商品才可以在网站展示。
- 上架的商品需要可以被检索。
分析sku在es中如何存储
商品mapping
分析:商品上架在es中是存sku还是spu?
1)、检索的时候输入名字,是需要按照sku的title进行全文检索的
2)、检素使用商品规格,规格是spu的公共属性,每个spu是一样的
3)、按照分类id进去的都是直接列出spu的,还可以切换。
4〕、我们如果将sku的全量信息保存到es中(包括spu属性〕就太多字段了
方案1:
{skuId:1spuId:11skyTitile:华为xxprice:999saleCount:99attr:[{尺寸:5},{CPU:高通945},{分辨率:全高清}]
缺点:如果每个sku都存储规格参数(如尺寸),会有冗余存储,因为每个sku对应的spu的规格参数都一样
方案2:
sku索引
{spuId:1skuId:11
}
attr索引
{skuId:11attr:[{尺寸:5},{CPU:高通945},{分辨率:全高清}]
}
先找到4000个符合要求的spu,再根据4000个spu查询对应的属性,封装了4000个id,long 8B*4000=32000B=32KB
1K个人检索,就是32MB结论:如果将规格参数单独建立索引,会出现检索时出现大量数据传输的问题,会引起网络网络
🚩 因此选用方案1,以空间换时间
建立product索引
最终选用的数据模型:
- { “type”: “keyword” }, # 保持数据精度问题,可以检索,但不分词
- “analyzer”: “ik_smart” # 中文分词器
- “index”: false, # 不可被检索,不生成index
- “doc_values”: false # 默认为true,不可被聚合,es就不会维护一些聚合的信息
PUT product
{"mappings":{"properties": {"skuId":{ "type": "long" },"spuId":{ "type": "keyword" }, # 不可分词"skuTitle": {"type": "text","analyzer": "ik_smart" # 中文分词器},"skuPrice": { "type": "keyword" }, # 保证精度问题"skuImg" : { "type": "keyword" }, # 视频中有false"saleCount":{ "type":"long" },"hasStock": { "type": "boolean" },"hotScore": { "type": "long" },"brandId": { "type": "long" },"catalogId": { "type": "long" },"brandName": {"type": "keyword"}, # 视频中有false"brandImg":{"type": "keyword","index": false, # 不可被检索,不生成index,只用做页面使用"doc_values": false # 不可被聚合,默认为true},"catalogName": {"type": "keyword" }, # 视频里有false"attrs": {"type": "nested","properties": {"attrId": {"type": "long" },"attrName": {"type": "keyword","index": false,"doc_values": false},"attrValue": {"type": "keyword" }}}}}
}
如果检索不到商品,自己用postman测试一下,可能有的字段需要更改,你也可以把没必要的"keyword"去掉
冗余存储的字段:不用来检索,也不用来分析,节省空间
库存是bool。
检索品牌id,但是不检索品牌名字、图片
用skuTitle检索
二、nested嵌入式对象
属性是"type": “nested”,因为是内部的属性进行检索
数组类型的对象会被扁平化处理(对象的每个属性会分别存储到一起)
user.name=["aaa","bbb"]
user.addr=["ccc","ddd"]这种存储方式,可能会发生如下错误:
错误检索到{aaa,ddd},这个组合是不存在的
数组的扁平化处理会使检索能检索到本身不存在的,为了解决这个问题,就采用了嵌入式属性,数组里是对象时用嵌入式属性(不是对象无需用嵌入式属性)
三、商品上架(ES保存)
@Override // SpuInfoServiceImplpublic void up(Long spuId) {// 1 组装数据 查出当前spuId对应的所有sku信息List<SkuInfoEntity> skus = skuInfoService.getSkusBySpuId(spuId);// 查询这些sku是否有库存List<Long> skuids = skus.stream().map(sku -> sku.getSkuId()).collect(Collectors.toList());// 2 封装每个sku的信息// 3.查询当前sku所有可以被用来检索的规格属性List<ProductAttrValueEntity> baseAttrs = attrValueService.baseAttrListForSpu(spuId);// 得到基本属性idList<Long> attrIds = baseAttrs.stream().map(attr -> attr.getAttrId()).collect(Collectors.toList());// 过滤出可被检索的基本属性id,即search_type = 1 [数据库中目前 4、5、6、11不可检索]Set<Long> ids = new HashSet<>(attrService.selectSearchAttrIds(attrIds));// 可被检索的属性封装到SkuEsModel.Attrs中List<SkuEsModel.Attrs> attrs = baseAttrs.stream().filter(item -> ids.contains(item.getAttrId())).map(item -> {SkuEsModel.Attrs attr = new SkuEsModel.Attrs();BeanUtils.copyProperties(item, attr);return attr;}).collect(Collectors.toList());// 每件skuId是否有库存Map<Long, Boolean> stockMap = null;try {// 3.1 远程调用库存系统 查询该sku是否有库存R hasStock = wareFeignService.getSkuHasStock(skuids);// 构造器受保护 所以写成内部类对象stockMap = hasStock.getData(new TypeReference<List<SkuHasStockVo>>() {}).stream().collect(Collectors.toMap(SkuHasStockVo::getSkuId, item -> item.getHasStock()));log.warn("服务调用成功" + hasStock);} catch (Exception e) {log.error("库存服务调用失败: 原因{}", e);}Map<Long, Boolean> finalStockMap = stockMap;//防止lambda中改变// 开始封装esList<SkuEsModel> skuEsModels = skus.stream().map(sku -> {SkuEsModel esModel = new SkuEsModel();BeanUtils.copyProperties(sku, esModel);esModel.setSkuPrice(sku.getPrice());esModel.setSkuImg(sku.getSkuDefaultImg());// 4 设置库存,只查是否有库存,不查有多少if (finalStockMap == null) {esModel.setHasStock(true);} else {esModel.setHasStock(finalStockMap.get(sku.getSkuId()));}// TODO 1.热度评分 刚上架是0esModel.setHotScore(0L);// 设置品牌信息BrandEntity brandEntity = brandService.getById(esModel.getBrandId());esModel.setBrandName(brandEntity.getName());esModel.setBrandImg(brandEntity.getLogo());// 查询分类信息CategoryEntity categoryEntity = categoryService.getById(esModel.getCatalogId());esModel.setCatalogName(categoryEntity.getName());// 保存商品的属性, 查询当前sku的所有可以被用来检索的规格属性,同一spu都一样,在外面查一遍即可esModel.setAttrs(attrs);return esModel;}).collect(Collectors.toList());// 5.发给ES进行保存 gulimall-searchR r = searchFeignService.productStatusUp(skuEsModels);if (r.getCode() == 0) {// 远程调用成功baseMapper.updateSpuStatus(spuId, ProductConstant.StatusEnum.SPU_UP.getCode());} else {// 远程调用失败 TODO 接口幂等性 重试机制/*** Feign 的调用流程 Feign有自动重试机制* 1. 发送请求执行* 2.*/}}
@Slf4j
@Service
public class ProductSaveServiceImpl implements ProductSaveService {@Resourceprivate RestHighLevelClient client;/*** 将数据保存到ES* 用bulk代替index,进行批量保存* BulkRequest bulkRequest, RequestOptions options*/@Override // ProductSaveServiceImplpublic boolean productStatusUp(List<SkuEsModel> skuEsModels) throws IOException {// 1. 批量保存BulkRequest bulkRequest = new BulkRequest();// 2.构造保存请求for (SkuEsModel esModel : skuEsModels) {// 设置es索引 gulimall_productIndexRequest indexRequest = new IndexRequest(EsConstant.PRODUCT_INDEX);// 设置索引idindexRequest.id(esModel.getSkuId().toString());// json格式String jsonString = JSON.toJSONString(esModel);indexRequest.source(jsonString, XContentType.JSON);// 添加到文档bulkRequest.add(indexRequest);}// bulk批量保存BulkResponse bulk = client.bulk(bulkRequest, GuliESConfig.COMMON_OPTIONS);// TODO 是否拥有错误boolean hasFailures = bulk.hasFailures();if(hasFailures){List<String> collect = Arrays.stream(bulk.getItems()).map(item -> item.getId()).collect(Collectors.toList());log.error("商品上架错误:{}",collect);}return hasFailures;}
}
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"}}}}}
}
四、检索服务
package com.atguigu.gulimall.search.service.impl;import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.TypeReference;
import com.atguigu.common.to.es.SkuEsModel;
import com.atguigu.common.utils.R;
import com.atguigu.gulimall.search.config.GuliESConfig;
import com.atguigu.gulimall.search.constant.EsConstant;
import com.atguigu.gulimall.search.feign.ProductFeignService;
import com.atguigu.gulimall.search.service.SearchService;
import com.atguigu.gulimall.search.vo.AttrResponseVo;
import com.atguigu.gulimall.search.vo.BrandVo;
import com.atguigu.gulimall.search.vo.SearchParam;
import com.atguigu.gulimall.search.vo.SearchResult;
import lombok.extern.slf4j.Slf4j;
import org.apache.lucene.search.join.ScoreMode;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.NestedQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.index.query.RangeQueryBuilder;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.aggregations.AggregationBuilders;
import org.elasticsearch.search.aggregations.bucket.nested.NestedAggregationBuilder;
import org.elasticsearch.search.aggregations.bucket.nested.ParsedNested;
import org.elasticsearch.search.aggregations.bucket.terms.ParsedLongTerms;
import org.elasticsearch.search.aggregations.bucket.terms.ParsedStringTerms;
import org.elasticsearch.search.aggregations.bucket.terms.Terms;
import org.elasticsearch.search.aggregations.bucket.terms.TermsAggregationBuilder;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightField;
import org.elasticsearch.search.sort.SortOrder;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;import javax.annotation.Resource;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;/*** <p>Title: MallServiceImpl</p>* Description:* date:2020/6/12 23:06*/
@Slf4j
@Service
public class SearchServiceImpl implements SearchService {@Resourceprivate RestHighLevelClient restHighLevelClient;@Resourceprivate ProductFeignService productFeignService;@Overridepublic SearchResult search(SearchParam Param) {SearchResult result = null;// 1.准备检索请求SearchRequest searchRequest = buildSearchRequest(Param);try {// 2.执行检索请求SearchResponse response = restHighLevelClient.search(searchRequest, GuliESConfig.COMMON_OPTIONS);// 3.分析响应数据result = buildSearchResult(response, Param);} catch (IOException e) {e.printStackTrace();}return result;}/*** 准备检索请求 [构建查询语句]*/private SearchRequest buildSearchRequest(SearchParam Param) {// 帮我们构建DSL语句的SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();// 1. 模糊匹配 过滤(按照属性、分类、品牌、价格区间、库存) 先构建一个布尔Query// 1.1 mustBoolQueryBuilder boolQuery = QueryBuilders.boolQuery();if(!StringUtils.isEmpty(Param.getKeyword())){boolQuery.must(QueryBuilders.matchQuery("skuTitle",Param.getKeyword()));}// 1.2 bool - filter Catalog3Idif(StringUtils.isEmpty(Param.getCatalog3Id() != null)){boolQuery.filter(QueryBuilders.termQuery("catalogId", Param.getCatalog3Id()));}// 1.2 bool - brandId [集合]if(Param.getBrandId() != null && Param.getBrandId().size() > 0){boolQuery.filter(QueryBuilders.termsQuery("brandId", Param.getBrandId()));}// 属性查询if(Param.getAttrs() != null && Param.getAttrs().size() > 0){for (String attrStr : Param.getAttrs()) {BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();String[] s = attrStr.split("_");// 检索的id 属性检索用的值String attrId = s[0];String[] attrValue = s[1].split(":");boolQueryBuilder.must(QueryBuilders.termQuery("attrs.attrId", attrId));boolQueryBuilder.must(QueryBuilders.termsQuery("attrs.attrValue", attrValue));// 构建一个嵌入式Query 每一个必须都得生成嵌入的 nested 查询NestedQueryBuilder attrsQuery = QueryBuilders.nestedQuery("attrs", boolQueryBuilder, ScoreMode.None);boolQuery.filter(attrsQuery);}}// 1.2 bool - filter [库存]if(Param.getHasStock() != null){boolQuery.filter(QueryBuilders.termQuery("hasStock",Param.getHasStock() == 1));}// 1.2 bool - filter [价格区间]if(!StringUtils.isEmpty(Param.getSkuPrice())){RangeQueryBuilder rangeQuery = QueryBuilders.rangeQuery("skuPrice");String[] s = Param.getSkuPrice().split("_");if(s.length == 2){// 有三个值 就是区间rangeQuery.gte(s[0]).lte(s[1]);}else if(s.length == 1){// 单值情况if(Param.getSkuPrice().startsWith("_")){rangeQuery.lte(s[0]);}if(Param.getSkuPrice().endsWith("_")){rangeQuery.gte(s[0]);}}boolQuery.filter(rangeQuery);}// 把以前所有条件都拿来进行封装sourceBuilder.query(boolQuery);// 1.排序if(!StringUtils.isEmpty(Param.getSort())){String sort = Param.getSort();// sort=hotScore_asc/descString[] s = sort.split("_");SortOrder order = s[1].equalsIgnoreCase("asc") ? SortOrder.ASC : SortOrder.DESC;sourceBuilder.sort(s[0], order);}// 2.分页 pageSize : 5sourceBuilder.from((Param.getPageNum()-1) * EsConstant.PRODUCT_PASIZE);sourceBuilder.size(EsConstant.PRODUCT_PASIZE);// 3.高亮if(!StringUtils.isEmpty(Param.getKeyword())){HighlightBuilder builder = new HighlightBuilder();builder.field("skuTitle");builder.preTags("<b style='color:red'>");builder.postTags("</b>");sourceBuilder.highlighter(builder);}// 聚合分析// TODO 1.品牌聚合TermsAggregationBuilder brand_agg = AggregationBuilders.terms("brand_agg");brand_agg.field("brandId").size(50);// 品牌聚合的子聚合brand_agg.subAggregation(AggregationBuilders.terms("brand_name_agg").field("brandName").size(1));brand_agg.subAggregation(AggregationBuilders.terms("brand_img_agg").field("brandImg").size(1));// 将品牌聚合加入 sourceBuildersourceBuilder.aggregation(brand_agg);// TODO 2.分类聚合TermsAggregationBuilder catalog_agg = AggregationBuilders.terms("catalog_agg").field("catalogId").size(20);catalog_agg.subAggregation(AggregationBuilders.terms("catalog_name_agg").field("catalogName").size(1));// 将分类聚合加入 sourceBuildersourceBuilder.aggregation(catalog_agg);// TODO 3.属性聚合 attr_agg 构建嵌入式聚合NestedAggregationBuilder attr_agg = AggregationBuilders.nested("attr_agg", "attrs");// 3.1 聚合出当前所有的attrIdTermsAggregationBuilder attrIdAgg = AggregationBuilders.terms("attr_id_agg").field("attrs.attrId");// 3.1.1 聚合分析出当前attrId对应的attrNameattrIdAgg.subAggregation(AggregationBuilders.terms("attr_name_agg").field("attrs.attrName").size(1));// 3.1.2 聚合分析出当前attrId对应的所有可能的属性值attrValue 这里的属性值可能会有很多 所以写50attrIdAgg.subAggregation(AggregationBuilders.terms("attr_value_agg").field("attrs.attrValue").size(50));// 3.2 将这个子聚合加入嵌入式聚合attr_agg.subAggregation(attrIdAgg);sourceBuilder.aggregation(attr_agg);log.info("\n构建语句:->\n" + sourceBuilder.toString());SearchRequest searchRequest = new SearchRequest(new String[]{EsConstant.PRODUCT_INDEX}, sourceBuilder);return searchRequest;}/*** 构建结果数据 指定catalogId 、brandId、attrs.attrId、嵌入式查询、倒序、0-6000、每页显示两个、高亮skuTitle、聚合分析*/private SearchResult buildSearchResult(SearchResponse response, SearchParam Param) {SearchResult result = new SearchResult();// 1.返回的所有查询到的商品SearchHits hits = response.getHits();List<SkuEsModel> esModels = new ArrayList<>();if(hits.getHits() != null && hits.getHits().length > 0){for (SearchHit hit : hits.getHits()) {String sourceAsString = hit.getSourceAsString();// ES中检索得到的对象SkuEsModel esModel = JSON.parseObject(sourceAsString, SkuEsModel.class);if(!StringUtils.isEmpty(Param.getKeyword())){// 1.1 获取标题的高亮属性HighlightField skuTitle = hit.getHighlightFields().get("skuTitle");String highlightFields = skuTitle.getFragments()[0].string();// 1.2 设置文本高亮esModel.setSkuTitle(highlightFields);}esModels.add(esModel);}}result.setProducts(esModels);// 2.当前所有商品涉及到的所有属性信息ArrayList<SearchResult.AttrVo> attrVos = new ArrayList<>();ParsedNested attr_agg = response.getAggregations().get("attr_agg");ParsedLongTerms attr_id = attr_agg.getAggregations().get("attr_id_agg");for (Terms.Bucket bucket : attr_id.getBuckets()) {SearchResult.AttrVo attrVo = new SearchResult.AttrVo();// 2.1 得到属性的idattrVo.setAttrId(bucket.getKeyAsNumber().longValue());// 2.2 得到属性的名字String attr_name = ((ParsedStringTerms) bucket.getAggregations().get("attr_name_agg")).getBuckets().get(0).getKeyAsString();attrVo.setAttrName(attr_name);// 2.3 得到属性的所有值List<String> attr_value = ((ParsedStringTerms) bucket.getAggregations().get("attr_value_agg")).getBuckets().stream().map(item -> item.getKeyAsString()).collect(Collectors.toList());attrVo.setAttrValue(attr_value);attrVos.add(attrVo);}result.setAttrs(attrVos);// 3.当前所有商品涉及到的所有品牌信息ArrayList<SearchResult.BrandVo> brandVos = new ArrayList<>();ParsedLongTerms brand_agg = response.getAggregations().get("brand_agg");for (Terms.Bucket bucket : brand_agg.getBuckets()) {SearchResult.BrandVo brandVo = new SearchResult.BrandVo();// 3.1 得到品牌的idlong brnadId = bucket.getKeyAsNumber().longValue();brandVo.setBrandId(brnadId);// 3.2 得到品牌的名String brand_name = ((ParsedStringTerms) bucket.getAggregations().get("brand_name_agg")).getBuckets().get(0).getKeyAsString();brandVo.setBrandName(brand_name);// 3.3 得到品牌的图片String brand_img = ((ParsedStringTerms) (bucket.getAggregations().get("brand_img_agg"))).getBuckets().get(0).getKeyAsString();brandVo.setBrandImg(brand_img);brandVos.add(brandVo);}result.setBrands(brandVos);// 4.当前商品所有涉及到的分类信息ParsedLongTerms catalog_agg = response.getAggregations().get("catalog_agg");List<SearchResult.CatalogVo> catalogVos = new ArrayList<>();for (Terms.Bucket bucket : catalog_agg.getBuckets()) {// 设置分类idSearchResult.CatalogVo catalogVo = new SearchResult.CatalogVo();catalogVo.setCatalogId(Long.parseLong(bucket.getKeyAsString()));// 得到分类名ParsedStringTerms catalog_name_agg = bucket.getAggregations().get("catalog_name_agg");String catalog_name = catalog_name_agg.getBuckets().get(0).getKeyAsString();catalogVo.setCatalogName(catalog_name);catalogVos.add(catalogVo);}result.setCatalogs(catalogVos);// ================以上信息从聚合信息中获取// 5.分页信息-页码result.setPageNum(Param.getPageNum());// 总记录数long total = hits.getTotalHits().value;result.setTotal(total);// 总页码:计算得到int totalPages = (int)(total / EsConstant.PRODUCT_PASIZE + 0.999999999999);result.setTotalPages(totalPages);// 设置导航页ArrayList<Integer> pageNavs = new ArrayList<>();for (int i = 1;i <= totalPages; i++){pageNavs.add(i);}result.setPageNavs(pageNavs);// 6.构建面包屑导航功能if(Param.getAttrs() != null){List<SearchResult.NavVo> navVos = Param.getAttrs().stream().map(attr -> {SearchResult.NavVo navVo = new SearchResult.NavVo();String[] s = attr.split("_");navVo.setNavValue(s[1]);R r = productFeignService.getAttrsInfo(Long.parseLong(s[0]));// 将已选择的请求参数添加进去 前端页面进行排除result.getAttrIds().add(Long.parseLong(s[0]));if(r.getCode() == 0){AttrResponseVo data = r.getData(new TypeReference<AttrResponseVo>(){});navVo.setName(data.getAttrName());}else{// 失败了就拿id作为名字navVo.setName(s[0]);}// 拿到所有查询条件 替换查询条件String replace = replaceQueryString(Param, attr, "attrs");navVo.setLink("http://search.gulimall.com/list.html?" + replace);return navVo;}).collect(Collectors.toList());result.setNavs(navVos);}// 品牌、分类if(Param.getBrandId() != null && Param.getBrandId().size() > 0){List<SearchResult.NavVo> navs = result.getNavs();SearchResult.NavVo navVo = new SearchResult.NavVo();navVo.setName("品牌");// TODO 远程查询所有品牌R r = productFeignService.brandInfo(Param.getBrandId());if(r.getCode() == 0){List<BrandVo> brand = r.getData("data", new TypeReference<List<BrandVo>>() {});StringBuffer buffer = new StringBuffer();// 替换所有品牌IDString replace = "";for (BrandVo brandVo : brand) {buffer.append(brandVo.getBrandName() + ";");replace = replaceQueryString(Param, brandVo.getBrandId() + "", "brandId");}navVo.setNavValue(buffer.toString());navVo.setLink("http://search.gulimall.com/list.html?" + replace);}navs.add(navVo);}return result;}/*** 替换字符* key :需要替换的key*/private String replaceQueryString(SearchParam Param, String value, String key) {String encode = null;try {encode = URLEncoder.encode(value,"UTF-8");// 浏览器对空格的编码和java的不一样encode = encode.replace("+","%20");encode = encode.replace("%28", "(").replace("%29",")");} catch (UnsupportedEncodingException e) {e.printStackTrace();}return Param.get_queryString().replace("&" + key + "=" + encode, "");}
}
相关文章:
谷粒商城----ES篇
一、product-es准备 P128 ES在内存中,所以在检索中优于mysql。ES也支持集群,数据分片存储。 需求: 上架的商品才可以在网站展示。上架的商品需要可以被检索。 分析sku在es中如何存储 商品mapping 分析:商品上架在es中是存s…...

Redis3.2.1如何设置远程连接?允许局域网访问
背景: 电脑A的redis需要开放给电脑B使用,二者处于同一局域网 【后面会补充更详细的踩坑历程,先发出来作为记录】 过程: 在你查了很多方法后,如果还是没有解决, 尝试考虑一下你的redis配置文件是不是修…...

网络原理(二)TCP的可靠传输
网络原理(一)目录 网络原理应用层传输层先说UDP(不可靠传输)重点说明TCP(可靠传输)一、确认应答二、超时重传三、链接管理建立连接断开链接 四、滑动窗口五、流量控制&am…...
Chat GPT 使用教学,文字创作、学习
目录 文章长篇文章学习任何东西文章 大纲、目录、标题、内容 写出10个即将被AI取代的工作的文章标题 当然,以下是一些可能会被AI取代的工作的文章标题:"未来十年,AI将如何改变传统制造业的就业格局?" "智能客服崛起:人工智能如何重塑客户服务行业?"…...

Android之 Canvas绘制
一 Canvas介绍 1.1 Canvas 是绘制图形的重要类之一,它可以在 View 或 SurfaceView 上绘制各种图形和文本. 1.2 要创建 Canvas,首先需要有一个 View 或 SurfaceView 对象,在 View 或 SurfaceView 的绘制方法中,可以通过 Canvas 的…...

Vue + Element UI 前端篇(十五):嵌套外部网页
Vue Element UI 实现权限管理系统 前端篇(十五):嵌套外部网页 嵌套外部网页 在有些时候,我们需要在我们的内容栏主区域显示外部网页。如查看服务端提供的SQL监控页面,接口文档页面等。 这个时候就要求我们的导航菜…...
Jabbi的Rust学习日记(二)
特征: 就目前我学习到的rust知识来看,我认为rust有以下几个特征: 链式调用表达式强类型 use 使用use导入包,我觉得rust的导包和python的很像 main main函数是rust可执行程序最先执行的代码,可以说是程序的入口&…...

【杂】环形时钟配色笔记
配色网站笔记 coolorsflatuicolorscolordrophttps://www.webdesignrankings.com/resources/lolcolors/ 配色2...

会话跟踪技术学习笔记(Cookie+Session)+ HTTP学习笔记
一、核心知识点(重点): 1.1 Cookie 1. Cookie:是一种客户端会话技术,数据会被保存在客户端,Cookie会携带数据访问服务器,用以完成一次会话内多次请求间的数据共享 2. 过程:浏览器…...

分类预测 | MATLAB实现PCA-BiLSTM(主成分双向长短期记忆神经网络)分类预测
分类预测 | MATLAB实现PCA-BiLSTM(主成分双向长短期记忆神经网络)分类预测 目录 分类预测 | MATLAB实现PCA-BiLSTM(主成分双向长短期记忆神经网络)分类预测预测效果基本介绍程序设计参考资料致谢 预测效果 基本介绍 分类预测 | MATLAB实现PCA-BiLSTM(主成分双向长短期记忆神经网…...
Yarn 和 npm 的区别
Yarn 和 npm 都是 JavaScript 的包管理工具,它们的主要区别在于以下几个方面: 性能:Yarn 的安装速度和包的下载速度通常比 npm 更快,这是因为 Yarn 使用本地缓存和并行下载等技术来提高性能。 可靠性:Yarn 具有更好的…...

第20章 原子操作实验(iTOP-RK3568开发板驱动开发指南 )
在上一章节的实验中,对并发与竞争进行了实验,两个app应用程序之间对共享资源的竞争访问引起了数据传输错误,而在Linux内核中,提供了四种处理并发与竞争的常见方法,分别是原子操作、自旋锁、信号量、互斥体,…...
Android 开机自启动
APP需要开机自启动,要通过开机广播实现。 1,在AndroidManifest.xml中增加权限 <!-- .接收启动完成的广播权限 --><uses-permission android:name"android.permission.RECEIVE_BOOT_COMPLETED" /> 2,在AndroidManifes…...

01_前端css编写的三种方式
前言 CSS的引入方式共有三种:行内样式、内部样式表、外部样式表 一、内联式引入 用法: 在元素上直接通过style属性进行设置css样式设置 示例: <h1 style"color:red;">style属性的应用</h1> <p style"font-si…...

07-垃圾收集算法详解
上一篇:06-JVM对象内存回收机制深度剖析 1.分代收集理论 当前虚拟机的垃圾收集都采用分代收集算法,这种算法没有什么新的思想,只是根据对象存活周期的不同将内存分为几块。一般将java堆分为新生代和老年代,这样我们就可以根据各…...

Redis高并发分布式锁实战
高并发场景秒杀抢购超卖bug实战重现 秒杀抢购场景下实战JVM级别锁与分布式锁 大厂分布式锁Resisson框架实战 Lua脚本语言快速入门与使用注意事项 Redisson分布式锁源码剖析 Redis主从架构锁失效问题解析 从CAP角度剖析Redis与Zookeeper分布式锁区别 Redlock分布式锁原理与…...

MybatisPlus分页插件使用
一. 效果展示 二. 代码编写 2.1 pom <dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-boot-starter</artifactId><version>3.4.2</version> </dependency>2.2 添加配置类 Configuration MapperScan(…...

Linux指令二【进程,权限,文件】
进程是一个具有一定独立功能的程序在一个数据集上的一次动态执行的过程,是操作系统进行 资源分配和调度的一个独立单位,是应用程序运行的载体。 一、进程基本指令 1.ps:当前的用户进程 ps 只显示隶属于自己的进程状态ps -aux 显示所有进程…...

uni-app运行到微信开发者工具-没有打印的情况
前言 到我们进场使用微信开发者工具时,就会发现它经常会有bug,特别是在软件更新,组件库更新之后 最近在更新微信开发者工具之后发现所有打印都不显示了,虽然是小问题-但对于强迫症很烦 以为是代码配置问题-结果是更新之后打印开…...
由前端接口入门学习后端的controller层
由前端接口入门学习后端的controller层 一、简单介绍一下controller层:二、前端调用后端接口时,一般会传递参数给后端,后端的控制层是如何接收的呢?三、更深入地介绍一下关于请求体参数DTO作为入参Map作为入参 本文是以一个前端工…...

【大模型RAG】拍照搜题技术架构速览:三层管道、两级检索、兜底大模型
摘要 拍照搜题系统采用“三层管道(多模态 OCR → 语义检索 → 答案渲染)、两级检索(倒排 BM25 向量 HNSW)并以大语言模型兜底”的整体框架: 多模态 OCR 层 将题目图片经过超分、去噪、倾斜校正后,分别用…...

AI-调查研究-01-正念冥想有用吗?对健康的影响及科学指南
点一下关注吧!!!非常感谢!!持续更新!!! 🚀 AI篇持续更新中!(长期更新) 目前2025年06月05日更新到: AI炼丹日志-28 - Aud…...

XCTF-web-easyupload
试了试php,php7,pht,phtml等,都没有用 尝试.user.ini 抓包修改将.user.ini修改为jpg图片 在上传一个123.jpg 用蚁剑连接,得到flag...
椭圆曲线密码学(ECC)
一、ECC算法概述 椭圆曲线密码学(Elliptic Curve Cryptography)是基于椭圆曲线数学理论的公钥密码系统,由Neal Koblitz和Victor Miller在1985年独立提出。相比RSA,ECC在相同安全强度下密钥更短(256位ECC ≈ 3072位RSA…...
大语言模型如何处理长文本?常用文本分割技术详解
为什么需要文本分割? 引言:为什么需要文本分割?一、基础文本分割方法1. 按段落分割(Paragraph Splitting)2. 按句子分割(Sentence Splitting)二、高级文本分割策略3. 重叠分割(Sliding Window)4. 递归分割(Recursive Splitting)三、生产级工具推荐5. 使用LangChain的…...

自然语言处理——循环神经网络
自然语言处理——循环神经网络 循环神经网络应用到基于机器学习的自然语言处理任务序列到类别同步的序列到序列模式异步的序列到序列模式 参数学习和长程依赖问题基于门控的循环神经网络门控循环单元(GRU)长短期记忆神经网络(LSTM)…...
聊一聊接口测试的意义有哪些?
目录 一、隔离性 & 早期测试 二、保障系统集成质量 三、验证业务逻辑的核心层 四、提升测试效率与覆盖度 五、系统稳定性的守护者 六、驱动团队协作与契约管理 七、性能与扩展性的前置评估 八、持续交付的核心支撑 接口测试的意义可以从四个维度展开,首…...

LINUX 69 FTP 客服管理系统 man 5 /etc/vsftpd/vsftpd.conf
FTP 客服管理系统 实现kefu123登录,不允许匿名访问,kefu只能访问/data/kefu目录,不能查看其他目录 创建账号密码 useradd kefu echo 123|passwd -stdin kefu [rootcode caozx26420]# echo 123|passwd --stdin kefu 更改用户 kefu 的密码…...

面向无人机海岸带生态系统监测的语义分割基准数据集
描述:海岸带生态系统的监测是维护生态平衡和可持续发展的重要任务。语义分割技术在遥感影像中的应用为海岸带生态系统的精准监测提供了有效手段。然而,目前该领域仍面临一个挑战,即缺乏公开的专门面向海岸带生态系统的语义分割基准数据集。受…...

Linux 中如何提取压缩文件 ?
Linux 是一种流行的开源操作系统,它提供了许多工具来管理、压缩和解压缩文件。压缩文件有助于节省存储空间,使数据传输更快。本指南将向您展示如何在 Linux 中提取不同类型的压缩文件。 1. Unpacking ZIP Files ZIP 文件是非常常见的,要在 …...