Netty
Netty 是高性能网络通信框架,常用于长连接、实时推送、聊天和网关场景。
推荐阅读顺序:
如果你想检查自己是否真的从零基础学懂到能面试、能落地、能排查,按 Netty 从零到精通验收清单 逐项验收。它会把 BIO/NIO、Reactor、EventLoop、Pipeline、ByteBuf、编解码、粘包半包、心跳、背压和生产排查串起来。
为什么需要 Netty
Netty 是基于 NIO 的网络通信框架,它封装了 Selector、Channel、Buffer、线程模型和编解码流程,让开发者更容易构建高性能网络应用。
如果直接使用 JDK NIO,需要自己处理很多细节:什么时候注册读事件,什么时候取消写事件,半包数据放在哪里,连接关闭时怎么释放资源,业务处理阻塞了 Selector 线程怎么办。初学者容易把代码写成“能跑但不稳定”,连接一多就出现 CPU 空转、内存泄漏、消息错乱和连接假死。
Netty 的价值是把这些低层细节整理成稳定的模型:
- 用
EventLoop统一处理 IO 事件和任务。 - 用
ChannelPipeline把解码、业务、编码拆成责任链。 - 用
ByteBuf解决网络字节读写和内存管理问题。 - 用内置编解码器解决粘包、拆包、协议边界问题。
- 用启动器和配置项隐藏 NIO 注册、绑定、监听的复杂流程。
Netty 适合什么场景
常见场景:
- IM 聊天、站内消息、实时推送。
- RPC 框架底层通信。
- 网关、代理、长连接服务。
- 自定义 TCP/UDP 协议。
- 大量连接、低延迟网络交互。
普通 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["必要时写回响应"]为什么这样能支撑高并发:
- 连接空闲时不会占住一个线程。
- IO 事件由固定数量的 EventLoop 轮询处理,减少线程创建和切换。
- 同一个 Channel 通常固定在同一个 EventLoop 上,减少并发锁竞争。
- 复杂业务可以投递到业务线程池,避免阻塞 IO 线程。
核心组件
| 组件 | 作用 | 初学者必须知道的点 |
|---|---|---|
EventLoopGroup | 线程组,负责处理连接和 IO 事件 | 不要在 EventLoop 中执行慢 SQL、同步 HTTP、大文件计算 |
Channel | 网络连接抽象 | 一个客户端连接通常对应一个 Channel |
ChannelPipeline | 处理器链 | 入站处理请求,出站处理响应,顺序很重要 |
ChannelHandler | 编解码、业务处理、异常处理 | Handler 是否可共享要看是否有状态 |
ByteBuf | Netty 的高性能字节缓冲区 | 读写指针和引用计数是排查内存问题的关键 |
Bootstrap | 客户端启动器 | 客户端连接远端服务用它 |
ServerBootstrap | 服务端启动器 | 服务端绑定端口、接收连接用它 |
学习重点
- Reactor 线程模型:理解 Boss 和 Worker 分工。
- ByteBuf:理解读写指针、引用计数、内存泄漏。
- 编解码:处理 TCP 粘包半包问题。
- Pipeline:掌握入站、出站处理器的执行顺序。
- 心跳和重连:保障长连接稳定性。
- 背压和限流:避免写入过快导致内存堆积。
排查方向
| 问题 | 排查方向 |
|---|---|
| 连接数上不去 | 检查系统文件句柄、线程模型、端口和 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 负责解码、业务处理和编码。
