深入理解Netty(持续更新)
NettyNetty 是基于 Java NIO 的高性能、异步事件驱动网络通信框架封装了原生 NIO 复杂 API用于快速开发 TCP/UDP 网络服务、RPC、网关、IM、物联网协议服务JT/T808、GB28181。底层Java NIOSelector、Channel、Buffer也支持 NIO.2(AIO)主流版本 Netty4.x生产基本不用 Netty3。一、核心组件1. EventLoop / EventLoopGroupEventLoop单线程无限循环做三件事IO事件处理、任务队列执行、定时任务。一个线程绑定一个 Selector。EventLoopGroup一组 EventLoop。NioEventLoopGroup主从线程模型bossGroup接收客户端连接accept线程数一般设 1。workerGroup处理已建立连接的 IO读写、编解码、业务逻辑。默认线程数 CPU核心数*2。重要Channel 一旦注册到某个 EventLoop整个生命周期都绑定这一个线程避免多线程竞争。业务耗时操作不要阻塞 EventLoop 线程耗时业务丢到自定义业务线程池。2. ChannelNetty 抽象网络连接对应一条 Socket。常用NioSocketChannelTCP客户端NioServerSocketChannelTCP服务端NioDatagramChannelUDP生命周期registered → active → read → inactive → unregistered3. ChannelHandler ChannelPipelinePipeline 是 Handler 的双向链表事件在链上传播。ChannelInboundHandler入站事件接收数据、连接激活从head往tail走ChannelOutboundHandler出站事件发送数据、关闭连接从tail往head走常用HandlerByteToMessageDecoder字节 → Java对象解码MessageToByteEncoderJava对象 → 字节编码LengthFieldBasedFrameDecoder解决粘包拆包最常用基于长度字段分包JT/T808、自定义二进制协议必备ProtobufDecoder / ProtobufEncoderProtobuf序列化IdleStateHandler空闲检测心跳超时断开连接LoggingHandler打印报文日志调试坑Handler 如果不是Sharable不能多个Channel共用同一个实例。4. ByteBufNetty 自己实现的字节缓冲区替代 NIO ByteBuffer。读写指针分离readerIndex、writerIndex不用flip()堆缓冲区、直接缓冲区堆外内存零拷贝IO性能高自动扩容支持切片注意释放内存referenceCount计数release()内存泄漏开启-Dio.netty.leakDetection.levelPARANOID调试泄漏。5. ChannelFuture PromiseNetty 全部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 ChannelInitializerSocketChannel() { 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底层就是NettyIM即时通讯、网关、代理服务器私有二进制TCP协议服务如果你需要我可以继续输出Netty面试精简背诵版LengthFieldBasedFrameDecoder参数详解示例适配JT/T808Netty客户端完整示例Netty内存泄漏排查要点Netty处理粘包拆包实战代码