Netty
Netty 是基于 Java NIO 的高性能、异步事件驱动网络通信框架,封装了原生 NIO 复杂 API,用于快速开发 TCP/UDP 网络服务、RPC、网关、IM、物联网协议服务(JT/T808、GB28181)。
底层:Java NIO(Selector、Channel、Buffer),也支持 NIO.2(AIO);主流版本 Netty4.x,生产基本不用 Netty3。
一、核心组件
1. EventLoop / EventLoopGroup
EventLoop:单线程,无限循环做三件事:IO事件处理、任务队列执行、定时任务。一个线程绑定一个 Selector。EventLoopGroup:一组 EventLoop。NioEventLoopGroup:主从线程模型- bossGroup:接收客户端连接(accept),线程数一般设 1。
- workerGroup:处理已建立连接的 IO读写、编解码、业务逻辑。默认线程数 = CPU核心数*2。
重要:Channel 一旦注册到某个 EventLoop,整个生命周期都绑定这一个线程,避免多线程竞争。业务耗时操作不要阻塞 EventLoop 线程,耗时业务丢到自定义业务线程池。
2. Channel
Netty 抽象网络连接,对应一条 Socket。常用:
NioSocketChannel:TCP客户端NioServerSocketChannel:TCP服务端NioDatagramChannel:UDP
生命周期:registered → active → read → inactive → unregistered
3. ChannelHandler & ChannelPipeline
Pipeline 是 Handler 的双向链表,事件在链上传播。
ChannelInboundHandler:入站事件(接收数据、连接激活),从head往tail走ChannelOutboundHandler:出站事件(发送数据、关闭连接),从tail往head走
常用Handler:
ByteToMessageDecoder:字节 → Java对象(解码)MessageToByteEncoder:Java对象 → 字节(编码)LengthFieldBasedFrameDecoder:解决粘包拆包最常用,基于长度字段分包(JT/T808、自定义二进制协议必备)ProtobufDecoder / ProtobufEncoder:Protobuf序列化IdleStateHandler:空闲检测,心跳超时断开连接LoggingHandler:打印报文日志调试
🔥坑:Handler 如果不是
@Sharable,不能多个Channel共用同一个实例。
4. ByteBuf
Netty 自己实现的字节缓冲区,替代 NIO ByteBuffer。
- 读写指针分离:
readerIndex、writerIndex,不用flip() - 堆缓冲区、直接缓冲区(堆外内存,零拷贝,IO性能高)
- 自动扩容;支持切片;注意释放内存:referenceCount计数,release()
内存泄漏:开启
-Dio.netty.leakDetection.level=PARANOID调试泄漏。
5. ChannelFuture & Promise
Netty 全部IO操作都是异步,所有方法返回ChannelFuture。
sync()/await():阻塞等待完成(不建议业务线程频繁用)addListener():回调方式,推荐
Promise:可手动设置成功/失败的Future。
二、简单服务端模板
// 1. 线程组 EventLoopGroup bossGroup = new NioEventLoopGroup(1); EventLoopGroup workerGroup = new NioEventLoopGroup(); try { ServerBootstrap b = new ServerBootstrap(); b.group(bossGroup, workerGroup) .channel(NioServerSocketChannel.class) // TCP参数 .option(ChannelOption.SO_BACKLOG, 1024) .childOption(ChannelOption.SO_KEEPALIVE, true) .childHandler(new ChannelInitializer<SocketChannel>() { @Override protected void initChannel(SocketChannel ch) { ChannelPipeline p = ch.pipeline(); // 先编解码,再业务handler p.addLast(new LengthFieldBasedFrameDecoder(1024*10,0,2,0,2)); p.addLast(new MyBusinessHandler()); } }); // 绑定端口,同步等待启动完成 ChannelFuture f = b.bind(8888).sync(); // 等待服务端socket关闭 f.channel().closeFuture().sync(); } finally { bossGroup.shutdownGracefully(); workerGroup.shutdownGracefully(); }三、关键问题:粘包 & 拆包
TCP是流协议,没有报文边界。 解决方案 Netty内置解码器:
FixedLengthFrameDecoder:固定长度报文DelimiterBasedFrameDecoder:分隔符分割(如换行)LengthFieldBasedFrameDecoder:长度域,绝大多数二进制协议(JT/T808)用这个。参数理解重点:- maxFrameLength:最大报文
- lengthFieldOffset:长度字段偏移
- lengthFieldLength:长度字段占几个字节
- lengthAdjustment:长度值之外还要加多少字节
- initialBytesToStrip:解码后跳过多少字节
四、线程模型重点(高频面试)
- Boss线程:只做accept,建立连接,把SocketChannel注册到worker的Selector。
- Worker(NioEventLoop):IO读写 + 执行pipeline里handler。
❗严禁在Handler中做耗时操作:数据库查询、http调用、复杂计算。会阻塞EventLoop线程,整个线程上所有连接全部卡死。 ✅解决:自定义业务线程池
p.addLast(businessExecutor, new MyHandler()),把业务扔到业务线程池执行。
五、零拷贝
Netty零拷贝体现:
DirectBuffer使用堆外内存,JVM堆与内核之间少一次拷贝。FileRegion文件传输,sendfile系统调用。CompositeByteBuf组合多个缓冲区,不需要复制内容。
六、常见坑
- 忘记释放ByteBuf→ 堆外内存泄漏。入站:Netty自动释放;出站自己分配的ByteBuf要release。
- Handler非Sharable被多Channel共享 → 并发异常。
- EventLoop线程阻塞 → 服务吞吐量暴跌。
- LengthFieldBasedFrameDecoder参数配错,报文解析错乱(车联网协议高频踩坑)。
shutdownGracefully()必须调用,否则线程不会退出。- SO_KEEPALIVE 是TCP层心跳;应用层建议配合
IdleStateHandler做业务心跳,检测僵死连接。
七、Netty适用场景
- 物联网终端服务:JT/T808、1078、GB28181设备接入
- RPC框架底层:Dubbo底层就是Netty
- IM即时通讯、网关、代理服务器
- 私有二进制TCP协议服务
如果你需要,我可以继续输出:
- Netty面试精简背诵版
- LengthFieldBasedFrameDecoder参数详解示例(适配JT/T808)
- Netty客户端完整示例
- Netty内存泄漏排查要点
- Netty处理粘包拆包实战代码