NIO和传统 BIO的性能较量

NIO和阻塞式IO的性能较量

基于 JDK 17 + Netty 4.1 的 实战压测报告 阻塞 I/O = 传统 BIO,NIO = 非阻塞 I/O(Netty 封装)


目录

  1. 测试背景
  2. 环境配置
  3. 测试用例设计
  4. 压测结果
  5. 结果分析
  6. 调优建议
  7. 总结

测试背景

  • 阻塞 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% 相近

结果分析

  1. 线程模型

    • BIO:一连接一线程,上下文切换爆炸。
    • NIO:事件驱动,单线程管理万连接,零切换。
  2. 内存

    • BIO:每条线程 1 MB 栈 + 缓冲区。
    • NIO:池化 DirectBuffer,复用内存。
  3. 延迟

    • BIO:阻塞 read() 导致排队。
    • NIO:非阻塞 + 零拷贝,P99 降低一个数量级。

调优建议

场景 参数
Linux 高并发 EpollEventLoopGroup
线程数 EventLoopGroup(0) 自动 CPU*2
内存 -XX:MaxDirectMemorySize=2G
TCP SO_BACKLOG=1024, TCP_NODELAY=true

总结

  • QPS :NIO 是 BIO 的 10+ 倍
  • 延迟 :P99 从 百毫秒降到毫秒级
  • 资源 :线程数 百分之一 ,内存 十分之一
相关推荐
RuoyiOffice8 分钟前
SpringBoot+Vue3 企业考勤如何处理法定假期?节假日方案、调休补班与工作日判断链路拆解
spring boot·后端·vue·anti-design-vue·ruoyioffice·假期·人力
Vane139 分钟前
从零开发一个AI插件,经历了什么?
人工智能·后端
952361 小时前
SpringBoot统一功能处理
java·spring boot·后端
rleS IONS1 小时前
SpringBoot中自定义Starter
java·spring boot·后端
DevilSeagull2 小时前
MySQL(2) 客户端工具和建库
开发语言·数据库·后端·mysql·服务
TeDi TIVE3 小时前
springboot和springframework版本依赖关系
java·spring boot·后端
雨辰AI3 小时前
SpringBoot3 + 人大金仓 V9 微服务监控实战|Prometheus+Grafana+SkyWalking 全链路监控
数据库·后端·微服务·grafana·prometheus·skywalking
Nicander3 小时前
理解 mybatis 源码:vibe-coding一个mini-mybatis
后端·mybatis
小呆呆6664 小时前
Codex 穷鬼大救星
前端·人工智能·后端
FelixBitSoul4 小时前
缓存淘汰策略全解:从原理到手写实现(Java / Go / Python)
后端·面试