写在前面
本文通过socket编程来实现一个简单的HttpServer。
1:单线程版本
我们使用单线程来实现一个HttpServer,如下:
package dongshi.daddy.io.httpserver;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
public class HttpServer1 {
public static void main(String[] args) throws Exception {
ServerSocket serverSocket = new ServerSocket(8801);
while (true) {
try {
Socket socket = serverSocket.accept();
service(socket);
} catch (Exception e) {
e.printStackTrace();
}
}
}
private static void service(Socket socket) {
String body = "hello,nio1";
try {
System.out.println("request come!!!");
// 模拟耗时操作
Thread.sleep(100);
PrintWriter printWriter = new PrintWriter(socket.getOutputStream(), true);
// 模拟http的响应,分为响应头和响应体(对象的组成也是头和体,不过是对象头和对象体),其中响应头和响应体通过换行符分割
/*** 响应头开始 ***/
printWriter.println("HTTP/1.1 200 OK");
printWriter.println("Content-Type:text/html;charset=utf-8");
// 注意一定要有该内容告知客户端响应体的长度,不然客户端将无法正常读取数据
printWriter.println("Content-Length:" + body.getBytes().length);
// 空行结束响应头
printWriter.println();
/*** 响应头结束 ***/
/*** 响应体开始 ***/
printWriter.write(body);
/*** 响应体结束 ***/
// printWriter.flush();
printWriter.close();
socket.close();
} catch (Exception e) {
}
}
}
curl:
C:\WINDOWS\system32>curl http://localhost:8801
hello,nio1
superbenchmark,即sb压力测试:
D:\>sb -u http://localhost:8801 -c 40 -N 10
Starting at 2023/5/31 星期三 15:48:15
[Press C to stop the test]
92 (RPS: 6.4)
---------------Finished!----------------
Finished at 2023/5/31 星期三 15:48:29 (took 00:00:14.5303526)
102 (RPS: 7) Status 200: 102
RPS: 9 (requests/second)
Max: 4471ms
Min: 194ms
Avg: 3580.1ms
50% below 4380ms
60% below 4389ms
70% below 4400ms
80% below 4405ms
90% below 4415ms
95% below 4418ms
98% below 4418ms
99% below 4420ms
99.9% below 4471ms
130 (RPS: 9)
从RPS: 9 (requests/second)
可以看到处每秒请求数是9,接下来看下多线程版本的性能表现。
2:多线程版本
我们使用多线程来实现一个HttpServer,如下:
package dongshi.daddy.io.httpserver;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
public class HttpServer2 {
public static void main(String[] args) throws Exception {
ServerSocket serverSocket = new ServerSocket(8802);
while (true) {
try {
Socket socket = serverSocket.accept();
// service(socket);
new Thread(() -> {
service(socket);
}).start();
} catch (Exception e) {
e.printStackTrace();
}
}
}
private static void service(Socket socket) {
String body = "hello,nio1";
try {
System.out.println("request come!!!");
Thread.sleep(100);
PrintWriter printWriter = new PrintWriter(socket.getOutputStream(), true);
// 模拟http的响应,分为响应头和响应体(对象的组成也是头和体,不过是对象头和对象体),其中响应头和响应体通过换行符分割
/*** 响应头开始 ***/
printWriter.println("HTTP/1.1 200 OK");
printWriter.println("Content-Type:text/html;charset=utf-8");
// 注意一定要有该内容告知客户端响应体的长度,不然客户端将无法正常读取数据
printWriter.println("Content-Length:" + body.getBytes().length);
// 空行结束响应头
printWriter.println();
/*** 响应头结束 ***/
/*** 响应体开始 ***/
printWriter.write(body);
/*** 响应体结束 ***/
// printWriter.flush();
printWriter.close();
socket.close();
} catch (Exception e) {
}
}
}
superbenchmark,即sb压力测试:
D:\>sb -u http://localhost:8802 -c 40 -N 10
Starting at 2023/5/31 星期三 15:49:33
[Press C to stop the test]
3478 (RPS: 253.3)
---------------Finished!----------------
Finished at 2023/5/31 星期三 15:49:47 (took 00:00:13.9143063)
Status 200: 3444
Status 303: 34
RPS: 308.8 (requests/second)
Max: 315ms
Min: 101ms
Avg: 112.8ms
50% below 108ms
60% below 108ms
70% below 109ms
80% below 110ms
90% below 114ms
95% below 124ms
98% below 218ms
99% below 220ms
99.9% below 238ms
RPS 是308.8,明显好于单线程版本,但是这里是每次创建线程,而创建线程本身因为涉及到底层的操作,所以比较重,我们再改为线程池的版本看下效果。
3:线程池版本
package dongshi.daddy.io.httpserver;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class HttpServer3 {
public static void main(String[] args) throws Exception {
ServerSocket serverSocket = new ServerSocket(8803);
// ExecutorService executorService = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors() * 2);
ExecutorService executorService = Executors.newFixedThreadPool(50);
while (true) {
try {
Socket socket = serverSocket.accept();
// service(socket);
executorService.execute(() -> service(socket));
} catch (Exception e) {
e.printStackTrace();
}
}
}
private static void service(Socket socket) {
String body = "hello,nio1";
try {
System.out.println("request come!!!");
Thread.sleep(100);
PrintWriter printWriter = new PrintWriter(socket.getOutputStream(), true);
// 模拟http的响应,分为响应头和响应体(对象的组成也是头和体,不过是对象头和对象体),其中响应头和响应体通过换行符分割
/*** 响应头开始 ***/
printWriter.println("HTTP/1.1 200 OK");
printWriter.println("Content-Type:text/html;charset=utf-8");
// 注意一定要有该内容告知客户端响应体的长度,不然客户端将无法正常读取数据
printWriter.println("Content-Length:" + body.getBytes().length);
// 空行结束响应头
printWriter.println();
/*** 响应头结束 ***/
/*** 响应体开始 ***/
printWriter.write(body);
/*** 响应体结束 ***/
// printWriter.flush();
printWriter.close();
socket.close();
} catch (Exception e) {
}
}
}
superbenchmark,即sb压力测试:
D:\>sb -u http://localhost:8803 -c 40 -N 10
Starting at 2023/5/31 星期三 15:59:52
[Press C to stop the test]
3422 (RPS: 248.7)
3428 (RPS: 248.9) ---------------Finished!----------------
3428 (RPS: 248.9) Finished at 2023/5/31 星期三 16:00:06 (took 00:00:13.8633252)
3466 (RPS: 251.6) Status 200: 3430
Status 303: 36
RPS: 314.4 (requests/second)
Max: 256ms
Min: 101ms
Avg: 112.9ms
50% below 107ms
60% below 108ms
70% below 108ms
80% below 109ms
90% below 114ms
95% below 126ms
98% below 217ms
99% below 232ms
99.9% below 255ms
RPS 是314.4,好于多线程版本的308.8。
写在后面
参考文章列表
SuperBenchmarker(简称“sb“)压力测试工具详解 。
在windows上通过choco安装superbenchmarker进行压测 。