Netty 基础介绍

2 阅读7分钟

Netty 基础介绍

Netty 是由 JBoss 开源的一款高性能、异步事件驱动的 NIO 框架,专门用于开发可扩展的网络应用程序(如服务器、客户端)。它基于 Java NIO 封装,解决了原生 NIO 编程复杂、易出错、断连重连/半包处理等痛点,是目前 Java 领域高性能网络编程的事实标准(如 Dubbo、Elasticsearch、RocketMQ 等中间件均基于 Netty 构建)。

一、Netty 核心定位与优势

1. 核心定义

Netty = 「Java NIO 增强版」 + 「异步事件驱动」 + 「高性能网络编程框架」

  • 本质:对 Java NIO 的封装和增强,屏蔽原生 NIO 的复杂性(如 Selector 空轮询、半包粘包、线程模型设计等);
  • 核心目标:让开发者以极简的代码实现高并发、高可靠的网络通信。

2. Netty 核心优势

特性说明
高性能基于 Reactor 线程模型,支持单线程/多线程 Reactor,吞吐量远超原生 NIO
易用性封装了 NIO 底层细节(如 Channel、Buffer 操作),API 简洁易懂
可靠性内置半包/粘包处理、断连重连、心跳检测等机制,解决网络编程常见问题
可扩展性基于责任链模式的 ChannelPipeline,可灵活扩展自定义处理器(Handler)
跨协议支持支持 TCP、UDP、HTTP、WebSocket 等多种协议,提供丰富的编解码器

二、Netty 核心架构流程图

1. Netty 核心工作流程(Reactor 主从线程模型)

Reactor 模型是 Netty 的核心,主流使用「主从 Reactor 模型」(多线程),以下是完整流程图:

flowchart TD
    subgraph 初始化阶段
        A["创建 BossGroup(主 Reactor 线程组)<br/>负责监听客户端连接"] --> B["创建 WorkerGroup(从 Reactor 线程组)<br/>负责处理读写事件"]
        B --> C["创建 ServerBootstrap(服务器启动器)"]
        C --> D["配置线程组、通道类型(NioServerSocketChannel)"]
        D --> E["配置 ChannelPipeline(责任链)<br/>添加编解码器、业务 Handler"]
        E --> F["绑定端口,启动服务器"]
    end
    
    subgraph 连接处理阶段
        F --> G["BossGroup 线程监听 ACCEPT 事件(新连接)"]
        G --> H["BossGroup 接收 SocketChannel 连接"]
        H --> I["将 SocketChannel 注册到 WorkerGroup 的某个线程"]
    end
    
    subgraph 读写事件处理阶段
        I --> J["Worker 线程监听 READ/WRITE 事件"]
        J --> K["事件触发后,通过 ChannelPipeline 传递事件"]
        K --> L["Pipeline 中的 Handler 依次处理事件<br/>(解码→业务处理→编码→回写)"]
        L --> M["处理完成后,返回结果给客户端"]
        M --> J
    end
    
    subgraph 资源释放阶段
        N["客户端断开连接"] --> O["Worker 线程取消 Channel 注册"]
        O --> P["关闭 Channel,释放资源"]
    end
    
    %% 样式优化
    classDef init fill:#e8f4f8,stroke:#3182ce,stroke-width:2px
    classDef conn fill:#fdf2f8,stroke:#9f7aea,stroke-width:2px
    classDef io fill:#f5f5f5,stroke:#48bb78,stroke-width:2px
    classDef release fill:#fef7fb,stroke:#e53e3e,stroke-width:2px
    
    class A,B,C,D,E,F init
    class G,H,I conn
    class J,K,L,M io
    class N,O,P release

2. 流程图核心解读

  • BossGroup:主线程组,通常只配置 1 个线程,专门负责监听客户端的连接请求(对应原生 NIO 的 ServerSocketChannel 监听 ACCEPT 事件);
  • WorkerGroup:工作线程组,可配置多个线程,每个线程负责处理一个或多个 SocketChannel 的读写事件;
  • ChannelPipeline:责任链模式,所有的 I/O 事件(连接、读、写)都会通过 Pipeline 中的 Handler 依次处理,开发者只需关注自定义业务 Handler;
  • Handler:分为 ChannelInboundHandler(处理入站事件,如读数据)和 ChannelOutboundHandler(处理出站事件,如写数据)。

三、Netty 核心组件说明

组件作用
EventLoopGroup事件循环组,包含多个 EventLoop,BossGroup/WorkerGroup 都属于此类
ServerBootstrap服务器启动器,用于配置 Netty 服务器的核心参数(线程组、通道类型等)
Channel通信通道,对应原生 NIO 的 Channel,封装了读写、连接等操作
ChannelPipeline责任链,管理所有 Handler,I/O 事件会在 Pipeline 中流转
ChannelHandler事件处理器,处理具体的业务逻辑(如解码、编码、业务处理)
ByteBufNetty 自定义的缓冲区,替代原生 NIO 的 ByteBuffer,性能更高、更易用
Codec编解码器,处理网络传输中的半包/粘包问题(如 LineBasedFrameDecoder

四、Netty 完整示例代码(TCP 服务器 + 客户端)

以下是可直接运行的 Netty 服务器和客户端示例,实现「客户端发送消息,服务器接收并回显」的功能,包含核心的编解码、业务处理逻辑。

1. 前置依赖(Maven)

首先在 pom.xml 中引入 Netty 依赖(推荐使用稳定版 4.1.x):

<dependencies>
    <!-- Netty 核心依赖 -->
    <dependency>
        <groupId>io.netty</groupId>
        <artifactId>netty-all</artifactId>
        <version>4.1.94.Final</version>
    </dependency>
</dependencies>

2. Netty 服务器代码

import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.codec.LineBasedFrameDecoder;
import io.netty.handler.codec.string.StringDecoder;
import io.netty.handler.codec.string.StringEncoder;

/**
 * Netty TCP 服务器示例
 * 功能:接收客户端消息,并回显「服务器已收到:xxx」
 */
public class NettyServer {
    // 监听端口
    private final int port;

    public NettyServer(int port) {
        this.port = port;
    }

    public void start() throws InterruptedException {
        // 1. 创建 BossGroup 和 WorkerGroup
        // BossGroup:处理客户端连接请求,默认线程数 = CPU 核心数 * 2
        EventLoopGroup bossGroup = new NioEventLoopGroup(1);
        // WorkerGroup:处理读写事件,默认线程数 = CPU 核心数 * 2
        EventLoopGroup workerGroup = new NioEventLoopGroup();

        try {
            // 2. 创建服务器启动器
            ServerBootstrap bootstrap = new ServerBootstrap();
            bootstrap.group(bossGroup, workerGroup) // 绑定线程组
                    .channel(NioServerSocketChannel.class) // 指定通道类型(NIO 模式)
                    .option(ChannelOption.SO_BACKLOG, 128) // 连接队列大小
                    .childOption(ChannelOption.SO_KEEPALIVE, true) // 开启心跳保活
                    .childHandler(new ChannelInitializer<SocketChannel>() {
                        // 3. 初始化 ChannelPipeline(责任链)
                        @Override
                        protected void initChannel(SocketChannel ch) throws Exception {
                            ChannelPipeline pipeline = ch.pipeline();
                            // 添加编解码器:解决半包/粘包问题
                            pipeline.addLast(new LineBasedFrameDecoder(1024)); // 按行分割消息
                            pipeline.addLast(new StringDecoder()); // 字节转字符串
                            pipeline.addLast(new StringEncoder()); // 字符串转字节
                            // 添加自定义业务处理器
                            pipeline.addLast(new NettyServerHandler());
                        }
                    });

            System.out.println("Netty 服务器已启动,监听端口:" + port);
            // 4. 绑定端口,同步等待启动完成
            ChannelFuture future = bootstrap.bind(port).sync();
            // 5. 监听通道关闭事件,同步等待服务器关闭
            future.channel().closeFuture().sync();
        } finally {
            // 6. 优雅关闭线程组
            bossGroup.shutdownGracefully();
            workerGroup.shutdownGracefully();
            System.out.println("Netty 服务器已关闭");
        }
    }

    public static void main(String[] args) throws InterruptedException {
        new NettyServer(8080).start();
    }

    /**
     * 自定义服务器业务处理器
     * SimpleChannelInboundHandler:自动释放缓冲区,简化开发
     */
    static class NettyServerHandler extends SimpleChannelInboundHandler<String> {
        // 当接收到客户端消息时触发
        @Override
        protected void channelRead0(ChannelHandlerContext ctx, String msg) throws Exception {
            System.out.println("收到客户端[" + ctx.channel().remoteAddress() + "]消息:" + msg);
            // 回显消息给客户端(异步写操作)
            ctx.writeAndFlush("服务器已收到:" + msg + "\n");
        }

        // 当客户端连接建立时触发
        @Override
        public void channelActive(ChannelHandlerContext ctx) throws Exception {
            System.out.println("客户端[" + ctx.channel().remoteAddress() + "]已连接");
            super.channelActive(ctx);
        }

        // 当客户端断开连接时触发
        @Override
        public void channelInactive(ChannelHandlerContext ctx) throws Exception {
            System.out.println("客户端[" + ctx.channel().remoteAddress() + "]已断开连接");
            super.channelInactive(ctx);
        }

        // 异常处理
        @Override
        public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
            cause.printStackTrace();
            ctx.close(); // 关闭通道
        }
    }
}

3. Netty 客户端代码

import io.netty.bootstrap.Bootstrap;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.codec.LineBasedFrameDecoder;
import io.netty.handler.codec.string.StringDecoder;
import io.netty.handler.codec.string.StringEncoder;

import java.util.Scanner;

/**
 * Netty TCP 客户端示例
 * 功能:向服务器发送消息,并接收服务器回显
 */
public class NettyClient {
    private final String host;
    private final int port;

    public NettyClient(String host, int port) {
        this.host = host;
        this.port = port;
    }

    public void start() throws InterruptedException {
        // 1. 创建客户端事件循环组(客户端只需一个线程组)
        EventLoopGroup group = new NioEventLoopGroup();

        try {
            // 2. 创建客户端启动器
            Bootstrap bootstrap = new Bootstrap();
            bootstrap.group(group) // 绑定线程组
                    .channel(NioSocketChannel.class) // 指定客户端通道类型
                    .option(ChannelOption.TCP_NODELAY, true) // 禁用 Nagle 算法,立即发送数据
                    .handler(new ChannelInitializer<SocketChannel>() {
                        // 3. 初始化 ChannelPipeline
                        @Override
                        protected void initChannel(SocketChannel ch) throws Exception {
                            ChannelPipeline pipeline = ch.pipeline();
                            // 与服务器保持一致的编解码器
                            pipeline.addLast(new LineBasedFrameDecoder(1024));
                            pipeline.addLast(new StringDecoder());
                            pipeline.addLast(new StringEncoder());
                            // 添加自定义客户端处理器
                            pipeline.addLast(new NettyClientHandler());
                        }
                    });

            // 4. 连接服务器
            ChannelFuture future = bootstrap.connect(host, port).sync();
            System.out.println("已连接到 Netty 服务器:" + host + ":" + port);

            // 5. 读取控制台输入,发送给服务器
            Channel channel = future.channel();
            Scanner scanner = new Scanner(System.in);
            System.out.println("请输入要发送的消息(输入 exit 退出):");
            while (scanner.hasNextLine()) {
                String msg = scanner.nextLine();
                if ("exit".equals(msg)) {
                    channel.close(); // 关闭通道
                    break;
                }
                // 发送消息(末尾加换行符,匹配服务器的 LineBasedFrameDecoder)
                channel.writeAndFlush(msg + "\n");
            }

            // 6. 等待通道关闭
            channel.closeFuture().sync();
        } finally {
            // 7. 优雅关闭线程组
            group.shutdownGracefully();
            System.out.println("Netty 客户端已关闭");
        }
    }

    public static void main(String[] args) throws InterruptedException {
        new NettyClient("localhost", 8080).start();
    }

    /**
     * 自定义客户端业务处理器
     */
    static class NettyClientHandler extends SimpleChannelInboundHandler<String> {
        // 接收服务器回显的消息
        @Override
        protected void channelRead0(ChannelHandlerContext ctx, String msg) throws Exception {
            System.out.println("收到服务器回显:" + msg);
        }

        // 异常处理
        @Override
        public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
            cause.printStackTrace();
            ctx.close();
        }
    }
}

4. 代码运行说明

  1. 先启动 NettyServer,控制台输出「Netty 服务器已启动,监听端口:8080」;
  2. 再启动 NettyClient,控制台输出「已连接到 Netty 服务器:localhost:8080」;
  3. 在客户端控制台输入任意消息(如「Hello Netty」),服务器会接收并回显,客户端能看到服务器的回复;
  4. 输入「exit」可关闭客户端,服务器会提示「客户端已断开连接」。

5. 核心代码解读

  • 编解码器LineBasedFrameDecoder 按换行符分割消息,解决了网络传输中的「半包/粘包」问题(原生 NIO 需要手动处理,Netty 内置解决);
  • Handler 设计SimpleChannelInboundHandler 自动释放 ByteBuf 内存,避免内存泄漏;
  • 异步操作writeAndFlush 是异步写操作,Netty 会通过 EventLoop 异步处理,不会阻塞主线程;
  • 优雅关闭shutdownGracefully() 会等待所有任务完成后再关闭线程组,避免资源泄漏。

五、Netty 典型应用场景

  1. 中间件通信:Dubbo、Spring Cloud Gateway 等微服务框架的底层通信;
  2. 消息队列:RocketMQ、Kafka 的网络通信模块;
  3. 大数据:Hadoop、Spark 的分布式通信;
  4. 即时通信:基于 WebSocket 的聊天系统、推送系统;
  5. 游戏服务器:高并发的游戏实时通信(如王者荣耀、英雄联盟的后台)。

总结

  1. Netty 核心定位:基于 Java NIO 的高性能网络编程框架,封装了原生 NIO 的复杂性,提供简洁的 API 和可靠的网络通信能力;
  2. 核心架构:采用主从 Reactor 线程模型,BossGroup 处理连接、WorkerGroup 处理读写,通过 ChannelPipeline 责任链处理业务逻辑;
  3. 核心优势:开箱即用的编解码器、半包/粘包处理、高并发支持,是 Java 高性能网络编程的首选框架,广泛应用于中间件、大数据、游戏等领域。