前言
由于互联网编程实验二第三题要求比较使用线程池与否的服务器的并发性能,遂检索信息并了解到Apache JMeter这个工具
本文主要介绍了在已有Java JDK的情况下对Apache JMeter的安装及配置,以及利用JMeter进行TCP压力测试
一、安装及配置
先在官网下载压缩包:Apache JMeter - Download Apache JMeter
将文件apache-jmeter-5.6.3.zip解压到自己选择的目录中
在根目录下,找到bin文件夹,进入文件夹中,找到jmeter.bat,双击即可打开软件
二、TCP服务器
编写Java代码,在Server类的main函数中,指定一个端口作为服务器端口,并在while死循环中不断接收客户端的请求,对于每一个请求新开一个线程ThreadServer,并在其中处理请求
public class Server { static int PORT = 9000; static int MAX_POOL = 100; public static void main(String[] args) throws IOException { ServerSocket server = new ServerSocket(PORT); // without thread poolwhile (true) { Socket accept = server.accept(); Thread thread = new Thread(new ThreadServer(accept)); thread.start(); } // with thread pool // ExecutorService service = Executors.newFixedThreadPool(MAX_POOL); // while (true) { // Socket accept = server.accept(); // service.submit(new ThreadServer(accept)); // } }
}
代码中将服务器分为两种模式,第一段为没有线程池的模式,第二段为使用线程池的模式。通过分别注释并重新编译运行来启动不同模式的服务器
为了简化服务端和客户端的交互,我们在ThreadServer中仅仅打印连接成功以及断开连接的信息,而不进行多余的通信,如下
public class ThreadServer implements Runnable{ Socket socket; static int BUFFER_SIZE = 1024; static String EXIT_STR = "exit"; public ThreadServer(Socket socket){ this.socket = socket; } @Override public void run(){ try{ System.out.println("connection start"); OutputStream out = socket.getOutputStream(); out.write("connect successfully".getBytes()); // echo service // InputStream in = socket.getInputStream(); // byte[] inBytes = new byte[BUFFER_SIZE]; // int len; // while((len = in.read(inBytes)) != 0){ // String str = new String(inBytes, 0, len); // if(str.equals(EXIT_STR)) // break; // System.out.println("received: " + str); // out.write(str.getBytes()); // } System.out.println("connection end"); socket.close(); }catch (IOException e){ e.printStackTrace(); } }
}
注释部分是提供echo服务,需要客户端同步实现,在本题中无需使用,客户端参考以下代码:
public class Client { static String SERVER_HOST = "127.0.0.1"; static int SERVER_PORT = 9000; static int BUFFER_SIZE = 1024; static String EXIT_STR = "exit"; public static void main(String[] args) throws IOException { Socket client = new Socket(SERVER_HOST, SERVER_PORT); Scanner scanner = new Scanner(System.in); OutputStream out = client.getOutputStream(); InputStream in = client.getInputStream(); byte[] buffer = new byte[BUFFER_SIZE]; // echo while(scanner.hasNextLine()){ String lineOfWord = scanner.nextLine(); out.write(lineOfWord.getBytes()); if(lineOfWord.equals(EXIT_STR)) break; int len = in.read(buffer); System.out.println("echo: " + new String(buffer, 0, len)); } client.close(); }
}
三、并发测试
1. 创建测试计划
打开JMeter,右键单击测试计划(test plan),一直选择到新建线程组,如下
右键单击新建的线程组,分别新建TCP Sampler和Response Time Graph,分别用于TCP连接以及输出响应时间与时间的折线图
注意到TCP Sampler属于Sampler模块,我们也可在此模块中选择HTTP Request进行HTTP请求的测试;Response Time Graph属于Listener模块,我们可以在此模块中选择Aggregate Report输出测试的聚合报告,包括响应时间的平均值、最小值,以及吞吐量等
在TCP Sampler中,指定服务器的地址及端口号如下
在Response Time Graph指定记录的间隔,单位为ms,这里设置为1000
2. 正式测试
首先启动服务端
D:\idea project\internetprog\exp2\exp2\src>javac Server.javaD:\idea project\internetprog\exp2\exp2\src>java Server
单击Thread Group,不断更改以下几个参数,测试不同程度的并发下服务的响应时间
其中
- Number of Threads即创建的线程数
- Ramp-up period即在多长时间内创建以上线程数,单位为s
- Loop Count即执行的次数,勾选Infinite代表无限次执行
在本例中,我选择固定Ramp-up period为1,Loop Count为5,修改Number of Threads依次为2000、4000、6000、8000以及10000,分别对比在有无线程池的服务器中,出现较长延迟的响应时间。每种情况分别测试3~5次。
测试结果如下
线程数 | 无线程池 | 有线程池 |
---|---|---|
2000 | 1ms内 | 1ms内 |
4000 | 1s内 | 1s内 |
6000 | 2s内 | 2s内 |
8000 | 4s ~ 8s | 3s ~ 6s |
10000 | 10s ~ 16s | 3s ~ 7s |
可以看出,当并发压力增大时,无线程池出现的长延迟响应时间,相比有线程池的情况增长要快