1.压力测试
在项目上线前对其进行压力测试(以每个微服务为单元)
目的:找到系统能承载的最大负荷,找到其他测试方法更难发现的错误(两种类型:内存泄漏,并发与同步).
1.性能指标
响应时间(Response Time (RT)): 响应时间 指用户从客户端发起一个请求开始,到客户端接收到从服务器端返回的响应结束,整个过程所耗费的时间。
HPS(Hits Per Second):每秒点击次数,单位是 次/秒.(意义不大)
TPS(Transaction Per Second):每秒处理的交易数,单位是 笔/秒.
QPS(Query Per Second):每秒处理的查询次数,单位是 次/秒
对于互联网业务中,如果某些业务有且仅有一个请求连接,那么TPS=QPS=HPS,般情况下用 TPS 来衡量整个业务流程,用 QPS 来衡量接口查询次数,用 HPS 来表示对服务器单击请求。
最大响应时间(Max Response Time):指用户发出请求或者指令到系统做出反应(响应)的最大时间.
最少响应时间(Mininum ResponseTime):指用户发出请求或者指令到系统做出反应(响应)的最小时间.
90%响应时间(90% ResponseTime):是指所有用户的响应时间进行排序,第 90%的响应时间.
从外部看,性能测试主要关注如下指标:
吞吐量:每秒钟系统能够处理的请求数、任务数。
响应时间:服务处理一个请求或一个任务的耗时。
错误率:一批请求中结果出错的请求所占比例。
2.JMeter
1.安装JMeter
Apache JMeter - Download Apache JMeter下载,解压,来到bin目录找到jmeter.bat文件
2.JMeter压测示例
1.添加线程组
HTTP请求里写的是要压力测试的目标,查看结果树,汇总报告,聚合报告可以看到我们上述说的很多性能指标
3.JMeter Address Already in use错误解决
windows 本身提供的端口访问机制的问题。Windows 提供给 TCP/IP 链接的端口为 1024-5000,并且要四分钟来循环回收他们。就导致我们在短时间内跑大量的请求时将端口占满了。
1.cmd中,用regedit命令打开注册表
2.性能监控
影响性能考虑点包括:
数据库,应用程序,中间件(tomcat,Nginx),网络和操作系统等方面
首先考虑自己的应用属于 CPU密集型 还是 IO密集型
1.JVM内存模型
优化更多的是在 堆
2.堆
运行期间所有的对象实例创建和内存分配都放在了 堆 里
新对象创建过程和垃圾回收机制:
先看Eden(伊甸园区)放不放的下,放不下,就进行一次MinorGC(小GC清理Eden区的没用的对象,把有用的对象放进Survival(幸存者区)区),然后继续看Eden放不放的下,放不下,就转进 老年代 ,看 老年代 是否放的下,放不下,进行一次FULL GC(大GC,会清理掉堆中所有的没用的对象,但是性能比小GC慢10倍),如果 老年代 也放不下,就会报内存溢出错误 OOM.
其中在进行 MinorGC的有用的旧对象会被放入 Survival(幸存者区),会根据MinorGC不断在S1和S0交换位置,方便腾出更大的空间,且每次MinorGC都会使 Survival 里的对象增加一岁,当增加至 15 岁时会被转入 老年代 中(15岁是因为计算年龄用4bit 1111是15).
了解垃圾回收机制后,我们在进行压力测试时就需要监控 堆 内的这些内存情况,可以使用 jconsole 和 jvisualvm 来监控
3.Jvisualvm
jdk8以上没有了,可以去官网下载VisualVM
下载后可以在Idea中找VisualVM插件,下载完配置VisualVM插件,以下可参考:
JVisualVM 性能分析与 Mybatis ResultHandler 实战_idea jvisualvm-CSDN博客
添加GC插件:
1.Jvisualvm可以干什么
监控内存泄露,跟踪垃圾回收,执行时内存、cpu分析,线程分析..
我们来测试中间件对响应的影响:
打开JMeter:
然后看
得出结论:中间件越多,性能损失越大,大多都损失在网络交互
业务:
Db(数据库)(MySQL优化)
模板的渲染速度(CPU ,缓存)
静态资源
3.优化
1.nginx动静分离
先把index挪进nginx:
删掉本机里的index:
然后在nginx的conf的nginx.conf文件里更改:
更改HTML页面的引用路径
2.增加内存
3.优化业务(三级分类数据获取)
之前是循环查询数据库,导致性能降低,我们只查询一次数据库,就会提升性能:
抽取方法:
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; //return baseMapper.selectList(new QueryWrapper<CategoryEntity>().eq("parent_cid", v.getCatId())); }
@Override public Map<String, List<Catelog2Vo>> getCatalogJson() { /** * 1.将数据库的多次查询变为一次 */ List<CategoryEntity> selectList = baseMapper.selectList(null); //1.查出所有1级分类 List<CategoryEntity> level1Categorys = getParent_cid(selectList,0L); //2.封装数据 Map<String, List<Catelog2Vo>> collect = level1Categorys.stream().collect(Collectors.toMap(k -> k.getCatId().toString(), v -> { //1.遍历每一个一级分类,查到这个一级分类里的所有二级分类 List<CategoryEntity> categoryEntities = getParent_cid(selectList,v.getParentCid()); //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.找三级分类 List<CategoryEntity> level3Catelog = getParent_cid(selectList,l2.getCatId()); if (level3Catelog!=null){ List<Catelog2Vo.Catelog3Vo> catelog3Vos = level3Catelog.stream().map(l3 -> { //2.封装成指定格式 Catelog2Vo.Catelog3Vo catelog3Vo = new Catelog2Vo.Catelog3Vo(l2.getCatId().toString(), l3.getCatId().toString(), l3.getName()); return catelog3Vo; }).collect(Collectors.toList()); catelog2Vo.setCatalog3List(catelog3Vos); } return catelog2Vo; }).collect(Collectors.toList()); } return catelog2Vos; })); return collect; }