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。

  • 读写指针分离:readerIndexwriterIndex,不用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内置解码器:

  1. FixedLengthFrameDecoder:固定长度报文
  2. DelimiterBasedFrameDecoder:分隔符分割(如换行)
  3. LengthFieldBasedFrameDecoder:长度域,绝大多数二进制协议(JT/T808)用这个。参数理解重点:
    • maxFrameLength:最大报文
    • lengthFieldOffset:长度字段偏移
    • lengthFieldLength:长度字段占几个字节
    • lengthAdjustment:长度值之外还要加多少字节
    • initialBytesToStrip:解码后跳过多少字节

四、线程模型重点(高频面试)

  1. Boss线程:只做accept,建立连接,把SocketChannel注册到worker的Selector。
  2. Worker(NioEventLoop):IO读写 + 执行pipeline里handler。

    严禁在Handler中做耗时操作:数据库查询、http调用、复杂计算。会阻塞EventLoop线程,整个线程上所有连接全部卡死。 ✅解决:自定义业务线程池 p.addLast(businessExecutor, new MyHandler()),把业务扔到业务线程池执行。

五、零拷贝

Netty零拷贝体现:

  1. DirectBuffer 使用堆外内存,JVM堆与内核之间少一次拷贝。
  2. FileRegion 文件传输,sendfile系统调用。
  3. CompositeByteBuf 组合多个缓冲区,不需要复制内容。

六、常见坑

  1. 忘记释放ByteBuf → 堆外内存泄漏。入站:Netty自动释放;出站自己分配的ByteBuf要release。
  2. Handler非Sharable被多Channel共享 → 并发异常。
  3. EventLoop线程阻塞 → 服务吞吐量暴跌。
  4. LengthFieldBasedFrameDecoder参数配错,报文解析错乱(车联网协议高频踩坑)。
  5. shutdownGracefully() 必须调用,否则线程不会退出。
  6. SO_KEEPALIVE 是TCP层心跳;应用层建议配合 IdleStateHandler 做业务心跳,检测僵死连接。

七、Netty适用场景

  • 物联网终端服务:JT/T808、1078、GB28181设备接入
  • RPC框架底层:Dubbo底层就是Netty
  • IM即时通讯、网关、代理服务器
  • 私有二进制TCP协议服务

如果你需要,我可以继续输出:

  1. Netty面试精简背诵版
  2. LengthFieldBasedFrameDecoder参数详解示例(适配JT/T808)
  3. Netty客户端完整示例
  4. Netty内存泄漏排查要点
  5. Netty处理粘包拆包实战代码

更多推荐