谷粒商城笔记合集
分布式基础篇 | 分布式高级篇 | 高可用集群篇 |
---|---|---|
===简介&环境搭建=== | ===Elasticsearch=== | |
项目简介与分布式概念(第一、二章) | Elasticsearch:全文检索(第一章) | |
基础环境搭建(第三章) | ===商品服务开发=== | |
===整合SpringCloud=== | 商品服务 & 商品上架(第二章) | |
整合SpringCloud、SpringCloud alibaba(第四、五章) | ===商城首页开发=== | |
===前端知识=== | 商城业务:首页整合、Nginx 域名访问、性能优化与压力测试 (第三、四、五章) | |
前端开发基础知识(第六章) | 缓存与分布式锁(第六章) | |
===商品服务开发=== | ===商城检索开发=== | |
商品服务开发:基础概念、三级分类(第七、八章) | 商城业务:商品检索(第七章) | |
商品服务开发:品牌管理(第九章) | ||
商品服务开发:属性分组、平台属性(第十、十一章) | ||
商品服务:商品维护(第十二、十三章) | ||
===仓储服务开发=== | ||
仓储服务:仓库维护(第十四章) | ||
基础篇总结(第十五章) |
三、商城业务 & 首页整合💡💡💡
3.1 前端渲染说明💡💡💡
org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration
org.springframework.boot.autoconfigure.web.servlet.WebMvcProperties
-
templates 下的视图页面只能通过controller返回。访问项目根地址的欢迎页默认返回 classpath:/templates/index.html。
-
如果controller返回的不是json数据,而是一个字符串。那么 springmvc 的视图解析器就会根据前后缀进行拼串找到并返回视图页面
-
静态资源存放路径:classpath:/META-INF/resources/、classpath:/resources/、classpath:/static/、classpath:/public/。可直接访问
-
项目架构
3.2 thymeleaf 语法
3.2.1 简单表达式
- 变量表达式:
${...}
- 选择变量表达式:
*{...}
- 消息表达式:
#{...}
- 链接网址表达式:
@{...}
- 片段表达式:
~{...}
3.2.2 文字
- 文本文字:
'one text'
,'Another one!'
,… - 数字文字:
0
,34
,3.0
,12.3
,… - 布尔文字:
true
,false
- 空文本:
null
- 文字标记:
one
,sometext
,main
,…
3.2.3 文本操作
- 字符串连接:
+
- 文字替换:
|The name is ${name}|
3.2.4 算术运算
- 二元运算符:
+
,-
,*
,/
,%
- 减号(一元运算符):
-
3.2.5 布尔运算
- 二元运算符:
and
,or
- 布尔取反(一元运算符):
!
,not
3.2.6 比较和平等
- 比较器:
>
,<
,>=
,<=
(gt
,lt
,ge
,le
) - 相等运算符:
==
,!=
(eq
,ne
)
3.2.7 条件运算符
- 如果-那么:
(if) ? (then)
- 如果-那么-否则:
(if) ? (then) : (else)
- 默认:
(value) ?: (defaultvalue)
3.2.8 特殊令牌
- 无操作:
_
3.3 API:整合 thymeleaf 渲染首页⚠️
3.3.1 需求分析
开发传统Java WEB工程时,我们可以使用JSP页面模板语言,但是在SpringBoot中已经不推荐使用了。SpringBoot支持如下页面模板语言
- Thymeleaf
- FreeMarker
- Velocity
- Groovy
- JSP
thymeleaf 官网:https://www.thymeleaf.org/
官网文档给出了 语法、相关标签 如何使用的步骤,由于官网文档都是英文,英文文档阅读能力好的同学可以选择阅读,英文不好的同学可以选择中文文档进行学习,为此我在网上找到了相关的中文文档:http://note.youdao.com/noteshare?id=7771a96e9031b30b91ed55c50528e918
3.3.2 整合⚠️
-
在商品微服务的 pom.xml 中引入thymeleaf依赖
<!-- thymeleaf模板引擎 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-thymeleaf</artifactId> </dependency>
-
修改配置文件,关闭 thymeleaf缓存:application.yml
spring: thymeleaf: cache: false
-
拷贝前端资源到指定目录
-
修改项目结构
3.4 整合 dev-tools⚠️
-
在商品微服务的 pom.xml 中引入dev-tools依赖
<!-- devtools --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-devtools</artifactId> <optional>true</optional> </dependency>
-
让页面修改实时生效:CTRL+F9、CTRL+SHIFT+F9
3.5 API:渲染首页分类数据💡
3.5.1 一级分类数据
-
创建首页跳转controller,首页渲染一级分类数据:cn/lzwei/bilimall/product/web/IndexController.java
@Controller public class IndexController { @Resource CategoryService categoryService; @GetMapping(value = {"/","/index.html"}) public String index(Model model){ //首页渲染:获取一级分类数据 List<CategoryEntity> categoryEntities=categoryService.getCategoryLevel1(); model.addAttribute("category",categoryEntities); return "index"; } }
-
CategoryService:首页渲染,获取一级分类数据
public interface CategoryService extends IService<CategoryEntity> { /** * 首页渲染:获取一级分类数据 */ List<CategoryEntity> getCategoryLevel1(); }
-
CategoryServiceImpl:首页渲染,获取一级分类数据
@Service("categoryService") public class CategoryServiceImpl extends ServiceImpl<CategoryDao, CategoryEntity> implements CategoryService { /** * 首页渲染:获取一级分类数据 */ @Override public List<CategoryEntity> getCategoryLevel1() { List<CategoryEntity> categoryEntities = baseMapper.selectList(new QueryWrapper<CategoryEntity>().eq("parent_cid", 0)); return categoryEntities; } }
-
在 /template/index.htnl 中添加 thymeleaf 属性命名空间
<html lang="en" xmlns:th="http://www.thymeleaf.org">
-
修改 /template/index.html,渲染页面一级分类数据
<!DOCTYPE html> <html lang="en" xmlns:th="http://www.thymeleaf.org"> <body> <header> <!--轮播主体内容--> <div class="header_main"> <div class="header_banner"> <div class="header_main_left"> <ul> <li> <a href="#" class="header_main_left_a" th:attr="ctg-data=${category.catId}" th:each="category : ${categorys}" th:text="${category.name}"><b>家用电器</b></a> </li> </ul> </div> ... </div> </div> </header> </body> </html>
3.4.3 二、三级 分类
-
首页渲染时发送请求到当前服务获取分类数据:src/main/resources/static/index/js/catalogLoader.js
$(function(){ /* 修改分类数据请求路径 */ $.getJSON("index/catalog.json",function (data) { ... }); });
-
分析数据结构
{ "1": [ { "catalog1Id": "1", "catalog3List": [ { "catalog2Id": "1", "id": "1", "name": "电子书" }, ... ], "id": "1", "name": "电子书刊" }, ... ], ... }
-
web/IndexController.java:获取分类数据:用于渲染二级、三级分类
@Controller public class IndexController { @Resource CategoryService categoryService; /** * 获取分类数据:用于渲染二级、三级分类 */ @ResponseBody @GetMapping(value = "/index/catalog.json") public Map<String, List<CategoryLevel2Vo>> getCategoryLevel2(){ Map<String, List<CategoryLevel2Vo>> categorys=categoryService.getCategoryLevel2(); return categorys; } }
-
CategoryService:获取分类数据:用于渲染二级、三级分类
public interface CategoryService extends IService<CategoryEntity> { /** * 获取分类数据:用于渲染二级、三级分类 */ Map<String, List<CategoryLevel2Vo>> getCategoryLevel2(); }
-
CategoryServiceImpl:获取分类数据:用于渲染二级、三级分类
@Service("categoryService") public class CategoryServiceImpl extends ServiceImpl<CategoryDao, CategoryEntity> implements CategoryService { /** * 获取分类数据:用于渲染二级、三级分类 */ @Override public Map<String, List<CategoryLevel2Vo>> getCategoryLevel2() { //1.获取一级分类:将一级分类转换为map进行遍历,自定义key、value List<CategoryEntity> categoryLevel1s = this.getCategoryLevel1(); Map<String, List<CategoryLevel2Vo>> collect=null; if(categoryLevel1s!=null){ collect = categoryLevel1s.stream().collect(Collectors.toMap(level1 -> level1.getCatId().toString(), level1 -> { //2.通过一级分类id获取二级分类列表进行遍历:封装成CategoryLevel2Vo集合 List<CategoryEntity> level2s = this.list(new QueryWrapper<CategoryEntity>().eq("parent_cid", level1.getCatId())); List<CategoryLevel2Vo> Level2Vos = null; if (level2s != null) { //遍历二级分类:封装成CategoryLevel2Vo Level2Vos = level2s.stream().map(level2 -> { CategoryLevel2Vo categoryLevel2Vo = new CategoryLevel2Vo(level2.getCatId().toString(), level2.getName(), level1.getCatId().toString(), null); //3.通过二级分类id获取三级分类列表:封装成CategoryLevel3Vo集合 List<CategoryEntity> level3s = this.list(new QueryWrapper<CategoryEntity>().eq("parent_cid", level2.getCatId())); if (level3s != null) { //遍历三级分类:封装成CategoryLevel3Vo List<CategoryLevel2Vo.CategoryLevel3Vo> level3Vos = level3s.stream().map(level3 -> { CategoryLevel2Vo.CategoryLevel3Vo categoryLevel3Vo = new CategoryLevel2Vo.CategoryLevel3Vo(level2.getCatId().toString(), level3.getCatId().toString(), level3.getName()); return categoryLevel3Vo; }).collect(Collectors.toList()); categoryLevel2Vo.setCatalog3List(level3Vos); } return categoryLevel2Vo; }).collect(Collectors.toList()); } return Level2Vos; })); } return collect; } }
四、商城业务 & Nginx 域名访问⚠️
4.1 前言
4.1.1 正向代理 与 反向代理
4.1.2 需求分析
4.1.3 Nginx 配置概述
http://nginx.org/en/docs/
4.2 内网穿透服务器配置(行不通)
由于Nginx跑在云服务器上,所以打算实现内网穿透:⚠️⚠️⚠️行不通,域名访问 公网:80 时要求域名需要备案⚠️⚠️⚠️
浏览器bilimall.com—>云服务器:80(Nginx)—>云服务器:88(http内网穿透端口)—>本地:88(网关服务)
-
修改云服务器安全组开放对应端口、关闭云服务器内部防火墙(systemctl stop firewalld)
-
在云服务下载 frp 服务端,或者通过科学的方法在github上拉比较快:https://github.com/fatedier/frp
[root@tencent opt]# wget https://github.com/fatedier/frp/releases/download/v0.37.0/frp_0.37.0_linux_amd64.tar.gz
-
将压缩包解压到当前目录,进入文件夹查看文件列表
- “frps”、“frps_full.ini”、"frps.ini"是放在服务器端运行的可执行文件和配置文件
- “frpc”、“frpc_full.ini”、"frpc.ini"是放在客户端端运行的可执行文件和配置文件
[root@tencent opt]# tar -zxvf frp_0.37.0_linux_amd64.tar.gz [root@tencent opt]# cd frp_0.37.0_linux_amd64/ [root@tencent frp_0.37.0_linux_amd64]# ll
-
修改服务端配置。注意:配置项中用到的端口,需要在服务器上开启
- “bind_addr”:服务器本地IP,不改。
- “bind_port”:frp监听端口。
- “token”:验证码
- “dashboard_port”:frp 图形化控制面板服务端口
- “dashboard_user”、“dashboard_pwd”:登录控制面板的账户密码。
[common] bind_addr=0.0.0.0 bind_port = 7777 #作为http映射的端口:即访问 云服务器:88 ,实现访问内网服务。内网服务 vhost_http_port = 88 token=****** dashboard_port=8888 dashboard_user=admin dashboard_pwd=******
-
给frp所在目录下的frp文件开通可执行权限
[root@tencent frp_0.37.0_linux_amd64]# chmod 755 ./frps
-
配置frp后台服务
[root@tencent frp_0.37.0_linux_amd64]# sudo vim /lib/systemd/system/frps.service [Unit] Description=fraps service After=network.target syslog.target Wants=network.target [Service] Type=simple #启动服务的命令(此处写你的frps的实际安装目录) ExecStart=/opt/frp_0.37.0_linux_amd64/frps -c /opt/frp_0.37.0_linux_amd64/frps.ini [Install] WantedBy=multi-user.target
-
启动 frps,打开开机启动
[root@tencent frp_0.37.0_linux_amd64]# systemctl start frps [root@tencent frp_0.37.0_linux_amd64]# systemctl enable frps Created symlink from /etc/systemd/system/multi-user.target.wants/frps.service to /usr/lib/systemd/system/frps.service.
-
访问控制面板:http://云服务器ip:控制面板服务端口
-
下载frp客户端:https://github.com/fatedier/frp/releases?page=2
-
解压下载后的压缩包,并修改配置文件:frpc.ini
#注意:相关参数需要与服务端相互对应 [common] server_addr = 云服务器IP server_port = 7777 token=****** #这里表示将云服务器的88端口的所有请求映射到本地88端口 [web] type=http local_ip=127.0.0.1 local_port=88 custom_domains=114.132.162.129 #localtions=/
-
客户端运行
-
命令行方式
nohup .\frpc.exe -c .\frpc.ini >/dev/null 2>&1 &
-
脚本方式,在安装目录下创建文件:xxx.bat
@echo off if "%1" == "h" goto begin mshta vbscript:createobject("wscript.shell").run("""%~nx0"" h",0)(window.close)&&exit :begin REM .\frpc.exe -c .\frpc.ini
-
-
配置成功
4.3 Nginx反向代理:云服务器(行不通)
配置HTTPS服务器文档:http://nginx.org/en/docs/http/load_balancing.html
HTTP负载均衡文档:http://nginx.org/en/docs/http/configuring_https_servers.html
负载均衡其他细节文档:http://www.nginx.com/blog/load-balancing-with-nginx-plus-part-2/
这里使用域名 bilimall.com 访问到云服务器
-
修改本地域名映射文件:C:\Windows\System32\drivers\etc\hosts
注意:可以将该文件拖到桌面修改,然后再拖回原文件夹。可能需要转成txt文件才可被修改,之后再重命名回来
#注意这里找到云服务器80端口,即nginx服务 114.132.162.129 bilimall.com
-
为 云服务器的nginx 配置上游服务器,上游服务为云服务器的88端口,88端口已内网穿透到本地网关服务
[root@tencent ~]# vim /mydata/nginx/conf/nginx.conf ... http { ... upstream bilimall{ server 114.132.162.129:88; } }
-
为 云服务器的nginx 配置反向代理,将访问nginx的所有服务转发到上游服务器
server { listen 80; server_name bilimall.com; location / { proxy_set_header Host $host; proxy_pass http://bilimall; } ... }
-
重启 nginx容器实例
-
浏览器访问:bilimall.com/api/product/attrattrgrouprelation/list
4.4 Nginx反向代理:本地(最终方案⚠️)
-
修改本地域名映射文件:C:\Windows\System32\drivers\etc\hosts
注意:可以将该文件拖到桌面修改,然后再拖回原文件夹。可能需要转成txt文件才可被修改,之后再重命名回来
#注意这里找到本地80端口,即nginx服务 192.168.100.1 bilimall.com
-
下载Windows版本的nginx安装包:http://nginx.org/en/download.html
-
将安装包解压到本地:F:\software\Nginx\
-
为 本地nginx 配置上游服务器和反向代理,上游服务为本地的网关服务,将访问nginx的所有请求转发到上游服务器:F:\software\Nginx\conf\nginx.conf
达到访问本机80端口(nginx服务),转发到本地网关服务根据规则进行路由的功能
... http { #添加下列配置:注意此配置为windows本地的nginx配置 upstream bilimall{ server 192.168.100.1:88; } #建议拷贝一份原来的进行修改:注意此配置为windows本地的nginx配置 server { listen 80; server_name bilimall.com; location / { proxy_set_header Host $host; proxy_pass http://bilimall; } ... } ... }
-
启动命令行窗口,进入nginx安装目录启动nginx
start nginx #开启nginx服务 nginx.exe -s stop #关闭nginx服务,快速停止nginx,可能并不保存相关信息 nginx.exe -s quit #关闭nginx服务,完整有序的停止nginx,并保存相关信息 nginx.exe -s reload #重载nginx服务,当你改变了nginx配置信息并需要重新载入这些配置时可以使用此命令重载nginx taskkill /F /IM nginx.exe > nul #强关nginx服务器
-
浏览器访问:bilimall.com/api/product/attrattrgrouprelation/list
4.5 网关服务:配置域名访问商城系统⚠️
-
在 网关服务 的配置文件中添加域名路由规则,实现域名访问到商城系统:application.yaml
spring: cloud: gateway: routes: - id: bilimall_route uri: lb://bilimall-product predicates: - Host=**.bilimall.com,bilimall.com
五、性能优化与压力测试⚠️
5.1 概述
压力测试考察当前软硬件环境下系统所能承受住的最大负荷并帮助找出系统的瓶颈所在,压测都是为了系统在线上的处理能力和稳定性维持在一个标准范围内,做到心中有数。
使用压力测试,我们有希望找到很多种用其他测试方法更难发现的错误,有两种错误类型是:内存泄漏、并发与同步
有效的压力测试系统将应用以下这些关键条件:重复、并发、量级、随机变化
5.2 性能指标
吞吐量大:系统支持高并发
响应时间:越短说明接口性能越好
SQL 耗时:越小越好、一般情况下微妙级别
命中率:越高越好、一般情况下不能低于95%
锁等待次数:越低越好、等待时间越短越好
-
响应时间(Response Time:RT)
响应时间指用户从客户端发起一个请求开始,到客户端接收到服务器端返回的响应结束,整个过程所耗费的时间
-
HPS(Hits Per Second) :每秒点击次数,单位是次/秒
-
TPS(Transaction per Second):系统每秒处理交易数,单位是笔/秒
-
QPS (Query perSecond) :系统每秒处理查询次数,单位是次/秒。对于互联网业务中,如果某些业务有且仅有一个请求连接,那么TPS=QPS=HPS,一般情况下用TPS来衡量整个业务流程,用QPS来衡量接口查询次数,用HPS来表示对服务器单击请求。
-
无论TPS、QPS、HPS,此指标是衡量系统处理能力非常重要的指标,越大越好,根据经验,一般情况下:
- 金融行业: 1000TPS~50000TPS, 不包括互联网化的活动
- 保险行业: 100TPS-100000TPS, 不包括互联网化的活动
- 制造行业: 10TPS~5000TPS
- 互联网电子商务: 10000TPS~1000000TPS
- 互联网中型网站: 1000TPS~50000TPS
- 互联网小型网站: 500TPS~10000TPS
-
最大响应时间(Max Response Time) :指用户发出请求或者指令到系统做出反应(响应)的最大时间。
-
最少响应时间 (Mininum ResponseTime):指用户发出请求或者指令到系统做出反应(响应)的最少时间
-
90%响应时间(90% Response Time): 是指所有用户的响应时间进行排序、第90%的响应时间
-
从外部看、性能测试主要关注如下三个指标:
- 吞吐量:每秒钟系统能够处理的请求数、任务数
- 响应时间:服务处理一个请求或一个任务的耗时
- 错误率:一批请求中结果出错的请求所占比例
5.3 JMeter
5.3.1 安装
jmeter官网:https://jmeter.apache.org/
-
下载安装包
-
解压后进入bin目录点击启动脚本,并更换显示语言:F:\software\Jmeter\bin\jemter.bat
5.3.2 压测示例
-
在示例测试计划中添加线程组
-
添加 HTTP 请求
-
添加 监听器
-
启动压测,查看测试结果
-
结果树
-
汇总报告
-
聚合报告
-
汇总图
-
5.3.3 问题解决
影响性能考虑点
- 数据库,应用程序,中间件(tomcat、Nginx),网络和操作系统等方面
- 首先考虑自己的应用属于 CPU密集型 还是 IO密集型
可能的异常:JMeter Address Already in use
到 90% 以上,则可以说明服务器有问题,压力机没有问题。
windows本身提供的端口访问机制的问题
Windows提供给TCP/IP 链接的端口为1024-5000,并且要四分钟来循环回收他们。就导致我们在短时间内跑大量的请求时将端口占满了。
- cmd中,用regedit命令打开注册表
- 在HKEY_ LOCAL MACHINE\SYSTEMCurrentControlSet\Services Tcpip\Parameters下
- 如果是分布式运行的话,控制机器和负载机器都需要这样操作哦
- 右击parameters,新建一个新的DWORD(32位)值(D),名字为 MaxUserPort。然后双击 MaxUserPort,基数选择十进制,输入数值数据为65534
- 右击parameters,新建一个新的DWORD(32位)值(D),名字为 TCPTimedWaitDelay。然后双击 TCPTimedWaitDelay,基数选择十进制,输入数值数据为30
- 修改配置完毕之后记得重启机器才会生效
5.3 JVM 性能监控
5.3.1 Jvm 内存模型
5.3.2 堆
5.3.3 jvisualvm
监控内存泄漏,跟踪垃圾回收,执行时内存、cpu分析,线程分析…
- 运行:正在运行的
- 休眠:sleep
- 等待:wait
- 驻留:线程池里面的空闲线程
- 监视:阻塞的线程,正在等待锁
安装jvisualvm
https://visualvm.github.io/download.html
-
进入官网下载地址,下载相应版本的安装包
-
解压安装包,修改配置文件:F:\software\Java\visualvm_215\etc\visualvm.conf
#添加下列配置:指定自己jdk的安装目录 visualvm_jdkhome="F:\software\Java\jdk-17.0.5"
-
双击启动脚本启动 jvisualvm:F:\software\Java\visualvm_215\bin\visualvm.exe
-
如果没有反应可能是因为jdk安装目录下存在jre文件夹,可以选择删除获取移动到别的地方
安装插件
插件更新地址,根据版本选择地址:https://visualvm.github.io/pluginscenters.html
-
打开插件窗口
-
安装 Visual GC
-
重新启动 jvisualvm
5.4 压力测试
测试nginx,gateway,简单服务等中单个服务以及服务结合的性能:吞吐量/ms、90%响应时间/ms、99%响应时间/ms
压测内容 | 压测线程数 | 吞吐量/ms | 90%响应时间/ms | 99%响应时间/ms |
---|---|---|---|---|
Nginx | 50 | 2335 | 11 | 944 |
GateWay | 50 | 10367 | 8 | 31 |
简单服务 | 50 | 11341 | 8 | 17 |
Gateway+简单服务 | 50 | 3126 | 30 | 125 |
全链路 | 50 | 800 | 88 | 310 |
首页:一级菜单渲染 | 50 | 270(db,themleaf渲染,日志) | 267 | 365 |
三级数据获取 | 50 | 2(db,业务代码) | …(24000) | …(25000) |
首页:全量数据 | 50 | 7(静态资源) |
5.5 性能优化:索引优化⚠️
中间件越多,性能损失越大,大多都损失在了中间之间的网络交互:
- 优化中间件吞吐量
- 提供之间的传输效率:更好的网线,更好的网卡,更好的传输协议…
业务:
- db:添加索引
- 模板的渲染速度:开启缓存
- cpu:降低日志等级
压测内容 | 压测线程数 | 吞吐量/ms | 90%响应时间/ms | 99%响应时间/ms |
---|---|---|---|---|
首页:一级菜单渲染(开themleaf缓存) | 50 | 270(290) | 267(251) | 365(365) |
首页:一级菜单渲染(开themleaf缓存、优化数据库,降低日志级别) | 50 | 270(700) | 267(105) | 365(183) |
三级数据获取(优化数据库,降低日志级别) | 50 | 2(8) |
5.6 性能优化:nginx 动静分离⚠️
业务:
- 静态资源:动静分离
压测内容 | 压测线程数 | 吞吐量/ms | 90%响应时间/ms | 99%响应时间/ms |
---|---|---|---|---|
首页:全量数据(开themleaf缓存、优化数据库,关日志,动静分离) | 50 | 7(11) | ||
首页:全量数据(开themleaf缓存、优化数据库,关日志,动静分离,jvm内存) | 200 | 7(14) |
-
将 商品服务 resources/static/ 下的静态资源拷贝到 nginx中,并删除商品服务下的静态资源:F:\software\Nginx\html\static\
-
首页返回后下载商品服务中静态资源 resources/static/index/css/swiper-3.4.2.min.css 的路径为:http://bilimall.com/index/css/swiper-3.4.2.min.css
<link rel="stylesheet" href="index/css/swiper-3.4.2.min.css">
-
修改首页中 所有 静态资源请求的路径,请求 nginx中的静态资源。重启商品服务
<link rel="stylesheet" href="index/css/swiper-3.4.2.min.css"> <!-- 例如将上面修改为: --> <link rel="stylesheet" href="/static/index/css/swiper-3.4.2.min.css">
-
修改nginx配置文件:F:\software\Nginx\conf\nginx.conf
... http { upstream bilimall{ server 192.168.100.1:88; } server { listen 80; server_name bilimall.com; #增加下列配置:注意此配置为windows本地的nginx配置 location /static/ { root html; location / { proxy_set_header Host $host; proxy_pass http://bilimall; } ... } ... }
-
重启nginx,分离成功
5.7 性能优化:业务代码💡
业务:
- IO:减少数据库IO
压测内容 | 压测线程数 | 吞吐量/ms | 90%响应时间/ms | 99%响应时间/ms |
---|---|---|---|---|
三级数据获取(业务代码减少数据库IO) | 50 | 2(111) | 24000(571) | 25000(896) |
-
修改 商品服务 中首页获取三级分类数据的业务代码,减少数据库IO:cn.lzwei.bilimall.product.service.impl.CategoryServiceImpl
@Service("categoryService") public class CategoryServiceImpl extends ServiceImpl<CategoryDao, CategoryEntity> implements CategoryService { /** * 获取分类数据:用于渲染二级、三级分类 */ @Override public Map<String, List<CategoryLevel2Vo>> getCategoryLevel2() { //**************************缓存所有三级分类数据************************************** List<CategoryEntity> categoryEntities = baseMapper.selectList(null); //1.获取一级分类:将一级分类转换为map进行遍历,自定义key、value List<CategoryEntity> categoryLevel1s = getParent_cid(categoryEntities,0l); Map<String, List<CategoryLevel2Vo>> collect=null; if(categoryLevel1s!=null){ collect = categoryLevel1s.stream().collect(Collectors.toMap(level1 -> level1.getCatId().toString(), level1 -> { //2.通过一级分类id获取二级分类列表进行遍历:封装成CategoryLevel2Vo集合 List<CategoryEntity> level2s = getParent_cid(categoryEntities,level1.getCatId()); List<CategoryLevel2Vo> Level2Vos = null; if (level2s != null) { //遍历二级分类:封装成CategoryLevel2Vo Level2Vos = level2s.stream().map(level2 -> { CategoryLevel2Vo categoryLevel2Vo = new CategoryLevel2Vo(level2.getCatId().toString(), level2.getName(), level1.getCatId().toString(), null); //3.通过二级分类id获取三级分类列表:封装成CategoryLevel3Vo集合 List<CategoryEntity> level3s = getParent_cid(categoryEntities,level2.getCatId()); if (level3s != null) { //遍历三级分类:封装成CategoryLevel3Vo List<CategoryLevel2Vo.CategoryLevel3Vo> level3Vos = level3s.stream().map(level3 -> { CategoryLevel2Vo.CategoryLevel3Vo categoryLevel3Vo = new CategoryLevel2Vo.CategoryLevel3Vo(level2.getCatId().toString(), level3.getCatId().toString(), level3.getName()); return categoryLevel3Vo; }).collect(Collectors.toList()); categoryLevel2Vo.setCatalog3List(level3Vos); } return categoryLevel2Vo; }).collect(Collectors.toList()); } return Level2Vos; })); } return collect; } //通过 parent_id 获取分类数据 private List<CategoryEntity> getParent_cid(List<CategoryEntity> categoryEntities,Long parentId) { List<CategoryEntity> collect = categoryEntities.stream().filter(item -> item.getParentCid().equals(parentId) ).collect(Collectors.toList()); return collect; } }
-
重启服务