压力测试考察当前软硬件环境下系统所能承受的最大负荷并帮助找出系统瓶颈所在。压测都是为了系统在线上的处理能力和稳定性维持在一个标准范围内,做到心中有数。
使用压力测试,我们有希望找到很多种用其他测试方法更难发现的错误。有两种错误类型是:内存泄漏,并发与同步。
有效的压力测试系统将应用以下这些关键条件:重复,并发,量级,随机变化。
响应时间指用户从客户端发起一个请求开始,到客户端接收到从服务器端返回的响应结束,整个过程所耗费的时间。
HPS (Hits Per Second) :每秒点击次数,单位是次秒。
TPS (Transaction per Second);,系統每秒处理交易数,单位是笔秒。
OPS (Query per Second) :系统每秒处理查询次数,单位是次秒。
对于互联网业务中,如果某些业务有且仅有一个请求连接,那么TPS-QPS-HPS,一般情况下用TPS来衡量整个业务流程,用QPS来衡量接口查询次数,用HPS来表示对服务器单击请求。
金融行业: 1000TPS-50000TPs,不包括互联网化的活动
保险行业: 100TPS-100000Tps. 包括互联网化的活动
制造行业: 10TPS-5000TPS
互联网电子商务: 1000OTPS-1000000TPS
互联网中型网站: 1000TPS-50000TPS
互联网小型网站: 500TPS~1000TPS
最大响应时间(Max Resonse Time,指用户发出请求或者指令到系统做出反应(响应)的最大时间。
最少响应时间(Mininum RespanseTime)指用户发出请求或者指令到系统做出反应(响应)的最少时间。
90%响应时间(90% Response Time) 是指所有用户的响应时间进行排序,第90%的响应时间。
从外部看,性能测试主要关注如下三个指标
吞吐量;每秒钟系统能够处理的请求数、任务数.
响应时间:服务处理一个请求或一个任务的耗时。
错误率:一批请求中结果出错的请求所占比例。
下载页
下载对应的压缩包,解压运行bin下的jmeter.bat即可
打开修改中文
添加线程组模拟用户
模拟200个用户1s内启动,循环100次,即20000个请求
添加http请求
请求百度
添加查看结果树,汇总报告
请求项目测试
可参考
windows本身提供的端口访问机制的问题。
Windows提供给TCP/IP错接的端口为1024-5000,并且要四分钟来循环回收他们。就导致我们在短时间内跑大量的请求时将端口占落了.
1.cmd中,用regedit命令打开注册表
2在 HKEY LOCAL MACHINESYSTEM\CurrentControlSet\Services\Ticpip\lParameters 下,
1、右击parameters,添加一个新的DWORD.名字为MaxUserPort
2、然后双击MaxUserPort,输入数值数据为65534,基数选择十进制(如果是分布式运行的话,控制机器和负载机器都需要这样操作哦)
3、30s即回收端口 TCPTimedWaitDelay: 30
3,修改配置完毕之后记得重启机器才会生效
微软帮助文档
Jdk的两个小工具console visulvm (升级版的consoke),通过命令行启动,可监控本地和远程应用.远程应用需要配置
使用步骤
对Nginx进行测试
监控nginx状态
docker stats
内存消耗少。多计算、耗cpu
对Gateway进行压测,同时监控内存
耗cpu
伊甸园区内存小,GC频繁
对简单请求进行压测
/**
* 简单请求
* @return
*/
@ResponseBody
@GetMapping("/hello")
public String hello() {
return "hello";
}
网关+简单请求
- id: product_route
#lb 表示转发至服务 服务也需注册到nacos
uri: lb://mall-product
predicates:
#当请求为指定路径 断言为真 **代表任意 这个路径需往上写提高优先级不被/api/**给拦截
- Path=/api/product/**,/hello
filters:
#断言为真,重写路径/api/(?.*)改为/$\{segment}
#例请求http://localhost:88/api/product/category/list/tree会改为
#http://mall-product端口/product/category/list/tree
- RewritePath=/api/(?>.*),/$\{segment}
吞吐量下降
将静态资源static包传输到nginx的html文件夹下
修改之前conf.d下商品服务配置文件,添加上规则
location /static/{
root /usr/share/nginx/html;
}
重启nginx
docker restart nginx
注意html文件中路径应该加上static,比如
href="/static/index/css/swiper-3.4.2.min.css"
200个线程 并行下载6
设置vm参数:-Xmx=100m
服务崩溃:
正确参数:
-Xmx=1024m -Xms1024m -Xmn512m
压测内容 | 压测线程数 | 吞吐量/s | 90%响应时间 | 99%响应时间 |
---|---|---|---|---|
Nginx | 50 | 8931 | 7 | 75 |
Gateway | 50 | 8972 | 8 | 36 |
简单服务 | 50 | 5899 | 9 | 143 |
首页一级菜单渲染 | 50 | 270 | 267 | 365 |
三级分类数据获取 | 50 | 7 | 11192 | 12755 |
首页全量数据获取 | 50 | 8/19 | ||
Nginx+Gateway | 50 | |||
Gateway 简单服务 | 50 | 1940 | 44 | 96 |
全链路 | 50 | 968 | 120 | 350 |
中间件越多,性能损失越大,大多都损失网络交互了
业务:
Db(MySQL优化,加索引,关日志)
模板的渲染速度(开缓存)
原有代码(查询了三次数据库):
/** * 查出所有分类 返回首页json * * @return */ @Override public Map<String, List<Catalog2Vo>> getCatalogJson() {
//先查出所有一级分类 List<CategoryEntity> level1Categorys = getLevel1Categorys(); //封装数据 map k,v 结构 Map<String, List<Catalog2Vo>> map = level1Categorys.stream().collect(Collectors.toMap(k -> k.getCatId().toString(), v -> { //每一个的一级分类,查到这个一级分类的二级分类 List<CategoryEntity> category2Entities = baseMapper.selectList(new QueryWrapper<CategoryEntity>().eq("parent_cid", v.getCatId())); List<Catalog2Vo> catelog2Vos = null; if (category2Entities != null) { catelog2Vos = category2Entities.stream().map(level2 -> { //封装catalog2Vo Catalog2Vo catalog2Vo = new Catalog2Vo(v.getCatId().toString(), null, level2.getCatId().toString(), level2.getName()); //每一个二级分类,查到三级分类 List<CategoryEntity> category3Entities = baseMapper.selectList(new QueryWrapper<CategoryEntity>().eq("parent_cid", level2.getCatId())); if (category3Entities != null) { List<Object> catalog3List = category3Entities.stream().map(level3 -> { //封装catalog3Vo Catalog2Vo.Catalog3Vo catalog3Vo = new Catalog2Vo.Catalog3Vo(level2.getCatId().toString(), level3.getCatId().toString(), level3.getName()); return catalog3Vo; }).collect(Collectors.toList()); //封装catalog3Vo到catalog2Vo catalog2Vo.setCatalog3List(catalog3List); } return catalog2Vo; }).collect(Collectors.toList()); } //返回v=catalog2Vo return catelog2Vos; })); return map; }
应该第一次查询出来所有,后面拿查询出来的去操作
优化后:
/** * 根据传进分类筛选出对应级别 * * @param list * @param parent_cid * @return */ public List<CategoryEntity> getCategorys(List<CategoryEntity> list, Long parent_cid) {
List<CategoryEntity> collect = list.stream().filter(l -> parent_cid.equals(l.getParentCid())).collect(Collectors.toList()); return collect; } /** * 查出所有分类 返回首页json * * @return */ @Override public Map<String, List<Catalog2Vo>> getCatalogJson() { //查询出所有分类 List<CategoryEntity> selectList = baseMapper.selectList(null); //先查出所有一级分类 List<CategoryEntity> level1Categorys = getCategorys(selectList, 0L); //封装数据 map k,v 结构 Map<String, List<Catalog2Vo>> map = level1Categorys.stream().collect(Collectors.toMap(k -> k.getCatId().toString(), v -> { //每一个的一级分类,查到这个一级分类的二级分类 List<CategoryEntity> category2Entities = getCategorys(selectList, v.getCatId()); List<Catalog2Vo> catelog2Vos = null; if (category2Entities != null) { catelog2Vos = category2Entities.stream().map(level2 -> { //封装catalog2Vo Catalog2Vo catalog2Vo = new Catalog2Vo(v.getCatId().toString(), null, level2.getCatId().toString(), level2.getName()); //每一个二级分类,查到三级分类 List<CategoryEntity> category3Entities = getCategorys(selectList, level2.getCatId()); if (category3Entities != null) { List<Object> catalog3List = category3Entities.stream().map(level3 -> { //封装catalog3Vo Catalog2Vo.Catalog3Vo catalog3Vo = new Catalog2Vo.Catalog3Vo(level2.getCatId().toString(), level3.getCatId().toString(), level3.getName()); return catalog3Vo; }).collect(Collectors.toList()); //封装catalog3Vo到catalog2Vo catalog2Vo.setCatalog3List(catalog3List); } return catalog2Vo; }).collect(Collectors.toList()); } //返回v=catalog2Vo return catelog2Vos; })); return map; }
大型网站高性能架构