谷粒商城学习笔记

一、项目简介

1、项目背景

1)、电商模式

在这里插入图片描述

2)、谷粒商城

谷粒商城是一个 B2C 模式的电商平台,销售自营商品给客户。

2、项目架构图

1、项目微服务架构图

在这里插入图片描述

2、微服务划分图

在这里插入图片描述

3、项目技术&特色

在这里插入图片描述

4、项目前置要求

在这里插入图片描述

二、分布式基础概念

2、集群&分布式&节点

分布式是指将不同的业务分布在不同的地方。
集群指的是将几台服务器集中在一起,实现同一业务。
例如:京东是一个分布式系统,众多业务运行在不同的机器,所有业务构成一个大型的业务集群。每一个小的业务,比如用户系统,访问压力大的时候一台服务器是不够的。我们就 应该将用户系统部署到多个服务器,也就是每一个业务系统也可以做集群化; 分布式中的每一个节点,都可以做集群。 而集群并不一定就是分布式的。 节点:集群中的一个服务器

4、负载均衡

在这里插入图片描述

7、服务熔断&服务降级

1)、服务熔断 a. 设置服务的超时,当被调用的服务经常失败到达某个阈值,我们可以开 启断路保护机制,后来的请求不再去调用这个服务。本地直接返回默认 的数据
2)、服务降级 a. 在运维期间,当系统处于高峰期,系统资源紧张,我们可以让非核心业 务降级运行。降级:某些服务不处理,或者简单处理【抛异常、返回 NULL、 调用 Mock 数据、调用 Fallback 处理逻辑】。

三、项目架构

架构图

在这里插入图片描述

微服务划分图

在这里插入图片描述

四、环境搭建

docker

centos安装docker

https://docs.docker.com/engine/install/centos/

设置开机自启

systemctl enable docker

设置开机自启docker容器mysql服务

docker update mysql --restart=always

配置阿里云镜像加速

sudo mkdir -p /etc/docker
sudo tee /etc/docker/daemon.json <<-‘EOF’
{
“registry-mirrors”: [“https://76ra8yjq.mirror.aliyuncs.com”]
}
EOF
sudo systemctl daemon-reload
sudo systemctl restart docker

docker安装mysql

启动docker镜像

docker run -p 3306:3306
–name mysql
-v /mydata/mysql/log:/var/log/mysql
-v /mydata/mysql/data:/var/lib/mysql
-v /mydata/mysql/conf:/etc/mysql
-e MYSQL_ROOT_PASSWORD=root
-d mysql:5.7
参数说明-p 3306:3306:将容器的 3306 端口映射到主机的 3306 端口 -v /mydata/mysql/conf:/etc/mysql:将配置文件夹挂载到主机 -v /mydata/mysql/log:/var/log/mysql:将日志文件夹挂载到主机 -v /mydata/mysql/data:/var/lib/mysql/:将配置文件夹挂载到主机 -e MYSQL_ROOT_PASSWORD=root:初始化 root 用户的密码

进入docker容器

docker exec -it f1178d5b0bd8 /bin/bash

修改mysql配置文件

vi /mydata/mysql/conf/my.cnf

[client]
default-character-set=utf8
[mysql]
default-character-set=utf8
[mysqld]
init_connect=‘SET collation_connection = utf8_unicode_ci’
init_connect=‘SET NAMES utf8’
character-set-server=utf8
collation-server=utf8_unicode_ci
skip-character-set-client-handshake
skip-name-resolve

使配置文件生效

docker ps
docker restart mysql

docker安装redis

挂载可能会把文件当成目录,所以预先创建好文件

mkdir -p /mydata/redis/conf
touch /mydata/redis/conf/redis.conf
docker run -p 6379:6379 --name redis -v /mydata/redis/data:/data \
-v /mydata/redis/conf/redis.conf:/etc/redis/redis.conf \
-d redis redis-server /etc/redis/redis.conf

连接容器内redis客户端

docker exec -it redis redis-cli

当前数据保存在内存中,需要配置持久化规则

vim /mydata/redis/conf/redis.conf
appendonly yes
docker restart redis

maven

在这里插入图片描述

git使用ssh连接

在这里插入图片描述

git管理仓库
在这里插入图片描述

开发生产者模型:开发在dev分支,开发完成合并到master分支

idea新建项目
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

创建微服务项目

在这里插入图片描述
在这里插入图片描述
从模块中复制一个pom文件放在根目录下,进行修改

可以取消这两个勾选,它会检查分析代码,包括TODO
在这里插入图片描述
在这里插入图片描述

数据库设计

在这里插入图片描述

整合人人开源-后台管理系统

下载

https://gitee.com/renrenio 下载renren-fast和renren-fast-vue
快速搭建后台管理系统:renren-fast整合到gulimall项目中;vscode打开renren-fast-vue配置开发环境
npm install npm run dev

配置前端运行环境

node.js

在这里插入图片描述

代码生成器(逆向工程)

下载:https://gitee.com/renrenio/renren-generator.git

整合进gulimall项目,修改配置文件,先生成product模块基本文件在这里插入图片描述

在这里插入图片描述
运行服务,访问localhost,点击生成下载.zip压缩文件,解压放进gulimall-product
在这里插入图片描述
缺失很多依赖,创建common模块,引入依赖和renren-fast中的部分依赖文件
shift+f6修改模块名称
修改逆向工程,不使用shiro依赖,重新生成代码
在这里插入图片描述

分布式组件

服务注册

1、引入依赖
在这里插入图片描述
2、开启服务注册
在这里插入图片描述
3、编写配置文件
在这里插入图片描述

feign

1、导入依赖
在这里插入图片描述
2、开启支持feign远程调用
在这里插入图片描述

3、编写声明式接口
在这里插入图片描述

配置中心

如何使用nacos作为配置中心统一管理配置

1、引入依赖

    <dependency>
        <groupId>com.alibaba.cloud</groupId>
        <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
    </dependency>

2、创建一个bootstrap.properties文件
spring.application.name=guli-coupon
spring.cloud.nacos.config.server-addr=127.0.0.1:8848

3、需要给配置中心默认添加一个叫 数据集(Data Id)guli-coupon.properties。默认规则:应用名.properties

4、给 应用名.properties添加任何配置

5、动态获取配置
@RefreshScope:动态获取并刷新配置
@Value(“${配置项的名称}”):获取配置
配置中心优先级>本地配置优先级

gateway

前端内容

ES6

ECMAScript 6.0(以下简称 ES6,ECMAScript 是一种由 Ecma 国际(前身为欧洲计算机制造商 协会,英文名称是 European Computer Manufacturers Association)通过 ECMA-262标准化的脚本 程序设计语言)是 JavaScript 语言的下一代标准

模块化

vue

1、文件夹内终端敲入命令:npm init -y
2、npm install vue
3、引入vue,script中src=“./node_modules/vue/dist/vue.js”
在这里插入图片描述
vue生命周期

vue模块化开发

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

目录结构

在这里插入图片描述

项目开发

商品服务

1、数据库导入数据
2、运行renren-vue和后台

配置网关

请求转发Service Unavailable问题

https://blog.csdn.net/kitahiragawa/article/details/124229580

原因:这是由于版本不兼容引发的问题,我当前使用的springcloud alibaba版本为2021.0.1.0,而springcloud alibaba在2020版之后不支持ribbon,而springcloud gateway使用ribbon,就导致了gateway无法路由到目标服务

跨域问题

不是简单请求就需要发送一个Options方式的预检请求

https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Access_control_CORS

在这里插入图片描述

解决跨域

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

product模块逻辑删除

1)、配置全局的逻辑删除规则(省略)
2)、配置逻辑删除的组件Bean(省略)
3)、给Bean加上逻辑删除注解@TableLogic

商品服务-三级分类

商品服务-品牌管理

oss上传

根据github上springCloudAlibaba演示demo引入
https://github.com/alibaba/spring-cloud-alibaba

在这里插入图片描述


nacos config配置:
yml文件配置
在这里插入图片描述

注意点:需要加如下依赖

<!–cloud新版本默认将bootstrap移除了,所以需要添加如下依赖–>

    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-bootstrap</artifactId>
    </dependency>

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

table显示图片

table->自定义模板->在里面配置template,scope内容自定义显示
在这里插入图片描述
异常排查:
在这里插入图片描述

1、main.js是否引入elementui
2、main.js引入的是src下的elementui
3、elemnetui/index.js中没有引入image
4、去element官网快速上手配置引入image

jsr303数据校验

JSR303
1)、给Bean添加校验注解:javax.validation.constraints,并定义自己的message提示
2)、开启校验功能@Valid
效果:校验错误以后会有默认的响应;
3)、给校验的bean后紧跟一个BindingResult,就可以获取到校验的结果
4)、分组校验(多场景的复杂校验)
1)、 @NotBlank(message = “品牌名必须提交”,groups = {AddGroup.class,UpdateGroup.class})
给校验注解标注什么情况需要进行校验
2)、@Validated({AddGroup.class})
3)、默认没有指定分组的校验注解@NotBlank,在分组校验情况@Validated({AddGroup.class})下不生效,只会在@Validated生效;
5)、自定义校验
1)、编写一个自定义的校验注解
2)、编写一个自定义的校验器 ConstraintValidator
3)、关联自定义的校验器和自定义的校验注解

统一异常处理

统一的异常处理
@ControllerAdvice
1)、编写异常处理类,使用@ControllerAdvice。
2)、使用@ExceptionHandler标注方法可以处理的异常。

spu、sku

SPU:Standard Product Unit(标准化产品单元)
SKU:Stock Keeping Unit(库存量单位)

iphoneX 是 SPU、MI 8 是 SPU
iphoneX 64G 黑曜石 是 SKU
MI8 8+64G+黑色 是 SKU

Object划分

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

项目运行内存配置

1、新建、配置compound
在这里插入图片描述
2、配置最大占用内存
在这里插入图片描述
在这里插入图片描述

@Transactional分析

断点执行看不到之前代码的入库数据:

1、因为事务没有提交之前,数据是读不出来的;
2、mysql默认的隔离级别是可重复读,就是最起码读到已经提交了的数据,为了测试方便,将当前会话的隔离级别等级设置为读未提交
SET SESSION TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;
3、然后就可以很方便得查看数据库变化

让@Transactional事务遇到异常不回滚

1、可以使用try catch捕获异常
2、
在这里插入图片描述

库存&采购流程

采购需求->合并整单->采购单->库存

在这里插入图片描述

基础篇总结

#在这里插入图片描述

分布式高级篇

ElasticSearch

官方文档:https://www.elastic.co/guide/en/elasticsearch/reference/current/index.html
官方中文:https://www.elastic.co/guide/cn/elasticsearch/guide/current/foreword_id.html 社区中文: https://es.xiaoleilu.com/index.html http://doc.codingdict.com/elasticsearch/0/

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

1、下载镜像文件

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

2、创建实例

1、ElasticSearch

mkdir -p /mydata/elasticsearch/config
mkdir -p /mydata/elasticsearch/data
echo “http.host: 0.0.0.0” >> /mydata/elasticsearch/config/elasticsearch.yml
chmod -R 777 /mydata/elasticsearch/ 保证权限
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.2

http.host: 0.0.0.0 : es可以被远程任何机器访问
-e:配置参数
-p:暴露端口-9200是我们给es的restAPI发请求的端口-9300是es在分布式集群状态下的节点通信端口
discovery.type:单节点模式
ES_JAVA_OPTS:不指定的话es一启动会将内存全部占用,虚拟机就卡死了
-d:后台启动

以后再外面装好插件重启即可;
特别注意: -e ES_JAVA_OPTS=“-Xms64m -Xmx256m” \ 测试环境下,设置 ES 的初始内存和最大内存,否则导 致过大启动不了 ES

查看日志:docker logs elasticsearch

访问:http://192.168.233.141:9200/

2、Kibana

docker run --name kibana -e ELASTICSEARCH_HOSTS=http://192.168.233.141:9200 -p 5601:5601 \ -d kibana:7.4.2

访问:http://192.168.233.141:5601/app/kibana#/dev_tools/console?_g=()

初步检索

在这里插入图片描述
在这里插入图片描述
_seq_no可以作乐观锁
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

样本数据 acounts.json

https://github.com/elastic/elasticsearch/blob/7.5/docs/src/test/resources/accounts.json

进阶检索

https://www.elastic.co/guide/en/elasticsearch/reference/7.4/getting-started-search.html

启动docker容器

docker ps -a
docker start containerId
开机自启
docker update containerId --restart=always

mapping

type是keyword类型:不会进行全文检索,只会精确匹配
type是text类型的:全文检索,保存数据的时候进行分词,检索的时候按照分词进行匹配

ES5.0及以后的版本取消了string类型,将原先的string类型拆分为text和keyword两种类型。它们的区别在于text会对字段进行分词处理而keyword则不会进行分词。
也就是说如果字段是text类型,存入的数据会先进行分词,然后将分完词的词组存入索引,而keyword则不会进行分词,直接存储。
text类型的数据被用来索引长文本,例如电子邮件主体部分或者一款产品的介绍,这些文本会被分析,在建立索引文档之前会被分词器进行分词,转化为词组。经过分词机制之后es允许检索到该文本切分而成的词语,但是text类型的数据不能用来过滤、排序和聚合等操作。
keyword类型的数据可以满足电子邮箱地址、主机名、状态码、邮政编码和标签等数据的要求,不进行分词,常常被用来过滤、排序和聚合

安装ik分词器

1、下载https://github.com/medcl/elasticsearch-analysis-ik/releases?after=v6.4.2
2、解压到plugin文件夹,装在ik目录中
在这里插入图片描述
3、进入es容器:docker exec -it 容器id /bin/bash,elasticsearch-plugin list
在这里插入图片描述
4、重启容器
docker restart elasticsearch

docker安装nginx

在这里插入图片描述

es自定义词库

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

springboot整合es

为什么不用js直接操作es
1、安全性
2、js对es支持度低

分析:
1、方便检索
{
skuId:1
spuId:11
skuTitle:华为xx
price:998
saleCount:99
attrs:[{尺寸:4寸},{CPU:高通945},{分辨率:全高清}]
}
冗余:100w商品 每个商品20个属性,假设合起来2kb数据 100w2kb=2000MB=2G 商城系统多了2G
2、
sku索引{
skuId:1
spuId:11
xxxxxx
}
attr索引{
spuId:11
attrs:[{尺寸:4寸},{CPU:高通945},{分辨率:全高清}]
}
问题: 搜索’小米’ 粮食、手机、电器
1w个 4k个spu
分步:4k个spu对应的所有可能属性
esClient请求搜索4k个spuId,每个都是long型数据 4k
8=32000byte=32kb
1w人在商城检索商品:1w*32kb=320MB 一次光数据传输320mb,高并发情况下会网络阻塞
总结:空间和时间不能二者兼得,所以我们这里选择第一种设计模型

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:false 表示该属性不可用来被检索
doc_values: true 表示可以用来作聚合、排序、脚本等操作,进行空间节省
“type”: “nested” 表示属性子对象中的某些值进行检索

nginx+windows搭建域名访问环境

在这里插入图片描述

域名解析:我们在浏览器上敲gulimall.com,windows如何知道哪个域名对应哪个ip地址?
1、查看系统内部系统映射规则(网卡带我们转过去)
2、如果系统没告诉gulimall.com在哪个地址,那就去网络上的dns(比如114.114.114.114,8.8.8.8),解析出域名(dns中保存了每个域名对应每个ip地址,在公网保存),查看ip映射规则

在这里插入图片描述

1、配置hosts
192.168.233.141 gulimall.com,http://gulimall.com:9200/测试配置是否成功
2、配置nginx配置文件,请求发到网关
在这里插入图片描述
3、在网关配置转发策略
在这里插入图片描述
4、域名请求依然是404
在这里插入图片描述
因为nginx代理给网关的时候,会丢失请求的host信息
配置proxy_set_header Host $host
在这里插入图片描述

压力测试

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

java内存模型

压测数据

压测内容路径压测线程数吞吐量/s90%响应时间99%响应时间
Nginxhttp://192.168.233.141:80/50233511944
Gatewayhttp://localhost:88/5010367831
简单服务localhost:10000/hello5011341817
首页一级菜单渲染localhost:1000050270(db,thymeleaf)267365
首页渲染(thymeleaf开缓存)localhost:1000050290251365
首页渲染(thymeleaf开缓存、 优化数据库、降低日志级别日 志(关日志))localhost:1000050700105183
三级分类数据获取localhost:10000/index/catalog.json502(db)/8(加索引)
三级分类(优化业 务,减少与数据库的交互)localhost:10000/index/catalog.json50111571896
三 级 分 类 ( 使 用 redis 作为缓存)localhost:10000/index/catalog.json50411153217
首页全量数据获取localhost:10000507(静态资源)
Nginx+Gateway50
Gateway+简单服务localhost:88/hello50312630125
全链路gulimall.com/hello5080088310

监控内存使用率等数据:docker stats

测nginx
在这里插入图片描述
在这里插入图片描述
简单服务
在这里插入图片描述

首页全量数据获取
在这里插入图片描述
在这里插入图片描述
开thymeleaf缓存、优化数据库、关日志、nginx动静分离
效果还是不明显,吞吐量从7-11
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述开thymeleaf缓存、优化数据库、关日志、nginx动静分离、调节内存
在这里插入图片描述

-Xms 堆内存的初始大小,默认为物理内存的1/64
-Xmx 堆内存的最大大小,默认为物理内存的1/4
-Xmn 堆内新生代的大小。通过这个值也可以得到老生代的大小:-Xmx减去-Xmn
-Xss 设置每个线程可使用的内存大小,即栈的大小

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

测试服务崩溃
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
三 级 分 类 ( 使 用 redis 作为缓存)
在这里插入图片描述

在这里插入图片描述
问题:压测报错
在这里插入图片描述

在这里插入图片描述

解决:
TODO:压测产生堆歪内存溢出:OutOfDirectMemoryError
1):springboot2.0以后默认使用lettuce作为操作redis的客户端,它使用netty来进行网络通信
2):lettuce的bug导致netty堆外内存溢出 -Xmx300m netty如果没有指定堆外内存,默认使用-Xmx300m
可以通过-Dio.netty.maxDirectMemory进行设置
解决方案:不能使用-Dio.netty.maxDirectMemory只去调大堆外内存(调大内存只能延缓报错的时间)
1、升级lettuce客户端
2、切换使用jedis客户端
在这里插入图片描述

总结:
1、中间件越多,性能损失越大,大多都损失在网络交互了;
2、业务:
 Db(MySQL 优化)  模板的渲染速度(缓存)  静态资源

优化数据库
新建索引
在这里插入图片描述

动静分离

1、在nginx的html文件夹中新建static文件夹
2、将guli-product中的index下静态资源复制到static文件夹下
3、将href和<script src =""等各路径指向的静态资源加/static路径
在这里插入图片描述
在这里插入图片描述

4、修改nginx配置文件(这里的share文件夹是因为拷贝的es分词器的,应该是docker默认路径)
在这里插入图片描述

缓存与分布式锁

ttl命令:查看key剩余的过期时间

会产生数据一致性问题

在这里插入图片描述

redisTemplate和Lettuce、Jedis关系
Lettuce、Jedis都是操作redis的底层客户端,spring再次封装成redisTemplate
在这里插入图片描述
在这里插入图片描述

所以不管是Lettuce还是Jedis连接redis,都是兼容的

高并发下缓存失效问题

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

本地锁

本地锁在分布式场景下锁不住所有的服务

本地锁:synchronized、JUC(Lock),在分布式场景下,想要锁住所有的服务,必须使用分布式锁
这里this锁对象指向当前的serviceImpl对象,分布式场景下每个this都不一样
在这里插入图片描述

问题:本地锁压力测试,测试结果:依然多次查询了数据库(锁的时序问题)

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
解决:当前锁时序
在这里插入图片描述
当前是在释放锁后存进redis
在这里插入图片描述
真正完整合理的逻辑:查数据库和存入缓存应该是一个原子操作,也就是在同一把锁内完成,否则就会有释放锁的时序问题,导致多次查询数据库
在这里插入图片描述
在这里插入图片描述

分布式锁_redis

本地锁在分布式场景下的问题

1、在这里插入图片描述

2、启动多个服务,压力测试
gulimall.com->nginx->gateway负载均衡
在这里插入图片描述

在这里插入图片描述

3、现象:
在这里插入图片描述
在这里插入图片描述

redis分布式锁

setnx是原子性的

xshell发送命令到全部客户端,docker exec -it redis redis-cli,进入redis容器的客户端命令行,发送setnx命令
在这里插入图片描述
版本一:
在这里插入图片描述
在这里插入图片描述
版本二:

如果getDataFromDb出现异常,又或者服务掉线,导致锁未释放,发生死锁
在这里插入图片描述
设置过期时间
又为了防止在获取到锁和设置过期时间之间发生宕机,使用setnx ex原子性操作
在这里插入图片描述
在这里插入图片描述

版本三
在这里插入图片描述

在这里插入图片描述
版本四

如果由于业务时间很长,锁自己过期了,我们直接删除,有可能把别人正在持有的锁删除了
解决: 占锁的时候,值指定为uuid,每个人匹配是自己 的锁才删除。
在这里插入图片描述

在这里插入图片描述

版本五

1、如果正好判断是当前值,正要删除锁的时候,锁已经过期(网络延时), 别人已经设置到了新的值。那么我们删除的是别人的锁
解决: 删除锁必须保证原子性。使用redis+Lua脚本完成

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

redis分布式锁两个核心:
1、加锁保证原子性
2、解锁保证原子性
3、key过期时间放长一点

分布式锁_redisson

在这里插入图片描述

文档:https://github.com/redisson/redisson/wiki/%E7%9B%AE%E5%BD%95

可重入锁:a、b方法都加锁,加的是一样的锁,a获取到的锁b可以用,也就等于b方法直接执行
不可重入锁:a、b方法都加锁,加的一样的锁,b需要等a释放锁才可以执行,导致死锁

读写锁

读锁要等写锁释放才可以读取数据,写锁要排队,读锁不用排队

读 + 读 :相当于无锁,并发读,只会在Redis中记录好,所有当前的读锁。他们都会同时加锁成功
写 + 读 :必须等待写锁释放
写 + 写 :阻塞方式
读 + 写 :有读锁。写也需要等待

闭锁

相当于juc中的CountDownLatch

缓存一致性

缓存保证的是最终一致性,不是强一致,实时一致

双写模式
在这里插入图片描述
失效模式
在这里插入图片描述
解决方案
在这里插入图片描述

springCache

@EnableConfigurationProperties

@EnableConfigurationProperties的作用是把springboot配置文件中的值与我们的xxxProperties.java的属性进行绑定,需要配合@ConfigurationProperties使用 我们这里是获取properties的配置文件数据然后将数据和CacheProperties属性对应

检索服务

搭建页面环境

在这里插入图片描述

1、将html中搜索页index.html复制进gulimall-search的template目录,将./改成/static/search/
在这里插入图片描述
(1、导入thymeleaf依赖,否则路径访问请求不到index.html页面;2、打包指定yml文件,否则报错Param ‘serviceName’ is illegal)
2、将其他静态资源放进nginx的/mydata/nginx/html/static/search目录
3、hosts文件配置search.gulimall.com映射,nginx配置文件配置server_name
在这里插入图片描述
4、在gateway配置search.gulimall.com的host映射
在这里插入图片描述
5、访问http://search.gulimall.com/
在这里插入图片描述

检索服务

1、skuPrice的类型改成double,否则range区间过滤有问题

# 创建索引
PUT gulimall_product
{
  "mappings": {
	"properties": {
		"attrs": {
			"properties": {
				"attrId": {
					"type": "long"
				},
				"attrName": {
					"type": "keyword"
				},
				"attrValue": {
					"type": "keyword"
				}
			},
			"type": "nested"
		},
		"brandId": {
			"type": "long"
		},
		"brandImg": {
			"type": "keyword"
		},
		"brandName": {
			"type": "keyword"
		},
		"catalogId": {
			"type": "long"
		},
		"catalogName": {
			"type": "keyword"
		},
		"catelogId": {
			"type": "long"
		},
		"catelogName": {
			"type": "keyword"
		},
		"hasStock": {
			"type": "boolean"
		},
		"hosStock": {
			"type": "boolean"
		},
		"hotScore": {
			"type": "long"
		},
		"saleCount": {
			"type": "long"
		},
		"skuId": {
			"type": "long"
		},
		"skuImg": {
			"type": "keyword"
		},
		"skuPrice": {
			"type": "keyword"
		},
		"skuTitle": {
			"type": "text"
		},
		"spuId": {
			"type": "long"
		}
	}
  }
}
#检索服务
#模糊匹配、复合类型字段查询
#过滤(按照属性、分类、品牌、价格区间、库存)
#排序、分页、高亮
#聚合分析
GET /gulimall_product/_search
{
  "query": {
    "bool": {
      "must": [
        {
          "match": {
            "skuTitle": "华为"
          }
        }
      ],
      "filter": [
        {
          "term": {
            "catalogId": "225"
          }
        },
        {
          "terms": {
            "brandId": [
              "1",
              "2",
              "9"
            ]
          }
        },
        {
          "nested": {
          "path": "attrs",
          "query": {
            "bool": {
              "must": [
                {
                  "term": {
                    "attrs.attrId": {
                      "value": "15"
                    }
                  }
                },
                {
                  "terms": {
                    "attrs.attrValue": [
                      "海思(Hisilicon)",
                      "以官网信息为准"
                    ]
                  }
                }
              ]
            }
          }
        }
        },
        {
          "term":{
            "hasStock":{
              "value":"true"
            }
          }
        },
        {
          "range":{
            "skuPrice":{
              "gte":0,
              "lte":6000
            }
          }
        }
      ]
    }
  },
  "sort": [
    {
      "skuPrice": {
        "order": "desc"
      }
    }
  ],
  "from": 0,
  "size": 5,
  "highlight": {
    "fields": {
      "skuTitle":{}},
    "pre_tags":"<b style='color:red'>",
    "post_tags":"</b>"
  }
}

异步&线程池

面试: 一个线程池 core 7; max 20 ,queue:50,100 并发进来怎么分配的;
先有 7 个能直接得到执行,接下来 50 个进入队列排队,在多开 13 个继续执行。现在 70 个 被安排上了。剩下 30 个默认拒绝策略。

初始化线程的 4 种方式

1、初始化线程的 4 种方式
 1)、继承 Thread
 2)、实现 Runnable 接口
 3)、实现 Callable 接口 + FutureTask (可以拿到返回结果,可以处理异常)
 4)、线程池 给线程池直接提交任务
区别:
 1、2不能得到返回值,3可以获取返回值
 1、2、3都不可以控制资源
 4可以控制资源,系统稳定,无论面对多高的并发,不会耗尽资源,让系统崩溃

线程池七大参数

  1. corePoolSize:核心线程数【一直存在,除非设置了allowCoreThreadTimeOut】;线程池创建好以后就准备就绪的线程数量,就等待来接受异步任务去执行
  2. maximumPoolSize:最大线程数量,控制资源
  3. keepAliveTime:存活时间。如果当前正在运行的线程数量大于core数量,释放空闲的线程。只要线程空闲大于指定的keepAliveTime
  4. unit:时间单位
  5. BlockingQueue<Runnable> workQueue:阻塞队列。如果任务有很多,就会将目前多的任务放在队列里面。只要有线程空闲,就会去队列里面去除新的任务继续执行
  6. threadFactory:线程的创建工厂
  7. RejectedExecutionHandler handler:如果队列满了,按照我们指定的拒绝策略拒绝执行任务

工作顺序:

1、线程池创建,准备好 core 数量的核心线程,准备接受任务
2、新的任务进来,用 core 准备好的空闲线程执行。
  (1) 、core 满了,就将再进来的任务放入阻塞队列中。空闲的 core 就会自己去阻塞队 列获取任务执行
  (2) 、阻塞队列满了,就直接开新线程执行,最大只能开到 max 指定的数量
  (3) 、max 都执行好了。Max-core 数量空闲的线程会在 keepAliveTime 指定的时间后自 动销毁。最终保持到 core 大小
  (4) 、如果线程数开到了 max 的数量,还有新任务进来,就会使用 reject 指定的拒绝策 略进行处理
3、所有的线程创建都是由指定的 factory 创建的。

拒绝策略

在这里插入图片描述
1、丢弃队列中最老的任务
2、直接拒绝新来的任务,会抛异常
3、直接调用run方法,异步变同步
4、直接拒绝,不抛异常

常见的 4 种线程池

在这里插入图片描述
在这里插入图片描述
1、core是0,所有都可回收
2、固定大小,core=max,所有都不可回收
3、定时任务的线程池
4、单线程池,后台从队列里面获取任务,挨个执行

CompletableFuture 异步编排

1、2、3可以并发查,4、5、6可以并发查,4、5、6依赖1返回的spuId
在这里插入图片描述

商品详情

1、配置hosts的item.gulimall.com,nginx的路由,gateway网关(nginx之前配置过*.gulimall.com,所以不用再配置了)
2、将详情的静态资源文件传到nginx的html/item目录下
3、修改html访问路径,访问http://item.gulimall.com/

自定义配置线程池

异步查询商品详情

认证服务

配置auth.gulimall.com,nginx不用配置,将登陆、注册页面的静态资源文件复制进reg、login文件夹下

在这里插入图片描述
jsr303校验

1、实体类加限制
2、加@Valid注解
3、BindingResult result接收错误
在这里插入图片描述
在这里插入图片描述

OAuth2.0协议

OAuth2.0:对于用户相关的 OpenAPI(例如获取用户信息,动态同步,照片,日志,分 享等),为了保护用户数据的安全和隐私,第三方网站访问用户数据前都需要显式的向 用户征求授权。

OAuth2.0:对于用户相关的 OpenAPI(例如获取用户信息,动态同步,照片,日志,分 享等),为了保护用户数据的安全和隐私,第三方网站访问用户数据前都需要显式的向 用户征求授权。

微博登录

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

分布式session

1、导入依赖
2、yml文件配置
3、@EnableRedisHttpSession
在这里插入图片描述
在这里插入图片描述
问题:
1、不能跨不同域名共享
2、同一个服务,复制多份,session不同步问题
3、不同服务,session不能共享问题

1、session存到redis,
2、解决子域session共享问题
3、使用json序列化的方式来序列化对象到redis
在这里插入图片描述
需要在gulimall域名下也可以访问
在这里插入图片描述

jdk默认的序列化方式存储到redis
在这里插入图片描述
json序列化的方式
在这里插入图片描述

单点登录

不用使用springSession域名放大
在这里插入图片描述

购物车

在这里插入图片描述

ThreadLocal

每个线程互不干扰
在这里插入图片描述

消息队列

消息中间件应用场景:
1、异步处理
2、应用解耦(B服务修改接口,A调用B也就需要修改接口)
3、流量控制
在这里插入图片描述
在这里插入图片描述

RabbitMQ概念
在这里插入图片描述

Docker安装RabbitMQ

docker run -d --name rabbitmq -p 5671:5671 -p 5672:5672 -p 4369:4369 -p 25672:25672 -p 15671:15671 -p 15672:15672 rabbitmq:management
docker update rabbitmq --restart=always

在这里插入图片描述
Exchange 类型
在这里插入图片描述
在这里插入图片描述

修改rabbitmq消息序列化方式

发送消息,如果发送的消息是个对象,会使用序列化机制,将对象写出去,对象必须实现Serializable接口
如果要修改为json,分析RabbitAutoConfiguration的RabbitTemplate的bean对象
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

RabbitMQ测试

参数可以写以下类型:
1、Message message:原生消息详情信息。头+体
2、T<发送的消息的类型> OrderReturnReasonEntity content;
3、Channel chanel:当前传输数据的通道
Queue:可以很多人都来监听。只要收到消息,队列删除消息,而且只能有一个收到此消息
场景:
1):订单服务启动多个(集群场景);同一个消息,只能有一个客户端收到
2):只有一个消息完全处理完,方法运行结束,我们就可以接收到下一个消息

监听消息:使用@RabbitListener; 主启动类必须有@EnableRabbit
@RabbitListener: 类+方法上(监听哪些队列即可)
@RabbitHandler: 标在方法上(重载区分不同的消息)

RabbitMQ消息确认机制-可靠抵达

在这里插入图片描述
消息确认机制_发送端确认
在这里插入图片描述

两个回调分别表示:
confirm回调:消息抵达Broker
return回调:只要消息没有投递给指定的队列,就触发这个失败回调(比如把routingkey写错)
在这里插入图片描述

消费端确认(保证每个消息都被正确消费,此时才可以broker删除这个消息)

在这里插入图片描述

订单服务

整合环境:
1、复制订单html进order模块,拷贝静态资源进nginx
2、配置网关

feign远程调用丢失请求头问题

在这里插入图片描述

在这里插入图片描述

feign异步调用丢失请求头问题

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

提交订单的幂等性处理

在这里插入图片描述
幂等解决方案
1、token 机制
Token 获取、比较和删除必须是原子性
2、各种锁机制
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

本地事务&分布式事务

在这里插入图片描述

本地事务

>数据库事务的几个特性:原子性(Atomicity )、一致性( Consistency )、隔离性或独立性( Isolation) 和持久性(Durabilily),简称就是 ACID;
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

springboot中的事务的坑
a方法中直接调用b、c方法,使用的事务配置和a保持一致;
如果要使用b、c方法自己的事务配置,需要bService.b(),cService.c()
在这里插入图片描述
在这里插入图片描述
本地事务失效问题
同一个对象内事务方法互调默认失效,原因是绕过了代理对象,事务使用代理对象来控制的
解决:使用代理对象来调用事务方法
1)、引入aop-starter(因为此依赖引入了aspectj依赖)

<dependency>
     <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-aop</artifactId>
</dependency>

2)、@EnableAspectJAutoProxy(exposeProxy = true)
//开启aspectj动态代理功能。以后所有的动态代理都是aspectj创建的(即使没有接口也可以创建动态代理,对外暴露代理对象)。注:jdk动态代理必须有接口
3)、AopContext.currentProxy()
在这里插入图片描述

分布式事务

在这里插入图片描述

七、RabbitMQ延时队列(实现定时任务)

在这里插入图片描述

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

RabbitMQ使用的是惰性检查机制,所以一般是给队列设置过期时间,不是给消息设置过期时间

在这里插入图片描述
代码实现
在这里插入图片描述
升级后
在这里插入图片描述
消息队列流程
在这里插入图片描述
在这里插入图片描述

解决:在订单释放结束,再给库存服务发送消息,释放库存
最终一致性方案

在这里插入图片描述

保证消息可靠性

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

支付业务

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

内网穿透

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

秒杀

定时任务

解决定时任务阻塞问题
在这里插入图片描述
使用redisson解决分布式系统的幂等性问题
在这里插入图片描述

在这里插入图片描述
使用redis的信号量模拟库存,减少并发场景下与数据库的交互

在这里插入图片描述

高并发场景应对

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

我们使用了:
1、我们将秒杀单独抽取出来了,秒杀服务
2、我们使用随机码,只有秒杀时间到了,才能收到随机码
3、我们使用定时任务,将秒杀的商品存进了redis,库存信息使用redisson分布式信号量控制,只有抢到了信号量才会去数据库
4、nginx动静分离
后续再加后四个
5、登陆拦截器(没有在网关层去做恶意请求拦截器)
6、我们使用队列削峰机制

秒杀

在这里插入图片描述

使用rabbitmq实现流量的削峰,而不是每个请求都去调用订单服务

SpringCloud Alibaba-Sentinel

功能:熔断、降级、限流
在这里插入图片描述
熔断:feign调用的远程服务出现问题,直接返回fallback指定的方法
降级:指定时间内指定请求数超过指定时长,一段时间内再调用该接口,就会触发熔断

  • 0
    点赞
  • 41
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值