第122章 分词器下载
第123章 修改liunx网络设置
我这里还是用的windows环境,等以后集群再上云服务安装环境,所以,这一章跳。
第124章 自定义词库
过
第125章 Elasticsearch-Rest-Client
使用后端整合ES,不能使用前端直接发送请求,因为不想对外暴露ES接口地址,为了安全。
引入流程
<dependency>
<groupId>org.elasticsearch.client</groupId>
<artifactId>elasticsearch-rest-high-level-client</artifactId>
<version>7.4.2</version>
</dependency>
编写配置,给容器中注入一个RestHighLeveLCLient,GulimallElasticSearchConfig
@Configuration
public class GulimallElasticSearchConfig {
// @Bean
// public RestHighLevelClient esRestClient(){
// RestHighLevelClient client = new RestHighLevelClient(
// RestClient.builder(new HttpHost("192.168.137.14", 9200, "http")));
// return client;
// }
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();
}
@Bean
public RestHighLevelClient esRestClient(){
RestHighLevelClient client = new RestHighLevelClient(
RestClient.builder(new HttpHost("127.0.0.1", 9200, "http")));
return client;
}
}
第126集 测试保存
@Test
public void indexData() throws IOException {
IndexRequest indexRequest = new IndexRequest("users");
indexRequest.id("1"); //数据的id
// indexRequest.source("userName","zhangsan","age",18,"gender","男");
User user = new User();
user.setUserName("zhangsan");
user.setAge(18);
user.setGender("男");
String jsonString = JSON.toJSONString(user);
indexRequest.source(jsonString, XContentType.JSON); //要保存的内容
//执行操作
IndexResponse index = client.index(indexRequest, GulimallElasticSearchConfig.COMMON_OPTIONS);
//提取有用的响应数据
System.out.println(index);
}
@Getter
@Setter
class User {
private String userName;
private String gender;
private Integer age;
}
第127章 测试复杂查询
这块最好自己动手写写,要不然容易忘记。
@Test
public void searchState() throws IOException {
//1. 创建检索请求
SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
// sourceBuilder.query(QueryBuilders.termQuery("city", "Nicholson"));
// sourceBuilder.from(0);
// sourceBuilder.size(5);
// sourceBuilder.timeout(new TimeValue(60, TimeUnit.SECONDS));
QueryBuilder matchQueryBuilder = QueryBuilders.matchQuery("state", "AK");
// .fuzziness(Fuzziness.AUTO)
// .prefixLength(3)
// .maxExpansions(10);
sourceBuilder.query(matchQueryBuilder);
SearchRequest searchRequest = new SearchRequest();
searchRequest.indices("bank");
searchRequest.source(sourceBuilder);
//2. 执行检索
SearchResponse searchResponse = client.search(searchRequest, RequestOptions.DEFAULT);
System.out.println(searchResponse);
}
@Test
public void searchData() throws IOException {
//1. 创建检索请求
SearchRequest searchRequest = new SearchRequest();
//1.1)指定索引
searchRequest.indices("bank");
//1.2)构造检索条件
SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
sourceBuilder.query(QueryBuilders.matchQuery("address", "Mill"));
//1.2.1)按照年龄分布进行聚合
TermsAggregationBuilder ageAgg = AggregationBuilders.terms("ageAgg").field("age").size(10);
sourceBuilder.aggregation(ageAgg);
//1.2.2)计算平均年龄
AvgAggregationBuilder ageAvg = AggregationBuilders.avg("ageAvg").field("age");
sourceBuilder.aggregation(ageAvg);
//1.2.3)计算平均薪资
AvgAggregationBuilder balanceAvg = AggregationBuilders.avg("balanceAvg").field("balance");
sourceBuilder.aggregation(balanceAvg);
System.out.println("检索条件:" + sourceBuilder);
searchRequest.source(sourceBuilder);
//2. 执行检索
SearchResponse searchResponse = client.search(searchRequest, RequestOptions.DEFAULT);
System.out.println("检索结果:" + searchResponse);
//3. 将检索结果封装为Bean
SearchHits hits = searchResponse.getHits();
SearchHit[] searchHits = hits.getHits();
for (SearchHit searchHit : searchHits) {
String sourceAsString = searchHit.getSourceAsString();
Account account = JSON.parseObject(sourceAsString, Account.class);
System.out.println(account);
}
//4. 获取聚合信息
Aggregations aggregations = searchResponse.getAggregations();
Terms ageAgg1 = aggregations.get("ageAgg");
for (Terms.Bucket bucket : ageAgg1.getBuckets()) {
String keyAsString = bucket.getKeyAsString();
System.out.println("年龄:" + keyAsString + " ==> " + bucket.getDocCount());
}
Avg ageAvg1 = aggregations.get("ageAvg");
System.out.println("平均年龄:" + ageAvg1.getValue());
Avg balanceAvg1 = aggregations.get("balanceAvg");
System.out.println("平均薪资:" + balanceAvg1.getValue());
}
第128章 ES在项目中的使用场景
ES承担项目内的所有全文检索功能,比如商品的检索功能,也负责日志的检索功能,日志的ELK使用的技术为Elasticsearch,LogStach,Kibana
ES的速度比mysql速度快很多,因为其是将数据存入内存之中, 那有人可能就要问了,商品数据那么大全部存内容中,可以吗?答:ES是一个天然支持分布式的架构,可以使用多个集群,保证内存的充足,将数据分片存储,即可解决这个问题。
之后使用ES要达成一个共识,就是内存要比硬盘存储要贵很多,所以只保存页面有用的数据,没用的我们都不要保存,如果要查一个商品的详细信息,我们可以先查ES获取sku,然后再查数据库,得到其他有用的信息。
商品的上架
上架的商品才可以在网站展示。 上架的商品需要可以被检索。
分析:商品上架在 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 单独一个索引可能涉及的 问题 。检索商品的名字,如“手机”,对应的 spu 有很多,我们要分析出这些 spu 的所有关联属性,再做一次查询,就必须将所有 spu_id 都发出去。假设有 1 万个数据,数据传输一次就10000*4=4MB ;并发情况下假设 1000 检索请求,那就是 4GB 的数据,,传输阻塞时间会很长,业务更加无法继续。所以,我们如下设计,这样才是文档区别于关系型数据库的地方,宽表设计,不能去考虑数据库范式。我们使用空间去换时间。
{
"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"
}
}
}
}
}
}
第129集 nested数据场景的使用
我推荐一个博客 写的不错
nested给我的感觉就是,将两个东西绑定起来,只能捆绑查询。这一块还需要慢慢吸收
6.ELK之Elasticsearch嵌套(Nested)类型_elasticsearch nested-CSDN博客
第130集 商品上架,保存数据到ES中
这里我大概讲下业务逻辑吧。就是商品上架之后,发送请求传出spuid,想要将数据按照ES中设定的映射进行存储结构,所以,首先查出spuid对应的sku信息,查出商品的spu规格属性,同时过滤掉这些数据中的库存为空的数据。数据准备好后,然后发送请求给ES服务。
第131集
同上面集所讲
第132集
远程调用的讲过好多次了,业务逻辑也比较简单,过。
第133集 发给ES进行保存
首先在ES服务中添加controller
ElasticSaveController
@Slf4j
@RequestMapping(value = "/search/save")
@RestController
public class ElasticSaveController {
@Autowired
private ProductSaveService productSaveService;
/**
* 上架商品
* @param skuEsModels
* @return
*/
@PostMapping(value = "/product")
public R productStatusUp(@RequestBody List<SkuEsModel> skuEsModels) {
boolean status=false;
try {
status = productSaveService.productStatusUp(skuEsModels);
} catch (IOException e) {
//log.error("商品上架错误{}",e);
return R.error(BizCodeEnum.PRODUCT_UP_EXCEPTION.getCode(), BizCodeEnum.PRODUCT_UP_EXCEPTION.getMessage());
}
if(status){
return R.error(BizCodeEnum.PRODUCT_UP_EXCEPTION.getCode(), BizCodeEnum.PRODUCT_UP_EXCEPTION.getMessage());
}else {
return R.ok();
}
}
}
ProductSaveServiceImpl,这个的含义就是调用esRestClient的API,类似于用postman发送请求一样,准备好json文件,准备好格式,准备好请求方式。获得错误的结果的返回信息。
@Override
public boolean productStatusUp(List<SkuEsModel> skuEsModels) throws IOException {
//1.在es中建立索引,建立号映射关系(doc/json/product-mapping.json)
//2. 在ES中保存这些数据
BulkRequest bulkRequest = new BulkRequest();
for (SkuEsModel skuEsModel : skuEsModels) {
//构造保存请求
IndexRequest indexRequest = new IndexRequest(EsConstant.PRODUCT_INDEX);
indexRequest.id(skuEsModel.getSkuId().toString());
String jsonString = JSON.toJSONString(skuEsModel);
indexRequest.source(jsonString, XContentType.JSON);
bulkRequest.add(indexRequest);
}
BulkResponse bulk = esRestClient.bulk(bulkRequest, GulimallElasticSearchConfig.COMMON_OPTIONS);
//TODO 如果批量错误
boolean hasFailures = bulk.hasFailures();
List<String> collect = Arrays.asList(bulk.getItems()).stream().map(item -> {
return item.getId();
}).collect(Collectors.toList());
log.info("商品上架完成:{}",collect);
return hasFailures;
}
第134集,第135集 调试代码以及feign源码讲解
这一节主要学习如何debug,以及feign源码讲解。我这一块也不太懂,等以后再补吧。QAQ
我这里有个报错,排查出没有加@Param("attrIds")导致的。
List<Long> selectSearchAttrIds(@Param("attrIds") List<Long> attrIds);
第136集 thymeleaf渲染首页
动静分离
静:图片,js、css等静态资源(以实际文件存在的方式)
动:服务器需要处理的请求,与相关业务相关的前端页面在微服务之中,保证每个微服务都可以独立运行,独立部署。
这个我遇见个问题,css没有加载出来
将所有的 href中的static都去掉,就变为这种 href="index/css/swiper-3.4.2.min.css"就可以显示了
之后改好就行了,前端内容过
第137集 整合dev-tool渲染一级分类
这一节主要讲前端的知识,后端配置简单,过
第138集 渲染二级三级分类数据
这一节也没什么新东西,过
第139集 Nginx配置域名访问环境
通过访问域名的方式访问网站。
我这里只用的windows环境 nginx.conf,可以成功跳转
#user nobody;
worker_processes 1;
#error_log logs/error.log;
#error_log logs/error.log notice;
#error_log logs/error.log info;
#pid logs/nginx.pid;
events {
worker_connections 1024;
}
http {
include mime.types;
default_type application/octet-stream;
#log_format main '$remote_addr - $remote_user [$time_local] "$request" '
# '$status $body_bytes_sent "$http_referer" '
# '"$http_user_agent" "$http_x_forwarded_for"';
#access_log logs/access.log main;
sendfile on;
#tcp_nopush on;
#keepalive_timeout 0;
keepalive_timeout 65;
#gzip on;
server {
listen 80;
server_name localhost;
#charset koi8-r;
#access_log logs/host.access.log main;
location / {
proxy_pass http://127.0.0.1:10001;
}
location /test {
root html;
index index.html index.htm;
}
#error_page 404 /404.html;
# redirect server error pages to the static page /50x.html
#
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root html;
}
# proxy the PHP scripts to Apache listening on 127.0.0.1:80
#
#location ~ \.php$ {
# proxy_pass http://127.0.0.1;
#}
# pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000
#
#location ~ \.php$ {
# root html;
# fastcgi_pass 127.0.0.1:9000;
# fastcgi_index index.php;
# fastcgi_param SCRIPT_FILENAME /scripts$fastcgi_script_name;
# include fastcgi_params;
#}
# deny access to .htaccess files, if Apache's document root
# concurs with nginx's one
#
#location ~ /\.ht {
# deny all;
#}
}
# another virtual host using mix of IP-, name-, and port-based configuration
#
#server {
# listen 8000;
# listen somename:8080;
# server_name somename alias another.alias;
# location / {
# root html;
# index index.html index.htm;
# }
#}
# HTTPS server
#
#server {
# listen 443 ssl;
# server_name localhost;
# ssl_certificate cert.pem;
# ssl_certificate_key cert.key;
# ssl_session_cache shared:SSL:1m;
# ssl_session_timeout 5m;
# ssl_ciphers HIGH:!aNULL:!MD5;
# ssl_prefer_server_ciphers on;
# location / {
# root html;
# index index.html index.htm;
# }
#}
}
第140集 再修改下nginx配置
修改 nginx.conf文件,这里注意几个点吧,分号注意不要少,更新完配置之后一定要在任务管理器里关掉nginx,之后再点击nginx.exe,重启。然后注意如果重启后,任务管理器里面没有nginx,那说明你的nginx.conf 文件语法格式写错了
#user nobody;
worker_processes 1;
#error_log logs/error.log;
#error_log logs/error.log notice;
#error_log logs/error.log info;
#pid logs/nginx.pid;
events {
worker_connections 1024;
}
http {
include mime.types;
default_type application/octet-stream;
#log_format main '$remote_addr - $remote_user [$time_local] "$request" '
# '$status $body_bytes_sent "$http_referer" '
# '"$http_user_agent" "$http_x_forwarded_for"';
#access_log logs/access.log main;
sendfile on;
#tcp_nopush on;
#keepalive_timeout 0;
keepalive_timeout 65;
upstream gulimall{
server 127.0.0.1:88;
}
#gzip on;
server {
listen 80;
server_name localhost;
#charset koi8-r;
#access_log logs/host.access.log main;
location / {
proxy_set_header Host $host;
proxy_pass http://gulimall;
}
location /test {
root html;
index index.html index.htm;
}
#error_page 404 /404.html;
# redirect server error pages to the static page /50x.html
#
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root html;
}
# proxy the PHP scripts to Apache listening on 127.0.0.1:80
#
#location ~ \.php$ {
# proxy_pass http://127.0.0.1;
#}
# pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000
#
#location ~ \.php$ {
# root html;
# fastcgi_pass 127.0.0.1:9000;
# fastcgi_index index.php;
# fastcgi_param SCRIPT_FILENAME /scripts$fastcgi_script_name;
# include fastcgi_params;
#}
# deny access to .htaccess files, if Apache's document root
# concurs with nginx's one
#
#location ~ /\.ht {
# deny all;
#}
}
# another virtual host using mix of IP-, name-, and port-based configuration
#
#server {
# listen 8000;
# listen somename:8080;
# server_name somename alias another.alias;
# location / {
# root html;
# index index.html index.htm;
# }
#}
# HTTPS server
#
#server {
# listen 443 ssl;
# server_name localhost;
# ssl_certificate cert.pem;
# ssl_certificate_key cert.key;
# ssl_session_cache shared:SSL:1m;
# ssl_session_timeout 5m;
# ssl_ciphers HIGH:!aNULL:!MD5;
# ssl_prefer_server_ciphers on;
# location / {
# root html;
# index index.html index.htm;
# }
#}
}
然后网关的yml那个直接复制老师的就可以了。成功