谷粒商城分布式高级(五)—— 性能压测(压力测试 & 性能监控 & nginx动静分离 )
一、压力测试
压力测试考察当前软硬件环境下系统所能承受的最大负荷并帮助找出系统瓶颈所在。压测都是为了系统在线上的处理能力和稳定性维持在一个标准范围内,做到心中有数。
使用压力测试,我们有希望找到很多种用其他测试方法更难发现的错误。有两种错误类型是:内存泄漏,并发与同步。
有效的压力测试系统将应用以下这些关键条件:重复,并发,量级,随机变化。
1、性能指标
2、JMeter
(1)JMeter 安装 https://jmeter.apache.org/download_jmeter.cgi
下载对应的压缩包,解压运行 jmeter.bat 即可
设置语言为中文
(2)JMeter 压测示例
(a)添加线程组
(b)添加HTTP请求
(c)添加监听器
(d)启动压测 & 查看分析结果
(3)JMeter Address Already in use 错误解决 windows 本身提供的端口访问机制的问题。
Windows 提供给 TCP/IP 链接的端口为 1024-5000,并且要四分钟来循环回收他们。就导致我们在短时间内跑大量的请求时将端口占满了。
解决办法:
(a)cmd 中,用 regedit 命令打开注册表
(b)在 HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\Tcpip\Parameters 下,
(1)右击 parameters,添加一个新的 DWORD,名字为 MaxUserPort
(2)然后双击 MaxUserPort,输入数值数据为 65534,基数选择十进制(如果是分布式运行的话,控制机器和负载机器都需要这样操作哦)
(c)修改配置完毕之后记得重启机器才会生效
https://support.microsoft.com/zh-cn/help/196271/when-you-try-to-connect-from-tcp-ports-greater-than-5000-you-receive-t
TCPTimedWaitDelay:30
二、性能监控
1、jvm内存模型
2、堆
3、jconsole 与 jvisualvm
Jdk 的两个小工具 jconsole、jvisualvm(升级版的 jconsole);通过命令行启动,可监控本地和 远程应用。远程应用需要配置
启动jconsole
(1)jvisualvm 能干什么
(2)安装插件方便查看gc
(a)cmd 启动 jvisualvm
(b)工具——>插件
如果 503 错误解决:
打开网址 https://visualvm.github.io/pluginscenters.html
cmd 查看自己的 jdk 版本,找到对应的
复制下面查询出来的链接。并重新设置上即可
设置完成后,重启 jvisualvm,就可以下载了
4、监控指标
(1)中间件指标
(2)数据库指标
5、JVM分析 & 调优
jvm 调优,调的是稳定,并不能带给你性能的大幅提升。服务稳定的重要性就不用多说了, 保证服务的稳定,gc 永远会是 Java 程序员需要考虑的不稳定因素之一。
复杂和高并发下的 服务,必须保证每次 gc 不会出现性能下降,各种性能指标不会出现波动,gc 回收规律而且干净,找到合适的 jvm 设置。
Full gc 最会影响性能,根据代码问题,避免 full gc 频率。可以 适当调大年轻代容量,让大对象可以在年轻代触发 yong gc,调整大对象在年轻代的回收频 次,
尽可能保证大对象在年轻代回收,减小老年代缩短回收时间;
(1)几个常用工具
(2)命令示例
三、优化
1、nginx 动静分离
查看 谷粒商城分布式高级(一)—— 环境搭建(高级篇补充)(ElasticSearch & nginx) 中的 “4、nginx 动静分离”
2、优化三级分类
优化之前
对二级菜单和三级菜单的每次遍历都需要查询数据库,浪费大量资源
优化后
仅查询一次数据库,剩下的数据通过遍历得到并封装
修改 com.atguigu.gulimall.product.service.impl.CategoryServiceImpl 的方法 getCatalogJson
@Override
public Map<String, List<Catelog2Vo>> getCatalogJson() {
/**
* 1、将数据库的多次查询变为一次
*/
List<CategoryEntity> selectList = baseMapper.selectList(null);
//1、查询所有一级分类
List<CategoryEntity> level1Catagorys = getParent_cid(selectList, 0L);
//2、封装数据
Map<String, List<Catelog2Vo>> parent_cid = level1Catagorys.stream().collect(Collectors.toMap(k -> k.getCatId().toString(), v -> {
// 1、每一个的一级分类,查到这个以及分类的二级分类
List<CategoryEntity> categoryEntities = getParent_cid(selectList, v.getCatId());
//2、封装上面的结果
List<Catelog2Vo> catelog2Vos = null;
if (categoryEntities != null) {
catelog2Vos = categoryEntities.stream().map(l2 -> {
Catelog2Vo catelog2Vo = new Catelog2Vo(v.getCatId().toString(), null, l2.getCatId().toString(), l2.getName());
//1、找当前二级分类的三级分类封装成vo
List<CategoryEntity> level3Catalog = getParent_cid(selectList, l2.getCatId());
if(level3Catalog!=null){
List<Catelog2Vo.Category3Vo> collect = level3Catalog.stream().map(l3 -> {
//2、封装成指定格式
Catelog2Vo.Category3Vo category3Vo = new Catelog2Vo.Category3Vo(l2.getCatId().toString(), l3.getCatId().toString(), l3.getName());
return category3Vo;
}).collect(Collectors.toList());
catelog2Vo.setCatalog3List(collect);
}
return catelog2Vo;
}).collect(Collectors.toList());
}
return catelog2Vos;
}));
return parent_cid;
}
private List<CategoryEntity> getParent_cid( List<CategoryEntity> selectList, Long parent_cid) {
List<CategoryEntity> collect = selectList.stream().filter(item -> item.getParentCid() == parent_cid).collect(Collectors.toList());
return collect;
}