写在前面
本文通过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: 102RPS: 9 (requests/second)
Max: 4471ms
Min: 194ms
Avg: 3580.1ms50% below 4380ms60% below 4389ms70% below 4400ms80% below 4405ms90% below 4415ms95% below 4418ms98% below 4418ms99% 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: 34RPS: 308.8 (requests/second)
Max: 315ms
Min: 101ms
Avg: 112.8ms50% below 108ms60% below 108ms70% below 109ms80% below 110ms90% below 114ms95% below 124ms98% below 218ms99% 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: 36RPS: 314.4 (requests/second)
Max: 256ms
Min: 101ms
Avg: 112.9ms50% below 107ms60% below 108ms70% below 108ms80% below 109ms90% below 114ms95% below 126ms98% below 217ms99% below 232ms
99.9% below 255ms
RPS 是314.4,好于多线程版本的308.8。
写在后面
参考文章列表
SuperBenchmarker(简称“sb“)压力测试工具详解 。
在windows上通过choco安装superbenchmarker进行压测 。