NIO和阻塞式IO的性能较量
基于 JDK 17 + Netty 4.1 的 实战压测报告 阻塞 I/O = 传统 BIO,NIO = 非阻塞 I/O(Netty 封装)
目录
- 测试背景
- 环境配置
- 测试用例设计
- 压测结果
- 结果分析
- 调优建议
- 总结
测试背景
- 阻塞 I/O(BIO) :
ServerSocket.accept()
+ 一连接一线程 - 非阻塞 I/O(NIO) :
Selector
+ 单线程管理万连接
疑问 :QPS、延迟、内存、CPU 到底差多少? 答案 :实测 10 倍差距 起步。
环境配置
表格
复制
项目 | 值 |
---|---|
CPU | Intel i7-12700H 14 核 |
内存 | 32 GB DDR4 |
OS | Ubuntu 22.04 |
JDK | 17.0.8 |
压测工具 | wrk 4.1.0 |
网络 | 本地回环(排除网卡干扰) |
测试用例设计
1. 阻塞 I/O(BIO)版本
java
// 每连接新建线程
while (true) {
Socket socket = serverSocket.accept();
new Thread(() -> handle(socket)).start();
}
private static void handle(Socket socket) {
try (BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
PrintWriter out = new PrintWriter(socket.getOutputStream(), true)) {
String line = in.readLine();
out.println("HTTP/1.1 200 OK\r\nContent-Length: 5\r\n\r\nHello");
} catch (IOException e) {
e.printStackTrace();
}
}
2. 非阻塞 I/O(NIO)版本
scss
EventLoopGroup boss = new NioEventLoopGroup(1);
EventLoopGroup worker = new NioEventLoopGroup(0);
ServerBootstrap b = new ServerBootstrap()
.group(boss, worker)
.channel(NioServerSocketChannel.class)
.childHandler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel ch) {
ch.pipeline().addLast(new HttpServerCodec());
ch.pipeline().addLast(new SimpleChannelInboundHandler<HttpRequest>() {
@Override
protected void channelRead0(ChannelHandlerContext ctx, HttpRequest msg) {
FullHttpResponse resp = new DefaultFullHttpResponse(
HttpVersion.HTTP_1_1,
HttpResponseStatus.OK,
Unpooled.wrappedBuffer("Hello".getBytes()));
ctx.writeAndFlush(resp);
}
});
}
});
b.bind(8080).sync();
压测结果
命令
arduino
wrk -t12 -c1000 -d30s http://localhost:8080/
表格
复制
指标 | BIO (1 线程/连接) | NIO (Netty) | 倍数 |
---|---|---|---|
QPS | 12,000 | 158,000 | ×13 |
P99 延迟 | 85 ms | 6.5 ms | ×13 |
线程数 | 1000+ | 16 | ×62 |
内存峰值 | 2.4 GB | 180 MB | ×13 |
CPU 利用率 | 420% | 380% | 相近 |
结果分析
-
线程模型
- BIO:一连接一线程,上下文切换爆炸。
- NIO:事件驱动,单线程管理万连接,零切换。
-
内存
- BIO:每条线程 1 MB 栈 + 缓冲区。
- NIO:池化 DirectBuffer,复用内存。
-
延迟
- BIO:阻塞
read()
导致排队。 - NIO:非阻塞 + 零拷贝,P99 降低一个数量级。
- BIO:阻塞
调优建议
场景 | 参数 |
---|---|
Linux 高并发 | EpollEventLoopGroup |
线程数 | EventLoopGroup(0) 自动 CPU*2 |
内存 | -XX:MaxDirectMemorySize=2G |
TCP | SO_BACKLOG=1024 , TCP_NODELAY=true |
总结
- QPS :NIO 是 BIO 的 10+ 倍
- 延迟 :P99 从 百毫秒降到毫秒级
- 资源 :线程数 百分之一 ,内存 十分之一