Bootstrap

Java基础之《JVM性能调优(15)—系统并发性能jmeter压测》

一、估算系统的性能最大吞吐量

1、首页获取商品列表
模拟APP首页产生10KB的数据。

2、在https://start.spring.io/生成一个springboot项目
添加:ItemVO.java

package com.example.demo;

public class ItemVO {
    private int id;

    private String name;

    private String city;

    private int price;

    private int number;

    private String picture;

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getCity() {
        return city;
    }

    public void setCity(String city) {
        this.city = city;
    }

    public int getPrice() {
        return price;
    }

    public void setPrice(int price) {
        this.price = price;
    }

    public int getNumber() {
        return number;
    }

    public void setNumber(int number) {
        this.number = number;
    }

    public String getPicture() {
        return picture;
    }

    public void setPicture(String picture) {
        this.picture = picture;
    }
}

添加:ItemController.java

package com.example.demo;

import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.ArrayList;
import java.util.List;

@RestController
public class ItemController {

    @RequestMapping("/home")
    public List<ItemVO> home() throws InterruptedException {
        byte[] bytes1 = new byte[1024*5];
        //模拟业务操作,睡5毫秒
        Thread.sleep(5);
        byte[] bytes2 = new byte[1024*5];
        //模拟业务操作,睡5毫秒
        Thread.sleep(5);

        List<ItemVO> lists = getItems();
        return lists;
    }

    private List<ItemVO>getItems() {
        List<ItemVO> lists = new ArrayList<>();
        for(int i=0; i<10; i++) {
            ItemVO item = new ItemVO();
            item.setId(i+1000);
            item.setName("商品"+i);
            item.setCity("上海");
            item.setNumber(i);
            item.setPrice(i);
            item.setPicture("http://www.picture.com");
            lists.add(item);
        }
        return lists;
    }

}

外部配置文件:application.properties

server.port=8080

3、上传文件到服务器,并启动
java -jar -Xms200m -Xmx200m -Dspring.config.location=/appserver/application.properties demo-0.0.1-SNAPSHOT.jar &

4、访问地址http://IP地址:8080/home

5、第一步:估算QPS理论值
先看单次请求多少毫秒
假设是200毫秒

QPS = 1000 / 200 = 5
所以压测的时候,在5左右上下浮动,是对的,和这个值偏离太多就要找原因。

1条线程,QPS = 5
10条线程,QPS = 50
100条线程,QPS = 500
200条线程,QPS = 1000

二、采用jmeter压测,验证系统吞吐量最大理论值

1、jmeter
jmeter是基于Java的压力测试工具。用于对软件做压力测试,它最初被设计用于Web应用测试,但后来扩展到其他测试领域。
https://jmeter.apache.org/

2、运行
windows下运行jmeter.bat

3、操作
1)设置语言
Options - Choose Language - Chinese

2)设置名称:demo测试

3)添加线程组
demo测试 - 右键 - 添加 - 线程(用户) - 线程组
线程数:200
Ramp-Up时间(秒):1
循环次数:500(假设我们要10万并发)

4)添加结果报告
线程组 - 右键 - 添加 - 监听器 - 查看结果树
线程组 - 右键 - 添加 - 监听器 - 汇总报告
线程组 - 右键 - 添加 - 监听器 - 聚合报告

5)添加HTTP请求
线程组 - 右键 - 添加 - 取样器 - HTTP请求

协议:http,服务器名称或IP:192.168.3.203,端口号:8080
HTTP请求:GET,路径:/home
去掉跟随重定向、使用KeepAlive

三、第一次压测(验证单条线程的最大QPS)

1、改tomcat配置文件
只配1条线程:

server.port=8080
server.tomcat.threads.max=1

2、内存给512M
年轻代=170M(Eden=136M,survivor0=17M,survivor1=17M)
老年代=341M
体验下效果

3、重新打包,名称为demo-0.0.1-SNAPSHOT-1.jar
java -jar -Xms512m -Xmx512m -Dspring.config.location=/appserver/application.properties demo-0.0.1-SNAPSHOT.jar &

4、jmeter点击启动

5、报告
1)汇总报告

吞吐量是86,远远大于预计的5,这是为啥

2)查看结果树
勾选仅错误日志,查看错误

四、分别测试
1条线程:吞吐量80
10条线程:吞吐量95
    错误Connection timed out: connect
    错误Connection refused: connect
100条线程:吞吐量85
    错误Connection timed out: connect
    错误Connection refused: connect
200条线程:吞吐量86
    错误Connection timed out: connect
    错误Connection refused: connect
    错误Read timed out

五、tomcat线程
tomcat用的是nio模式,如果最大线程设置为10,则exec线程有10个

 1924 root      20   0 2587756 281976  13540 S  0.0 15.0   0:00.22 http-nio-8080-e                                                                                                                     
 1925 root      20   0 2587756 281976  13540 S  0.0 15.0   0:00.00 http-nio-8080-e                                                                                                                     
 1926 root      20   0 2587756 281976  13540 S  0.0 15.0   0:00.00 http-nio-8080-e                                                                                                                     
 1927 root      20   0 2587756 281976  13540 S  0.0 15.0   0:00.00 http-nio-8080-e                                                                                                                     
 1928 root      20   0 2587756 281976  13540 S  0.0 15.0   0:00.00 http-nio-8080-e                                                                                                                     
 1929 root      20   0 2587756 281976  13540 S  0.0 15.0   0:00.00 http-nio-8080-e                                                                                                                     
 1930 root      20   0 2587756 281976  13540 S  0.0 15.0   0:00.00 http-nio-8080-e                                                                                                                     
 1931 root      20   0 2587756 281976  13540 S  0.0 15.0   0:00.00 http-nio-8080-e                                                                                                                     
 1932 root      20   0 2587756 281976  13540 S  0.0 15.0   0:00.00 http-nio-8080-e                                                                                                                     
 1933 root      20   0 2587756 281976  13540 S  0.0 15.0   0:00.00 http-nio-8080-e                                                                                                                     
 1934 root      20   0 2587756 281976  13540 S  0.0 15.0   0:00.01 http-nio-8080-P                                                                                                                     
 1935 root      20   0 2587756 281976  13540 S  0.0 15.0   0:00.01 http-nio-8080-A

六、jmeter界面说明

1、变量定义
双击测试项目名称
全局变量(用户定义的变量):User Defined Variables

局部变量(用户参数):User Parameters
线程组上 - 右键 - Add

2、http请求头
HTTP Header Manager
线程组上 - 右键 - Add

3、需要动态生成的变量
BeanShell Sampler
线程组上 - 右键 - Add

shell脚本中,jdk自带的类,直接import就能使用
外部.java文件和jar需要指定或配置后才能使用

4、http请求
HTTP Request
线程组上 - 右键 - Add

5、结果查看
View Results Tree
Summary Report
Aggregate Report
线程组上 - 右键 - Add

6、执行日志
点击右上角的三角感叹号符号

7、Json提取器

提取上一个Sampler返回json串的某个返回字段

8、跨线程组传参数
前一个线程组存入全局属性:${__setProperty(xxx, ${xxx})}

后一个线程组获取属性:${__P(xxx)}

9、响应断言
在Http Request下加响应断言,这样只对http请求这个sampler有效,如果加在线程组下面,则对每个脚本执行都会有断言

10、固定定时器
在Http Request下加固定定时器,控制线程等待多少毫秒执行一次

11、jmeter内置对象
(1)vars对象
定义变量
vars.put(String key, String value)
vars.get(String key)

(2)ctx对象

import org.apache.jmeter.samplers.SampleResult;

// 获取取样器结果
SampleResult result = ctx.getPreviousResult();
// 获取请求头
String RequestHeaders = result.getRequestHeaders();
// 获取响应头
String ResponseHeaders = result.getResponseHeaders();
// 获取响应码
String responseCode = result.getResponseCode();
// 获取响应数据
String responseData = result.getResponseDataAsString();

(3)prev对象
该内置对象可以直接使用SampleResult类中的方法

// 获取请求头
String RequestHeaders = prev.getRequestHeaders();
// 获取响应头
String ResponseHeaders = prev.getResponseHeaders();
// 获取响应码
String responseCode = prev.getResponseCode();
// 获取响应数据
String responseData = prev.getResponseDataAsString();

12、jmeter清除open recent(最近打开)
搜索regedit,打开“注册表编辑器”,按照以下路径:
计算机\HKEY_CURRENT_USER\Software\JavaSoft\Prefs\org\apache\jmeter\gui\action

删除recent_file_0到recent_file_5这六个记录

13、jmeter线程组单个执行和顺序执行

(1)线程组分类
setUp Thread Group:在普通线程组执行之前执行
Thread Group:普通线程组
tearDown Thread Group:在普通线程组执行之后执行

(2)jmeter会按照添加顺序依次执行所有的setUp Thread Group(不可跳过)

(3)tearDown Thread Group测试下来也会执行(不可跳过)

(4)线程单个执行和多个顺序执行,只针对于Thread Group普通线程组
所以如果你案例里只有三个线程组:
setUp Thread Group
Thread Group
tearDown Thread Group
不论怎么设置都会执行,因为只有一个普通线程组

(5)配置参数

Run Thread Groups consecutively (i.e. one at time):
控制线程组是并行执行还是顺序执行

说明:
当存在多个线程组(Thread Group),jmeter默认是同时执行的,也就是说是并行的
如果勾选了TestPlan(测试计划)中的Run Thread Groups consecutively(独立运行每个组件组),让线程组执行结束后再执行下一个线程组,就可以简单的控制线程组的执行顺序

Run tearDown Thread Groups after shutdown of main threads:
控制如果主线程Thread Groups意外终止,是否会执行tearDown线程,勾选才执行tearDown,不勾选不执行

(6)结论
jmeter如果设置多个线程组(Thread Group),只能设置并行执行还是顺序执行,不能其中某一个单个执行!

14、找到了一个方法,可以将线程组状态设置为Disable,这样就不会执行这个线程组了

;