Netty笔记
线程模型
Netty 采用多线程 Reactor 模型,核心线程分为两类:
- Boss 线程组 (EventLoopGroup)
- 负责接受客户端连接
- 将新连接注册到 Worker 线程组
- 通常只需要1-2个线程
- Worker 线程组 (EventLoopGroup)
- 处理已建立连接的I/O操作
- 执行用户的 ChannelHandler
- 线程数通常为 CPU 核心数×2
EventLoopGroup bossGroup = new NioEventLoopGroup(1);
EventLoopGroup workerGroup = new NioEventLoopGroup();
核心组件
Channel
- 网络连接的抽象,代表一个打开的连接
- 不同类型的实现:NIO、OIO、Epoll 等
EventLoop
- 事件循环,处理 Channel 的I/O事件
- 一个 EventLoop 可服务多个 Channel
- 每个 Channel 的生命周期内只绑定一个 EventLoop
ChannelPipeline
- 处理链,包含一系列 ChannelHandler
- 入站(Inbound)和出站(Outbound)处理分开
ChannelHandler
- 实际业务逻辑处理器
- 常用实现:编解码器、业务处理器等
ChannelFuture
- 异步操作的结果通知机制
- 可以添加监听器处理操作完成事件
工作流程
典型 TCP 服务器工作流程:
- 初始化两个 EventLoopGroup
- 创建 ServerBootstrap 并配置参数
- 绑定端口,启动服务
- Boss 线程接受连接,分配给 Worker 线程
- Worker 线程处理连接的读写事件
- 事件在 ChannelPipeline 中流转处理
ServerBootstrap b = new ServerBootstrap();
b.group(bossGroup, workerGroup)
.channel(NioServerSocketChannel.class)
.childHandler(new ChannelInitializer<SocketChannel>() {
@Override
public void initChannel(SocketChannel ch) {
ch.pipeline().addLast(new MyHandler());
}
});
ChannelFuture f = b.bind(port).sync();
关键设计
零拷贝
Netty 通过以下方式实现高效数据传输:
ByteBuf
- 自定义的字节容器,支持堆内存和直接内存
- 引用计数管理,减少内存拷贝
- 池化技术减少内存分配开销
CompositeByteBuf
- 合并多个 ByteBuf,逻辑上视为一个整体
- 避免合并时的内存拷贝
FileRegion
- 文件传输时使用零拷贝技术
- 直接通过 DMA 将文件内容发送到网络
内存管理
内存池化
- 预先分配大块内存,减少频繁分配/释放
- 通过 Arena 分配策略提高并发性能
引用计数
- 基于 ReferenceCounted 接口
- 当引用计数为0时自动释放资源
内存泄漏检测
- 通过采样方式检测未释放的 ByteBuf
- 开发阶段可开启严格检测模式
高性能设计
无锁化设计
- 每个 Channel 绑定固定 EventLoop
- 单线程处理 Channel 的所有事件
- 避免多线程竞争
高效序列化
- 支持 Protobuf、Thrift 等高效编解码
- 提供 LengthFieldBasedFrameDecoder 解决粘包问题
灵活的线程模型
- 支持单线程、多线程模型
- 可配置业务线程池处理耗时操作
核心机制
事件传播机制
事件在 Pipeline 中的流动方向:
- Inbound 事件:从网络到应用方向
- 如 channelRead、channelActive 等
- 依次调用 InboundHandler
- Outbound 事件:从应用到网络方向
- 如 write、connect 等
- 逆序调用 OutboundHandler
责任链模式
ChannelPipeline 采用责任链模式:
- 每个 Handler 处理特定功能
- 可以动态添加/移除 Handler
- 事件依次传递,可中途终止
pipeline.addLast("decoder", new MyDecoder());
pipeline.addLast("encoder", new MyEncoder());
pipeline.addLast("handler", new BusinessHandler());
异步编程模型
基于 Future-Listener 机制:
- I/O 操作返回 ChannelFuture
- 可以同步等待或异步监听
- 通过回调处理操作结果
ChannelFuture future = channel.write(msg);
future.addListener(f -> {
if (f.isSuccess()) {
// 操作成功
} else {
// 操作失败
}
});
性能优化
- 合理配置线程数
- BossGroup 通常1个线程足够
- WorkerGroup 建议2-16个线程
- 避免阻塞EventLoop
- 耗时操作应使用业务线程池
- 不要在执行器中执行阻塞操作
- 合理使用内存
- 优先使用池化的直接内存
- 及时释放引用计数对象
- 优化Handler设计
- 共享无状态的Handler
- 避免在Handler中创建大量临时对象