Netty 初学者学习笔记

608 阅读3分钟

Netty 初学者学习笔记 🚀

Netty 是一个 高性能 Java NIO 网络框架,用于构建 高并发、低延迟的 TCP/UDP/HTTP 服务器和客户端,广泛应用于 RPC(如Dubbo)、游戏服务器、实时通信(WebSocket)、大数据传输(如Kafka) 等领域。


📌 1. Netty 核心概念

🔹 1.1 Netty vs Java NIO

对比项Java NIONetty
API易用性复杂(Selector、Channel、Buffer)封装简化,提供更易用的Handler机制
性能优化需手动管理内存、事件驱动优化零拷贝、事件循环、内存池优化
协议支持需额外实现HTTP/WebSocket等内置多种协议支持
生态基础API完善的社区支持(Dubbo、RocketMQ)

🔹 1.2 核心组件

组件作用
EventLoop事件循环,处理 I/O 事件(连接、读写)
Channel网络连接(如 NioSocketChannel
ChannelHandler处理业务逻辑(编解码、逻辑处理)
Pipeline一个pipeline管理多个 ChannelHandler
ByteBufNetty 的高性能字节缓存(替代 ByteBuffer

为了更好地理解,下面找了一张图,红色圆圈的Socket包含组件Channel,负责客户端与服务端的通信

image.png


📌 2. 快速搭建一个 Netty 服务端

🔹 2.1 依赖引入(Maven)

<dependency>
    <groupId>io.netty</groupId>
    <artifactId>netty-all</artifactId>
    <version>4.1.86.Final</version>
</dependency>

🔹 2.2 Netty服务端代码

public class NettyServer {
    public static void main(String[] args) throws InterruptedException {
        EventLoopGroup bossGroup = new NioEventLoopGroup();
        EventLoopGroup workerGroup = new NioEventLoopGroup();

        try {
            ServerBootstrap bootstrap = new ServerBootstrap();
            bootstrap.group(bossGroup, workerGroup)
                     .channel(NioServerSocketChannel.class)
                     .childHandler(new ChannelInitializer<SocketChannel>() {
                         @Override
                         protected void initChannel(SocketChannel ch) {
                             // 1. 解决粘包拆包:消息头(4字节长度)+ 消息体
                             ch.pipeline().addLast(new LengthFieldBasedFrameDecoder(
                                     1024,       // max frame length
                                     0,         // length field offset (header starts at 0)
                                     4,         // length field size (int = 4 bytes)
                                     0,         // length adjustment
                                     4          // bytes to strip (after decoding)
                             ));
                             // 2. 自定义业务处理器
                             ch.pipeline().addLast(new ServerHandler());
                         }
                     });

            ChannelFuture future = bootstrap.bind(8080).sync();
            System.out.println("Netty 服务端启动,监听 8080 端口...");
            future.channel().closeFuture().sync();
        } finally {
            bossGroup.shutdownGracefully();
            workerGroup.shutdownGracefully();
        }
    }
}

🔹 2.3 自定义 Handler(处理消息)

public class ServerHandler extends ChannelInboundHandlerAdapter {
    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) {
        // 收到的数据(自动转为 ByteBuf)
        ByteBuf buf = (ByteBuf) msg;
        String received = buf.toString(CharsetUtil.UTF_8);
        System.out.println("收到客户端消息: " + received);

        // 回写相同数据
        ByteBuf response = Unpooled.copiedBuffer("Server: " + received, CharsetUtil.UTF_8);
        ctx.writeAndFlush(response);
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
        cause.printStackTrace();
        ctx.close();
    }
}

📌 3. 快速搭建一个 Netty 客户端

🔹 3.1 客户端代码

public class NettyClient {
    public static void main(String[] args) throws InterruptedException {
        EventLoopGroup group = new NioEventLoopGroup();

        try {
            Bootstrap bootstrap = new Bootstrap();
            bootstrap.group(group)
                     .channel(NioSocketChannel.class)
                     .handler(new ChannelInitializer<SocketChannel>() {
                         @Override
                         protected void initChannel(SocketChannel ch) {
                             // 1. 客户端编码:消息头(4字节长度)+ 消息体
                             ch.pipeline().addLast(new LengthFieldPrepender(4)); // 长度字段占4字节

                             // 2. 解码(与服务端一致)
                             ch.pipeline().addLast(new LengthFieldBasedFrameDecoder(
                                     1024, 0, 4, 0, 4
                             ));

                             // 3. 自定义业务处理器
                             ch.pipeline().addLast(new ClientHandler());
                         }
                     });

            ChannelFuture future = bootstrap.connect("127.0.0.1", 8080).sync();
            System.out.println("Netty 客户端连接成功...");
            
            // 模拟发送多条消息(测试粘包拆包)
            for (int i = 0; i < 5; i++) {
                String msg = "Message #" + i;
                ByteBuf buf = Unpooled.copiedBuffer(msg, CharsetUtil.UTF_8);
                future.channel().writeAndFlush(buffer);
                Thread.sleep(500);
            }

            future.channel().closeFuture().sync();
        } finally {
            group.shutdownGracefully();
        }
    }
}

🔹 3.2 客户端 Handler

public class ClientHandler extends ChannelInboundHandlerAdapter {
    @Override
    public void channelActive(ChannelHandlerContext ctx) {
        // 连接建立后发送消息
        ctx.writeAndFlush(Unpooled.copiedBuffer("Hello, Netty!", CharsetUtil.UTF_8));
    }

    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) {
        ByteBuf buf = (ByteBuf) msg;
        System.out.println("收到服务端回复: " + buffer.toString(CharsetUtil.UTF_8));
    }
}

📌 4. Netty 高级特性

🔹 4.1 编解码(如处理 HTTP 协议)

pipeline.addLast(new HttpServerCodec()); // HTTP 编解码
pipeline.addLast(new HttpObjectAggregator(65536)); // 聚合 HTTP 请求
pipeline.addLast(new MyHttpHandler()); // 自定义业务处理器

🔹 4.2 粘包/拆包解决

// 固定长度解析
pipeline.addLast(new FixedLengthFrameDecoder(10));
// 分隔符解析(如 \n)
pipeline.addLast(new DelimiterBasedFrameDecoder(1024, Unpooled.wrappedBuffer("\n".getBytes())));

🔹 4.3 心跳检测(避免连接闲置)

pipeline.addLast(new IdleStateHandler(5, 0, 0, TimeUnit.SECONDS)); // 5秒读空闲检测
pipeline.addLast(new MyHeartbeatHandler()); // 处理心跳

📌 5. 常见面试题

  1. Netty 的线程模型是什么?
    • Reactor 多线程模型(BossGroup + WorkerGroup)。
  2. ByteBuf 和 ByteBuffer 的区别?
    • ByteBuf 支持动态扩容、引用计数、零拷贝优化。
  3. 如何解决 TCP 粘包问题?
    • 使用 LengthFieldBasedFrameDecoder 或自定义协议(如 header + body)。

🌟 总结

  • Netty 通过 事件驱动 + 异步非阻塞 实现高性能。
  • 核心组件: EventLoopChannelPipelineByteBuf
  • 适用场景: 高并发服务器、RPC、实时通信(如WebSocket)。