Skip to content

Netty

Netty 是高性能网络通信框架,常用于长连接、实时推送、聊天和网关场景。

推荐阅读顺序:

  1. 从零到生产级掌握
  2. 从零到精通验收清单
  3. 商业场景训练营
  4. 基础
  5. Reactor模型
  6. EventLoop
  7. ByteBuf
  8. ChannelPipeline
  9. 编解码
  10. 粘包拆包
  11. 实战入门
  12. 面试题

如果你想检查自己是否真的从零基础学懂到能面试、能落地、能排查,按 Netty 从零到精通验收清单 逐项验收。它会把 BIO/NIO、Reactor、EventLoop、Pipeline、ByteBuf、编解码、粘包半包、心跳、背压和生产排查串起来。

为什么需要 Netty

Netty 是基于 NIO 的网络通信框架,它封装了 Selector、Channel、Buffer、线程模型和编解码流程,让开发者更容易构建高性能网络应用。

如果直接使用 JDK NIO,需要自己处理很多细节:什么时候注册读事件,什么时候取消写事件,半包数据放在哪里,连接关闭时怎么释放资源,业务处理阻塞了 Selector 线程怎么办。初学者容易把代码写成“能跑但不稳定”,连接一多就出现 CPU 空转、内存泄漏、消息错乱和连接假死。

Netty 的价值是把这些低层细节整理成稳定的模型:

  1. EventLoop 统一处理 IO 事件和任务。
  2. ChannelPipeline 把解码、业务、编码拆成责任链。
  3. ByteBuf 解决网络字节读写和内存管理问题。
  4. 用内置编解码器解决粘包、拆包、协议边界问题。
  5. 用启动器和配置项隐藏 NIO 注册、绑定、监听的复杂流程。

Netty 适合什么场景

常见场景:

  1. IM 聊天、站内消息、实时推送。
  2. RPC 框架底层通信。
  3. 网关、代理、长连接服务。
  4. 自定义 TCP/UDP 协议。
  5. 大量连接、低延迟网络交互。

普通 HTTP CRUD 系统不一定需要直接使用 Netty,Spring MVC 或 Spring WebFlux 已经足够。Netty 更适合需要掌控网络协议和连接生命周期的场景。

Netty 请求处理流程

mermaid
flowchart TD
    A["客户端连接"] --> B["BossGroup 接收连接"]
    B --> C["注册到 WorkerGroup"]
    C --> D["ChannelPipeline 处理链"]
    D --> E["Decoder 解码字节"]
    E --> F["业务 Handler 处理消息"]
    F --> G["Encoder 编码响应"]
    G --> H["写回客户端"]

这张图要按两个边界理解:

边界说明如果分不清会怎样
连接接入边界BossGroup 只负责接收新连接,把连接交给 WorkerGroup把耗时逻辑放到 BossGroup 会影响新连接接入
消息处理边界WorkerGroup 触发 Pipeline,Pipeline 再逐层处理消息把解码、鉴权、业务、编码混在一个 Handler 中,后期很难排查和复用

核心原理一眼看懂

Netty 的本质不是“更多线程”,而是“少量线程监听大量连接的事件”。一个连接没有数据时不占用一个业务线程,有数据可读、可写或关闭时才触发事件。

mermaid
flowchart TD
    A["操作系统监听 Socket 状态"] --> B["Selector 发现就绪事件"]
    B --> C["EventLoop 取出事件"]
    C --> D["找到对应 Channel"]
    D --> E["触发 ChannelPipeline"]
    E --> F["Handler 顺序处理"]
    F --> G["必要时写回响应"]

为什么这样能支撑高并发:

  1. 连接空闲时不会占住一个线程。
  2. IO 事件由固定数量的 EventLoop 轮询处理,减少线程创建和切换。
  3. 同一个 Channel 通常固定在同一个 EventLoop 上,减少并发锁竞争。
  4. 复杂业务可以投递到业务线程池,避免阻塞 IO 线程。

核心组件

组件作用初学者必须知道的点
EventLoopGroup线程组,负责处理连接和 IO 事件不要在 EventLoop 中执行慢 SQL、同步 HTTP、大文件计算
Channel网络连接抽象一个客户端连接通常对应一个 Channel
ChannelPipeline处理器链入站处理请求,出站处理响应,顺序很重要
ChannelHandler编解码、业务处理、异常处理Handler 是否可共享要看是否有状态
ByteBufNetty 的高性能字节缓冲区读写指针和引用计数是排查内存问题的关键
Bootstrap客户端启动器客户端连接远端服务用它
ServerBootstrap服务端启动器服务端绑定端口、接收连接用它

学习重点

  1. Reactor 线程模型:理解 Boss 和 Worker 分工。
  2. ByteBuf:理解读写指针、引用计数、内存泄漏。
  3. 编解码:处理 TCP 粘包半包问题。
  4. Pipeline:掌握入站、出站处理器的执行顺序。
  5. 心跳和重连:保障长连接稳定性。
  6. 背压和限流:避免写入过快导致内存堆积。

排查方向

问题排查方向
连接数上不去检查系统文件句柄、线程模型、端口和 backlog
内存持续增长检查 ByteBuf 是否释放、是否有写队列堆积
消息解析错乱检查协议边界、长度字段、粘包半包处理
延迟升高检查 EventLoop 是否被阻塞、业务是否异步化

如果不用或用错 Netty 会怎样

场景可能后果正确思路
用 BIO 做大量长连接线程数量暴涨,内存和上下文切换成本很高使用 NIO/Reactor 模型
在 EventLoop 里查数据库一个慢查询拖慢同 EventLoop 上的多个连接投递到业务线程池,完成后切回 EventLoop 写响应
没有处理粘包半包消息解析错乱,偶发出现“多一段/少一段”数据使用长度字段、分隔符或固定长度协议
忘记释放 ByteBuf堆外内存持续增长,最终 OOM明确消息所有权,使用 release 或自动释放 Handler
Pipeline 顺序错误编码器、解码器、鉴权器不生效按“拆包 -> 解码 -> 鉴权 -> 业务 -> 编码”组织

代码 Demo:最小 Netty 服务端

下面是一个最小 TCP 服务端,收到客户端消息后回写 ok

java
EventLoopGroup boss = new NioEventLoopGroup(1);
EventLoopGroup worker = new NioEventLoopGroup();

try {
    ServerBootstrap bootstrap = new ServerBootstrap();
    bootstrap.group(boss, worker)
        .channel(NioServerSocketChannel.class)
        .childHandler(new ChannelInitializer<SocketChannel>() {
            @Override
            protected void initChannel(SocketChannel channel) {
                channel.pipeline()
                    .addLast(new StringDecoder())
                    .addLast(new StringEncoder())
                    .addLast(new SimpleChannelInboundHandler<String>() {
                        @Override
                        protected void channelRead0(ChannelHandlerContext ctx, String msg) {
                            System.out.println("收到消息:" + msg);
                            ctx.writeAndFlush("ok\n");
                        }
                    });
            }
        });

    ChannelFuture future = bootstrap.bind(9000).sync();
    future.channel().closeFuture().sync();
} finally {
    boss.shutdownGracefully();
    worker.shutdownGracefully();
}

这个 Demo 对应前面的流程:Boss 接收连接,Worker 处理 IO,Pipeline 负责解码、业务处理和编码。