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

微服务实战——ElasticSearch(保存)

商品上架——ElasticSearch(保存)

0.商城架构图

1.商品Mapping

分析:商品上架在 es 中是存 sku 还是 spu ?

  1. 检索的时候输入名字,是需要按照 sku 的 title 进行全文检索的
  2. 检索使用商品规格,规格是 spu 的公共属性,每个 spu 是一样的
  3. 按照分类 id 进去的都是直接列出 spu 的,还可以切换。
  4. 我们如果将 sku 的全量信息保存到 es 中(包括 spu 属性)就太多量字段了。
  5. 我们如果将 spu 以及他包含的 sku 信息保存到 es 中,也可以方便检索。但是 sku 属于spu 的级联对象,在 es 中需要 nested 模型,这种性能差点。
  6. 但是存储与检索我们必须性能折中。
  7. 如果我们分拆存储,spu 和 attr 一个索引,sku 单独一个索引可能涉及的问题。
    1. 检索商品的名字,如“手机”,对应的 spu 有很多,我们要分析出这些 spu 的所有关联属性,再做一次查询,就必须将所有 spu_id 都发出去。假设有 1 万个数据,数据传输一次就10000*4=4MB;并发情况下假设 1000 检索请求,那就是 4GB 的数据,传输阻塞时间会很长,业务更加无法继续。
    2. 所以,我们如下设计,这样才是文档区别于关系型数据库的地方,宽表设计,不能去考虑数据库范式。
    3. 向ES添加商品属性映射

向ES添加商品属性映射

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"}}}}}
}

index :

默认 true ,如果为 false ,表示该字段不会被索引,但是检索结果里面有,但字段本身不能

当做检索条件。

doc_values :

默认 true ,设置为 false ,表示不可以做排序、聚合以及脚本操作,这样更节省磁盘空间。

还可以通过设定 doc_values 为 true , index 为 false 来让字段不能被搜索但可以用于排序、聚合以及脚本操作:

spu在es中的存储模型分析总结

如果每个sku都存储规格参数,会有冗余存储,因为每个spu对应的sku的规格参数都一样。但是如果将规格参数单独建立索引会出现检索时出现大量数据传输的问题,会阻塞网络因此我们选用第一种存储模型,以空间换时间。

2.上架细节

上架是将后台的商品放在 es 中可以提供检索和查询功能:

  1. hasStock:代表是否有库存。默认上架的商品都有库存。如果库存无货的时候才需要更新一下 es
  2. 库存补上以后,也需要重新更新一下 es
  3. hotScore 是热度值,我们只模拟使用点击率更新热度。点击率增加到一定程度才更新热度值。
  4. 下架就是从 es 中移除检索项,以及修改 mysql 状态

商品上架步骤:

  1. 先在 es 中按照之前的 mapping 信息,建立 product 索引。
  2. 点击上架,查询出所有 sku 的信息,保存到 es 中
  3. es 保存成功返回,更新数据库的上架状态信息

3.数据一致性

  1. 商品无库存的时候需要更新 es 的库存信息
  2. 商品有库存也要更新 es 的信息

4.ES中的数组扁平化

关于“nested”,Nested datatype | Elasticsearch Guide [7.6] | Elastic

ES中数组的扁平化处理:

对象数组的扁平化:

内部对象字段数组的工作方式与您预期的不同。Lucene没有内部对象的概念,所以Elasticsearch将对象层次结构简化为字段名和值的简单列表。例如,以下文件:

PUT my_index/_doc/1
{"group" : "fans","user" : [ {"first" : "John","last" :  "Smith"},{"first" : "Alice","last" :  "White"}]
}

在内部将转换成一个文档,看起来是这样的:

{"group" :        "fans","user.first" : [ "alice", "john" ],"user.last" :  [ "smith", "white" ]
}

查询my_index的映射

GET my_index/_mapping
{"my_index" : {"mappings" : {"properties" : {"group" : {"type" : "text","fields" : {"keyword" : {"type" : "keyword","ignore_above" : 256}}},"user" : {"properties" : {"first" : {"type" : "text","fields" : {"keyword" : {"type" : "keyword","ignore_above" : 256}}},"last" : {"type" : "text","fields" : {"keyword" : {"type" : "keyword","ignore_above" : 256}}}}}}}}
}

user.first和user.last字段被平铺成多值字段,alice和white之间的关联也丢失了。在查询alice和smith时,这个文档将将发生错误的匹配

GET my_index/_search
{"query": {"bool": {"must": [{ "match": { "user.first": "Alice" }},{ "match": { "user.last":  "Smith" }}]}}
}

所想要的只是user.first="Alice",user.last="Smith",本身是查询不到的,但是却查询出来了两条结果:

{"took" : 49,"timed_out" : false,"_shards" : {"total" : 1,"successful" : 1,"skipped" : 0,"failed" : 0},"hits" : {"total" : {"value" : 1,"relation" : "eq"},"max_score" : 0.5753642,"hits" : [{"_index" : "my_index","_type" : "_doc","_id" : "1","_score" : 0.5753642,"_source" : {"group" : "fans","user" : [{"first" : "John","last" : "Smith"},{"first" : "Alice","last" : "White"}]}}]}
}

删除“my_index”索引

DELETE my_index

重新创建my_index索引

PUT my_index
{"mappings": {"properties": {"user": {"type": "nested" }}}
}

重新插入数据

PUT my_index/_doc/1
{"group" : "fans","user" : [ {"first" : "John","last" :  "Smith"},{"first" : "Alice","last" :  "White"}]
}

再次查询user.first="Alice",user.last="Smith"时,查询不到数据

GET my_index/_search
{"query": {"bool": {"must": [{ "match": { "user.first": "Alice" }},{ "match": { "user.last":  "Smith" }}]}}
}

查询结果:

{"took" : 1,"timed_out" : false,"_shards" : {"total" : 1,"successful" : 1,"skipped" : 0,"failed" : 0},"hits" : {"total" : {"value" : 0,"relation" : "eq"},"max_score" : null,"hits" : [ ]}
}

5.商品上架接口实现

商品上架需要在es中保存spu信息并更新spu的状态信息,由于SpuInfoEntity与索引的数据模型并不对应,所以我们要建立专门的vo进行数据传输

1、商品上架接口

接口文档:商品系统 - 20、商品上架

POST /product/spuinfo/{spuId}/up

请求参数

分页数据

响应数据

{
"msg": "success",
"code": 0
}

功能效果

新增“com.cwh.common.to.es.SkuEsModel”类,代码如下:

package com.cwh.common.to.es;
import lombok.Data;
import java.math.BigDecimal;
import java.util.List;
@Data
public class SkuEsModel {private Long skuId;private Long spuId;private String skuTitle;private BigDecimal skuPrice;private String skuImg;private Long saleCount;private boolean hasStock;private Long hotScore;private Long brandId;private Long catalogId;private String brandName;private String brandImg;private String catalogName;private List attrs;@Datapublic static class Attr{private Long attrId;private String attrName;private String attrValue;}
}

编写商品上架的接口

修改“com.cwh.gulimall.product.controller.SpuInfoController”类,代码如下:

@PostMapping("spuinfo/{spuId}/up")
public R spuUp(@PathVariable("spuId") Long spuId){spuInfoService.up(spuId);return R.ok();
}

修改“com.cwh.gulimall.product.service.SpuInfoService”类,代码如下:

/*** 商品上架** @param spuId*/
void up(Long spuId);

由于每个spu对应的各个sku的规格参数相同,因此我们要将查询规格参数提前,只查询一次

修改“com.cwh.gulimall.product.service.impl.SpuInfoServiceImpl”类,代码如下:

@Override
public void up(Long spuId) {// 1、查出当前spuId对应的sku信息,品牌名字List<SkuInfoEntity> skus = skuInfoService.getSkuBySpuId(spuId);List<Long> skuIdList = skus.stream().map(SkuInfoEntity::getSkuId).collect(Collectors.toList());// 2.1、发送远程调用,库存系统查询是否有库存Map<Long, Boolean> stockMap = null;try {R r = wareFeignService.getSkusHasStock(skuIdList);TypeReference<List<SkuHasStockVo>> typeReference = new TypeReference<List<SkuHasStockVo>>() {};stockMap = r.getData(typeReference).stream().collect(Collectors.toMap(SkuHasStockVo::getSkuId, SkuHasStockVo::getHasStock));} catch (Exception e) {log.error("库存服务查询异常,原因:", e);}// 2.4、查询当前sku的所有可以被用来检索的规格属性List<ProductAttrValueEntity> baseAttrs = productAttrValueService.baseAttrListForSpu(spuId);List<Long> attrIds = baseAttrs.stream().map(attr -> 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 -> idSet.contains(item.getAttrId())).map(item -> {SkuEsModel.Attrs attrs1 = new SkuEsModel.Attrs();BeanUtils.copyProperties(item, attrs1);return attrs1;}).collect(Collectors.toList());// 2、封装每个sku的信息Map<Long, Boolean> finalStockMap = stockMap;List<SkuEsModel> upProducts = skus.stream().map(sku -> {// 组装需要的数据SkuEsModel esModel = new SkuEsModel();BeanUtils.copyProperties(sku, esModel);esModel.setSkuPrice(sku.getPrice());esModel.setSkuImg(sku.getSkuDefaultImg());// 2.1、是否有库存 hasStock,hotScoreif (finalStockMap == null) {esModel.setHasStock(true);} else {esModel.setHasStock(finalStockMap.get(sku.getSkuId()));}// 2.2、热度评分。0esModel.setHotScore(0L);// 2.3、查询品牌和分类的名字信息BrandEntity brand = brandService.getById(esModel.getBrandId());esModel.setBrandName(brand.getName());esModel.setBrandImg(brand.getLogo());CategoryEntity category = categoryService.getById(esModel.getCatalogId());esModel.setCatalogName(category.getName());// 2.4、设置检索属性esModel.setAttrs(attrsList);System.out.println("======================esModel" + esModel);return esModel;}).collect(Collectors.toList());// 3、将数据发送给es进行保存R r = searchFeignService.productStatusUp(upProducts);System.out.println("=========================" + r);if (r.getCode() == 0) {//远程调用成功// 3.1、修改当前spu的状态System.out.println("修改当前spu的状态");baseMapper.updateSpuStatus(spuId, ProductConstant.StatusEnum.SPU_UP.getCode());} else {// 远程调用失败// TODO 3.2、重复调用?接口幂等性;重试机制/*** Feign调用流程:* 1、构造请求数据,将对象转为json*      RequestTemplate template = buildTemplateFromArgs.create(argv);* 2、发送请求进行执行(执行成功会解码响应数据)*      executeAndDecode(template)* 3、执行请求会有重试机制*      while(true){*          try{*              executeAndDecode(template);*          }catch(){*              retryer.continueOrPropagate(e);*              throw ex;*              continue;*          }*      }*/}
}
2、查出当前spuId对应的sku信息,品牌名字

修改“com.cwh.gulimall.product.service.SkuInfoService”类,代码如下:

/*** 查出当前spuId对应的sku信息** @param spuId* @return*/
List<SkuInfoEntity> getSkuBySpuId(Long spuId);

修改“com.cwh.gulimall.product.service.impl.SkuInfoServiceImpl”类,代码如下:

@Override
public List<SkuInfoEntity> getSkuBySpuId(Long spuId) {List<SkuInfoEntity> list = this.list(new QueryWrapper<SkuInfoEntity>().eq("spu_id", spuId));return list;
}
3、封装每个sku的信息
3.1、发送远程调用,库存系统查询是否有库存

修改“com.cwh.gulimall.product.feign.WareFeignService”类,代码如下:

package com.cwh.gulimall.product.feign;
import com.cwh.common.utils.R;
import com.cwh.gulimall.product.vo.SkuHasStockVo;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import java.util.List;@FeignClient("gulimall-ware")
public interface WareFeignService {/*** 1、R设计的时候可以加上泛型* 2、直接返回我们想要的结果* 3、自己封装返回结果* @param skuIds* @return*/@PostMapping("/ware/waresku/hasStock")R getSkusHasStock(@RequestBody List<Long> skuIds);
}

修改”com.cwh.gulimall.ware.controller.WareSkuController”,代码如下:

/*** 查询sku是否有库存*/
@PostMapping("hasStock")
public R getSkusHasStock(@RequestBody List<Long> skuIds){// sku_id, stockList<SkuHasStockVo> vos = wareSkuService.getSkusHasStock(skuIds);return R.ok().setData(vos);
}

修改”com.cwh.gulimall.ware.service.WareSkuService”类,代码如下:

List getSkusHasStock(List skuIds);  

修改”com.cwh.gulimall.ware.service.WareSkuService”类,代码如下:

@Override
public List<SkuHasStockVo> getSkusHasStock(List<Long> skuIds) {List<SkuHasStockVo> collect = skuIds.stream().map(skuId -> {SkuHasStockVo vo = new SkuHasStockVo();// 查询sku的总库存量Long count = baseMapper.getSkuStock(skuId);vo.setSkuId(skuId);vo.setHasStock(count == null ? false : count > 0);return vo;}).collect(Collectors.toList());return collect;
}

修改“com.cwh.gulimall.ware.dao.WareSkuDao”类,代码如下

 Long getSkuStock(Long skuId);

修改“com.cwh.gulimall.ware.dao.WareSkuDao.xml”类,代码如下

<select id="getSkuStock" resultType="java.lang.Long">select sum(stock - stock_locked) from wms_ware_sku where sku_id=#{sku_id}
</select>

2.2、查询当前sku的所有可以被用来检索的规格属性

修改“com.cwh.gulimall.product.service.AttrService”类,代码如下:

/*** 在指定的所有属性集合里面,挑出检索属性** @param attrIds* @return*/
List<Long> selectSearchAttrs(List<Long> attrIds);

修改“com.cwh.gulimall.product.service.impl.AttrServiceImpl”类,代码如下:

@Override
public List<Long> selectSearchAttrs(List<Long> attrIds) {return baseMapper.selectSearchAttrIds(attrIds);
}
4、将数据发送给es进行保存

修改“com.cwh.gulimall.product.feign.SearchFeignService”类,代码如下:


package com.cwh.gulimall.product.feign;
import com.cwh.common.to.es.SkuEsModel;
import com.cwh.common.utils.R;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import java.util.List;@FeignClient("gulimall-search")
public interface SearchFeignService {
@PostMapping("search/save/product")
public R productStatusUp(@RequestBody List<SkuEsModel> skuEsModels);
}
4.1、创建gulimall-search

1、添加pom

<parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>2.3.5.RELEASE</version><relativePath/> <!-- lookup parent from repository -->
</parent><groupId>com.cwh.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></properties><dependencies><!--导入es的rest-high-level-client--><dependency><groupId>org.elasticsearch.client</groupId><artifactId>elasticsearch-rest-high-level-client</artifactId><version>7.4.2</version></dependency><dependency><groupId>com.auguigu.gulimall</groupId><artifactId>gulimall-commom</artifactId><version>0.0.1-SNAPSHOT</version></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope><exclusions><exclusion><groupId>org.junit.vintage</groupId><artifactId>junit-vintage-engine</artifactId></exclusion></exclusions></dependency></dependencies><build><plugins><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId></plugin></plugins></build>

2、修改yml

spring.cloud.nacos.discovery.server-addr=127.0.0.1:8848 
spring.application.name=gulimall-search 
server.port=12000 

3、添加主配置类

package com.cwh.gulimall.search;import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;@EnableDiscoveryClient
@SpringBootApplication(exclude = DataSourceAutoConfiguration.class)
public class GulimallSearchApplication {public static void main(String[] args) {SpringApplication.run(GulimallSearchApplication.class, args);}}

4、配置ElaseaticSearch

修改“com.cwh.gulimall.search.config.GulimallElasticSearchConfig”类,代表如下:

package com.cwh.gulimall.search.config;import org.apache.http.HttpHost;
import org.elasticsearch.client.*;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.client.RestOperations;/*** 1、导入依赖* 2、编写配置,给容器中注入一个RestHighLevelClient* 3、参照API操作*/@Configuration
public class GulimallElasticSearchConfig {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 restHighLevelClient() {RestClientBuilder builder = RestClient.builder(new HttpHost("192.168.43.125", 9200, "http"));return new RestHighLevelClient(builder);}
}

修改“com.cwh.gulimall.search.controller.ElasticSaveController”类,代表如下:

package com.cwh.gulimall.search.controller;import com.cwh.common.constant.ProductConstant;
import com.cwh.common.exception.BizCodeEnume;
import com.cwh.common.to.es.SkuEsModel;
import com.cwh.common.utils.R;
import com.cwh.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.util.List;@RequestMapping("/search/save")
@RestController
@Slf4j
public class ElasticSaveController {@AutowiredProductSaveService productSaveService;/*** 上架商品*/@PostMapping("/product")public R productStatusUp(@RequestBody List<SkuEsModel> skuEsModels) {boolean b;try {b = productSaveService.productStatusUp(skuEsModels);} catch (Exception e) {log.error("ElasticSaveController商品上架错误:{}", e);return R.error(BizCodeEnume.PRODUCT_UP_EXCEPTION.getCode(), BizCodeEnume.PRODUCT_UP_EXCEPTION.getMsg());}if (!b) {return R.ok();} else {return R.error(BizCodeEnume.PRODUCT_UP_EXCEPTION.getCode(), BizCodeEnume.PRODUCT_UP_EXCEPTION.getMsg());}}
}

修改“com.cwh.gulimall.search.service.ProductSaveService”类,代表如下:

public class EsConstant {public static final String PRODUCT_INDEX = "product"; //sku数据在es中的索引
}

修改“com.cwh.gulimall.search.service.impl.ProductSaveServiceImpl”类,代表如下:

package com.cwh.gulimall.search.service.impl;import com.alibaba.fastjson.JSON;
import com.cwh.common.to.es.SkuEsModel;
import com.cwh.gulimall.search.config.GulimallElasticSearchConfig;
import com.cwh.gulimall.search.constant.EsConstant;
import com.cwh.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;@Slf4j
@Service
public class ProductSaveServiceImpl implements ProductSaveService {@AutowiredRestHighLevelClient restHighLevelClient;@Overridepublic boolean productStatusUp(List<SkuEsModel> skuEsModels) throws IOException {// 保存到es// 1、给es中建立索引。product,建立好映射关系// 2、给es中保存这些数据// 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 jsonString = JSON.toJSONString(model);indexRequest.source(jsonString, 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 -> item.getId()).collect(Collectors.toList());log.info("商品上架完成:{},返回数据:{}", collect, bulk.toString());return b;}}
4.2、修改当前spu的状态

修改"com.cwh.gulimall.product.dao.SpuInfoDao"类,代码如下:

void updateSpuStatus(@Param("spuId") Long spuId,@Param("code") int code);

修改"com.cwh.gulimall.product.dao.SpuInfoDao.xml"类,代码如下:

<update id="updateSpuStatus">update pms_spu_info set publish_status=#{code},update_time=NOW() where id =#{spuId}
</update>

相关文章:

微服务实战——ElasticSearch(保存)

商品上架——ElasticSearch&#xff08;保存&#xff09; 0.商城架构图 1.商品Mapping 分析&#xff1a;商品上架在 es 中是存 sku 还是 spu &#xff1f; 检索的时候输入名字&#xff0c;是需要按照 sku 的 title 进行全文检索的检索使用商品规格&#xff0c;规格是 spu 的…...

leetcode练习 路径总和II

给你二叉树的根节点 root 和一个整数目标和 targetSum &#xff0c;找出所有 从根节点到叶子节点 路径总和等于给定目标和的路径。 叶子节点 是指没有子节点的节点。 示例 1&#xff1a; 输入&#xff1a;root [5,4,8,11,null,13,4,7,2,null,null,5,1], targetSum 22 输出&a…...

使用Three.js库创建的简单WebGL应用程序,主要用于展示具有不同透明度和缩放比例的圆环列

上述HTML文档是一个使用Three.js库创建的简单WebGL应用程序&#xff0c;主要用于展示具有不同透明度和缩放比例的圆环列。以下是代码的详细解释&#xff1a; HTML结构: 文档类型声明为HTML5。<html>标签设置了语言属性为英语&#xff08;lang"en"&#xff09;…...

Redis: 集群架构,优缺点和数据分区方式和算法

集群 集群指的就是一组计算机作为一个整体向用户提供一组网络资源 我就举一个简单的例子&#xff0c;比如百度&#xff0c;在北京和你在上海访问的百度是同一个服务器吗&#xff1f;答案肯定是不是的&#xff0c;每一个应用可以部署在不同的地方&#xff0c;但是我们提供的服务…...

负载均衡可以在网络模型的哪一层?

一、网络模型概述 网络模型是用于描述网络通信过程和网络服务的抽象框架。最常见的网络模型有两种&#xff1a;OSI&#xff08;开放式系统互联&#xff09;模型和TCP/IP模型。 OSI模型 OSI&#xff08;Open Systems Interconnection&#xff09;模型是由国际标准化组织&…...

YOLOv11改进 | 上采样篇 | YOLOv11引入CARAFE上采样

1. DySample介绍 1.1 摘要:特征上采样是许多现代卷积网络体系结构(如特征金字塔)中的关键操作。它的设计对于密集预测任务(如对象检测和语义/实例分割)至关重要。在本文中,我们提出了一个通用、轻量级、高效的特征重组算子CARAFE来实现这一目标.CARAFE有几个吸引人的特性…...

【Linux运维】grep命令粗浅学习

文章目录 1 背景介绍1.1 为什么要学习grep&#xff1f;1.2 grep是什么&#xff1f;1.3 grep可以做什么&#xff1f; 2 grep基本语法2.1 命令格式2.2 “PATTERN”部分中的正则表达式语法学习2.3 grep命令参数学习 3 典型案例3.1 匹配非空行&#xff0c;过滤纯空行3.2 匹配IPv4地…...

【Godot4.3】匀速和匀变速直线运动粒子

概述 本篇论述&#xff0c;如何用加速度在Godot中控制粒子运动。 匀速和匀变速直线运动的统一 以下是匀变速运动的速度和位移公式&#xff1a; v t v 0 a t x t v 0 t 1 2 a t 2 v_tv_0 at \\ x_tv_0t \frac{1}{2}at^2 vt​v0​atxt​v0​t21​at2 当a 0 时&#xf…...

基于Hive和Hadoop的用电量分析系统

本项目是一个基于大数据技术的用电量分析系统&#xff0c;旨在为用户提供全面的电力消耗信息和深入的用电量分析。系统采用 Hadoop 平台进行大规模数据存储和处理&#xff0c;利用 MapReduce 进行数据分析和处理&#xff0c;通过 Sqoop 实现数据的导入导出&#xff0c;以 Spark…...

一个简单的摄像头应用程序4

我们进一步完善了这个app01.py,我们优化了界面使其更人性化,下面介绍中包含了原有的功能及新增的功能: 创建和管理文件夹: create_folder 函数用于创建保存照片和视频的文件夹。 get_next_file_number 函数用于获取文件夹中下一个可用的文件编号。 图像处理: pil_to_cv 函…...

SpringBoot使用EasyPoi根据模板导出word or pdf

1、导出效果 1.1 wrod 1.2 pdf 2、依赖 <!--word--><dependency><groupId>cn.afterturn</groupId><artifactId>easypoi-base</artifactId><version>4.3.0</version></dependency><dependency><groupId>cn.…...

NVIDIA Hopper 架构深入

在 2022 年 NVIDIA GTC 主题演讲中,NVIDIA 首席执行官黄仁勋介绍了基于全新 NVIDIA Hopper GPU 架构的全新 NVIDIA H100 Tensor Core GPU。 文章目录 前言一、NVIDIA H100 Tensor Core GPU 简介二、NVIDIA H100 GPU 主要功能概述1. 新的流式多处理器 (SM) 具有许多性能和效率…...

AWS IoT Core for Amazon Sidewalk

目录 1 前言2 AWS IoT2.1 准备条件2.2 创建Credentials2.2.1 创建user2.2.2 配置User 2.3 本地CLI配置Credentials 3 小结 1 前言 在测试Sidewalk时&#xff0c;device发送数据&#xff0c;网关接收到&#xff0c;网关通过网络发送给NS&#xff0c;而此处用到的NS是AWS IoT&am…...

今日指数项目项目集成RabbitMQ与CaffienCatch

今日指数项目项目集成RabbitMQ与CaffienCatch 一. 为什么要集成RabbitMQ 首先CaffeineCatch 是作为一个本地缓存工具 使用CaffeineCatch 能够大大较少I/O开销 股票项目 主要分为两大工程 --> job工程(负责数据采集) , backend(负责业务处理) 由于股票的实时性也就是说 ,…...

C0005.Clion中移动ui文件到新目录后,报错问题的解决

报错问题如下 AutoUic error ------------- "SRC:/confirmwizardpage.cpp" includes the uic file "ui_confirmwizardpage.h", but the user interface file "confirmwizardpage.ui" could not be found in the following directories"SRC…...

基于STM32的智能家居灯光控制系统设计

引言 本项目将使用STM32微控制器实现一个智能家居灯光控制系统&#xff0c;能够通过按键、遥控器或无线模块远程控制家庭照明。该项目展示了如何结合STM32的外设功能&#xff0c;实现对灯光的智能化控制&#xff0c;提升家居生活的便利性和节能效果。 环境准备 1. 硬件设备 …...

06.useEffect

在 React 开发中,正确使用 useEffect 钩子对于优化组件性能至关重要。一个常见但容易被忽视的性能问题是在依赖数组中使用对象作为依赖项。这可能导致不必要的效果重新执行,从而影响应用性能。通过优先使用原始值(如字符串、数字)作为依赖项,我们可以显著提高组件的效率。…...

【设计模式-中介者模式】

定义 中介者模式&#xff08;Mediator Pattern&#xff09;是一种行为设计模式&#xff0c;通过引入一个中介者对象&#xff0c;来降低多个对象之间的直接交互&#xff0c;从而减少它们之间的耦合度。中介者充当不同对象之间的协调者&#xff0c;使得对象之间的通信变得简单且…...

树和二叉树知识点大全及相关题目练习【数据结构】

树和二叉树 要注意树和二叉树是两个完全不同的结构、概念&#xff0c;它们之间不存在包含之类的关系 树的定义 树&#xff08;Tree&#xff09;是n&#xff08;n≥0&#xff09;个结点的有限集&#xff0c;它或为空树&#xff08;n 0&#xff09;&#xff1b;或为非空树&a…...

ajax的原理,使用场景以及如何实现

AJAX 原理 AJAX&#xff08;Asynchronous JavaScript and XML&#xff09;是一种在网页中实现异步通信的技术&#xff0c;允许网页在不重新加载整个页面的情况下与服务器交换数据。这使得网页应用可以更加响应式和动态&#xff0c;提升用户体验。 AJAX 的核心原理是在后台通过…...

lock_guard和unique_lock学习总结

1.std::lock_guard std::lock_guard其实就是简单的RAII&#xff08;Resource Acquisition Is Initialization&#xff09;封装&#xff0c;资源获取即初始化。在构造函数中进行加锁&#xff0c;析构函数中进行解锁&#xff0c;这样可以保证函数退出时&#xff0c;锁一定被释放…...

数据挖掘-padans初步使用

目录标题 Jupyter Notebook安装启动 Pandas快速入门查看数据验证数据建立索引数据选取⚠️注意&#xff1a;排序分组聚合数据转换增加列绘图line 或 **&#xff08;默认&#xff09;&#xff1a;绘制折线图。bar&#xff1a;绘制条形图。barh&#xff1a;绘制水平条形图。hist&…...

小阿轩yx-案例:项目发布基础

小阿轩yx-案例&#xff1a;项目发布基础 前言 随着软件开发需求及复杂度的不断提高&#xff0c;团队开发成员之间如何更好地协同工作以确保软件开发的质量已经慢慢成为开发过程中不可回避的问题。Jenkins 自动化部署可以解决集成、测试、部署等重复性的工作&#xff0c;工具集…...

【HarmonyOS】时间处理Dayjs

背景 在项目中经常会使用要时间的格式转换&#xff0c;比如数据库返回一个Date数据&#xff0c;你需要转成2024-10-2的格式&#xff0c;鸿蒙的原生SDK中是没有办法实现的&#xff0c;因此&#xff0c;在这里介绍第三方封装好并且成熟使用的库Dayjs。 安装 切换到Entry文件夹下…...

论React Native 和 UniApp 的区别

1. 开发语言与框架 React Native: 使用 JavaScript 和 React 框架进行开发。采用了 React 的组件化开发模式&#xff0c;适合熟悉 React 生态的开发者。使用 JavaScript 编写的代码会通过 React Native 框架桥接到原生代码&#xff08;如 iOS 的 Swift 或 Android 的 Java/Kotl…...

微信小程序处理交易投诉管理,支持多小程序

大家好&#xff0c;我是小悟 1、问题背景 玩过微信小程序生态的&#xff0c;或许就有这种感受&#xff0c;如果收到投诉单&#xff0c;不会及时通知到手机端&#xff0c;而是每天早上10:00向小程序的管理员及运营者推送通知。通知内容为截至前一天24时该小程序账号内待处理的交…...

Pikachu-xss防范措施 - href输出 js输出

总体原则&#xff1a; 输入做过滤&#xff0c;输出做转义 过滤&#xff1a;根据业务需要进行过滤&#xff0c;如&#xff1a;输入点要求输入手机号&#xff0c;则只允许输入手机号格式的数字&#xff1b; 转义&#xff1a;所有输出到前端的数据&#xff0c;都根据输出点进行转…...

数据结构双向链表和循环链表

目录 一、循环链表二、双向链表三、循环双向链表 一、循环链表 循环链表就是首尾相接的的链表&#xff0c;就是尾节点的指针域指向头节点使整个链表形成一个循环&#xff0c;这就弥补了以前单链表无法在后面某个节点找到前面的节点&#xff0c;可以从任意一个节点找到目标节点…...

go基础面试题汇总第一弹

init函数是什么时候执行的? init的函数的作用是什么&#xff1f; 通常作为程序执行前包的初始化&#xff0c;例如mysql redis 等中间件的初始化 init函数的执行顺序是怎样的&#xff1f; 分不同情况来回答&#xff1a; 在同一个go文件里面如果有多个init方法&#xff0c;它们…...

Redis 实现分布式锁时需要考虑的问题

引言 分布式系统中的多个节点经常需要对共享资源进行并发访问&#xff0c;若没有有效的协调机制&#xff0c;可能会导致数据竞争、资源冲突等问题。分布式锁应运而生&#xff0c;它是一种保证在分布式环境中多个节点可以安全地访问共享资源的机制。而在Redis中&#xff0c;使用…...