本文面向初学者,手把手教你掌握 Netty 的核心概念和实战应用。


一、什么是 Netty?

1.1 从一个问题开始

想象一下,你要开发一个聊天应用,需要同时处理成千上万用户的连接。

使用传统的 Socket 编程,你可能会遇到:

  • 性能瓶颈:阻塞 IO 效率低

  • 代码复杂:需要处理各种底层细节

  • 难以维护:代码耦合度高

  • Bug 频繁:容易出现各种问题

这时候,Netty 就派上用场了。

1.2 Netty 是什么?

Netty 是一个 高性能、异步事件驱动的网络框架

简单来说,Netty 就是一个"网络编程助手",它帮你处理了所有复杂的网络细节,让你只需要关注业务逻辑。

┌─────────────────────────────────────────────────────────────┐
│                        Netty 架构                            │
├─────────────────────────────────────────────────────────────┤
│  ┌───────────────────────────────────────────────────────┐  │
│  │                    你的业务代码                         │  │
│  └───────────────────────────────────────────────────────┘  │
│                          │                                  │
│                          ▼                                  │
│  ┌───────────────────────────────────────────────────────┐  │
│  │                    Netty 框架                          │  │
│  │  ┌─────────────┐  ┌─────────────┐  ┌─────────────┐   │  │
│  │  │  EventLoop  │  │   Channel   │  │   Handler   │   │  │
│  │  └─────────────┘  └─────────────┘  └─────────────┘   │  │
│  └───────────────────────────────────────────────────────┘  │
│                          │                                  │
│                          ▼                                  │
│  ┌───────────────────────────────────────────────────────┐  │
│  │                    操作系统                             │  │
│  │  ┌─────────────┐  ┌─────────────┐  ┌─────────────┐   │  │
│  │  │    Linux    │  │   Windows   │  │     Mac     │   │  │
│  │  └─────────────┘  └─────────────┘  └─────────────┘   │  │
│  └───────────────────────────────────────────────────────┘  │
└─────────────────────────────────────────────────────────────┘

1.3 核心特性

特性 说明 好处
高性能 零拷贝、内存池化 处理更多请求
易用性 API 简洁 学习成本低
可扩展 组件可插拔 灵活配置
稳定性 大量生产验证 可靠运行

1.4 Netty vs 原生 NIO

对比项 原生 NIO Netty
API 复杂度 复杂 简单
学习成本
代码量
性能 更高
稳定性 一般
社区支持 一般 活跃

简单记忆: Netty = NIO + 简化 + 优化 + 稳定


二、为什么使用 Netty?

2.1 传统网络编程的问题

问题1:代码复杂

// 传统 NIO 代码(复杂)
Selector selector = Selector.open();
ServerSocketChannel serverChannel = ServerSocketChannel.open();
serverChannel.configureBlocking(false);
serverChannel.register(selector, SelectionKey.OP_ACCEPT);
​
while (true) {
    selector.select();
    Set<SelectionKey> keys = selector.selectedKeys();
    for (SelectionKey key : keys) {
        if (key.isAcceptable()) {
            // 处理连接
        } else if (key.isReadable()) {
            // 处理读取
        }
    }
}

问题2:性能瓶颈

  • 阻塞 IO:一个线程只能处理一个连接

  • 线程开销:大量线程消耗系统资源

问题3:难以维护

  • 代码耦合度高

  • 难以扩展和修改

2.2 Netty 的优势

优势1:简化开发

// Netty 代码(简洁)
ServerBootstrap bootstrap = new ServerBootstrap();
bootstrap.group(bossGroup, workerGroup)
         .channel(NioServerSocketChannel.class)
         .childHandler(new ChannelInitializer<SocketChannel>() {
             @Override
             protected void initChannel(SocketChannel ch) {
                 ch.pipeline().addLast(new MyHandler());
             }
         });

优势2:高性能

  • 异步非阻塞:一个线程处理多个连接

  • 零拷贝:减少内存复制

  • 内存池化:减少 GC 压力

优势3:易于维护

  • 组件化设计

  • 代码解耦

  • 易于扩展

2.3 谁在使用 Netty?

公司/项目 说明
Dubbo 阿里 RPC 框架
Elasticsearch 搜索引擎
Hadoop 大数据框架
RocketMQ 消息队列
gRPC Google RPC 框架
Spring WebFlux 响应式 Web 框架

为什么这些项目选择 Netty?

  • 高性能:处理大量并发连接

  • 稳定性:经过生产环境验证

  • 易用性:开发效率高


三、Netty 核心组件

3.1 核心组件概览

组件 作用 类比
EventLoop 事件循环 工人(处理任务)
Channel 通道 管道(传输数据)
ChannelFuture 异步结果 快递单(查询状态)
ChannelHandler 处理器 加工员(处理数据)
ChannelPipeline 处理链 流水线(多个加工员)
ByteBuf 字节缓冲区 仓库(存放数据)

3.2 EventLoop(事件循环)

EventLoop 是 Netty 的核心,负责处理 IO 事件。

┌─────────────────────────────────────────────────────────────┐
│                      EventLoop                               │
├─────────────────────────────────────────────────────────────┤
│  ┌───────────────────────────────────────────────────────┐  │
│  │                    事件循环                             │  │
│  │  ┌─────────────┐  ┌─────────────┐  ┌─────────────┐   │  │
│  │  │  接收连接    │  │  读取数据    │  │  写入数据    │   │  │
│  │  └─────────────┘  └─────────────┘  └─────────────┘   │  │
│  └───────────────────────────────────────────────────────┘  │
└─────────────────────────────────────────────────────────────┘

特点:

  • 一个 EventLoop 可以绑定多个 Channel

  • 保证线程安全

  • 避免线程切换开销

3.3 Channel(通道)

Channel 代表一个网络连接。

// Channel 常用方法
channel.read()          // 读取数据
channel.write(msg)      // 写入数据
channel.writeAndFlush(msg)  // 写入并刷新
channel.close()         // 关闭连接
channel.isActive()      // 是否活跃
channel.remoteAddress() // 远程地址

3.4 ChannelHandler(处理器)

ChannelHandler 是处理数据的组件。

// 入站处理器:处理接收到的数据
public class MyInboundHandler extends ChannelInboundHandlerAdapter {
    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) {
        // 处理接收到的数据
        System.out.println("收到消息:" + msg);
        ctx.fireChannelRead(msg);  // 传递给下一个处理器
    }
}
​
// 出站处理器:处理发送的数据
public class MyOutboundHandler extends ChannelOutboundHandlerAdapter {
    @Override
    public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) {
        // 处理要发送的数据
        System.out.println("发送消息:" + msg);
        ctx.write(msg, promise);  // 传递给下一个处理器
    }
}

3.5 ChannelPipeline(处理链)

ChannelPipeline 是多个 Handler 组成的流水线。

┌─────────────────────────────────────────────────────────────┐
│                    ChannelPipeline                           │
├─────────────────────────────────────────────────────────────┤
│  入站方向 →                                                   │
│  ┌─────────────┐  ┌─────────────┐  ┌─────────────┐         │
│  │  Handler1    │  │  Handler2    │  │  Handler3    │         │
│  │  (入站)      │  │  (入站)      │  │  (入站)      │         │
│  └─────────────┘  └─────────────┘  └─────────────┘         │
│                                                             │
│  ← 出站方向                                                  │
│  ┌─────────────┐  ┌─────────────┐  ┌─────────────┐         │
│  │  Handler6    │  │  Handler5    │  │  Handler4    │         │
│  │  (出站)      │  │  (出站)      │  │  (出站)      │         │
│  └─────────────┘  └─────────────┘  └─────────────┘         │
└─────────────────────────────────────────────────────────────┘

执行顺序:

  • 入站:Handler1 → Handler2 → Handler3

  • 出站:Handler6 → Handler5 → Handler4

3.6 ByteBuf(字节缓冲区)

ByteBuf 是 Netty 的字节容器,比 Java NIO 的 ByteBuffer 更好用。

// 创建 ByteBuf
ByteBuf buf = Unpooled.buffer(256);
​
// 写入数据
buf.writeBytes("Hello Netty".getBytes());
​
// 读取数据
byte[] bytes = new byte[buf.readableBytes()];
buf.readBytes(bytes);
​
// 释放资源
buf.release();

ByteBuf 优势:

  • 读写分离:有独立的读写索引

  • 自动扩容:容量不足时自动扩展

  • 池化管理:减少 GC 压力


四、快速入门:第一个 Netty 程序

4.1 添加依赖

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

4.2 创建服务器

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.string.StringDecoder;
import io.netty.handler.codec.string.StringEncoder;

public class NettyServer {
    
    private final int port;
    
    public NettyServer(int port) {
        this.port = port;
    }
    
    public void start() throws Exception {
        // 创建两个线程组
        // bossGroup:负责接收连接
        // workerGroup:负责处理业务
        EventLoopGroup bossGroup = new NioEventLoopGroup(1);
        EventLoopGroup workerGroup = new NioEventLoopGroup();
        
        try {
            // 创建服务器启动类
            ServerBootstrap bootstrap = new ServerBootstrap();
            bootstrap.group(bossGroup, workerGroup)
                     .channel(NioServerSocketChannel.class)  // 指定通道类型
                     .option(ChannelOption.SO_BACKLOG, 128)   // 设置队列大小
                     .childOption(ChannelOption.SO_KEEPALIVE, true)  // 保持连接
                     .childHandler(new ChannelInitializer<SocketChannel>() {
                         @Override
                         protected void initChannel(SocketChannel ch) {
                             ChannelPipeline pipeline = ch.pipeline();
                             // 添加编解码器
                             pipeline.addLast(new StringDecoder());
                             pipeline.addLast(new StringEncoder());
                             // 添加业务处理器
                             pipeline.addLast(new SimpleChannelInboundHandler<String>() {
                                 @Override
                                 protected void channelRead0(ChannelHandlerContext ctx, String msg) {
                                     System.out.println("收到消息:" + msg);
                                     // 回复消息
                                     ctx.writeAndFlush("服务器已收到:" + msg);
                                 }
                             });
                         }
                     });
            
            // 绑定端口并启动
            ChannelFuture future = bootstrap.bind(port).sync();
            System.out.println("服务器启动成功,监听端口:" + port);
            
            // 等待服务器关闭
            future.channel().closeFuture().sync();
        } finally {
            // 优雅关闭
            bossGroup.shutdownGracefully();
            workerGroup.shutdownGracefully();
        }
    }
    
    public static void main(String[] args) throws Exception {
        new NettyServer(8080).start();
    }
}

4.3 创建客户端

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.string.StringDecoder;
import io.netty.handler.codec.string.StringEncoder;

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 Exception {
        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) {
                             ChannelPipeline pipeline = ch.pipeline();
                             pipeline.addLast(new StringDecoder());
                             pipeline.addLast(new StringEncoder());
                             pipeline.addLast(new SimpleChannelInboundHandler<String>() {
                                 @Override
                                 protected void channelRead0(ChannelHandlerContext ctx, String msg) {
                                     System.out.println("服务器回复:" + msg);
                                 }
                             });
                         }
                     });
            
            // 连接服务器
            ChannelFuture future = bootstrap.connect(host, port).sync();
            System.out.println("连接服务器成功");
            
            // 发送消息
            future.channel().writeAndFlush("Hello Netty!");
            
            // 等待关闭
            future.channel().closeFuture().sync();
        } finally {
            group.shutdownGracefully();
        }
    }
    
    public static void main(String[] args) throws Exception {
        new NettyClient("localhost", 8080).start();
    }
}

4.4 运行测试

步骤1:启动服务器

服务器启动成功,监听端口:8080

步骤2:启动客户端

连接服务器成功
服务器回复:服务器已收到:Hello Netty!

步骤3:服务器控制台

收到消息:Hello Netty!

五、实战案例

5.1 HTTP 服务器

import io.netty.bootstrap.ServerBootstrap;
import io.netty.buffer.Unpooled;
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.http.*;
import io.netty.util.CharsetUtil;

public class HttpServer {
    
    public static void main(String[] args) throws Exception {
        EventLoopGroup bossGroup = new NioEventLoopGroup(1);
        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) {
                             ch.pipeline()
                               .addLast(new HttpServerCodec())        // HTTP 编解码器
                               .addLast(new HttpObjectAggregator(65536))  // 聚合 HTTP 消息
                               .addLast(new SimpleChannelInboundHandler<FullHttpRequest>() {
                                   @Override
                                   protected void channelRead0(ChannelHandlerContext ctx, FullHttpRequest request) {
                                       // 获取请求路径
                                       String uri = request.uri();
                                       System.out.println("请求路径:" + uri);
                                       
                                       // 构建响应内容
                                       String content = "Hello, Netty HTTP Server!\n" +
                                                       "请求路径:" + uri + "\n" +
                                                       "请求方法:" + request.method();
                                       
                                       // 构建响应
                                       FullHttpResponse response = new DefaultFullHttpResponse(
                                           HttpVersion.HTTP_1_1,
                                           HttpResponseStatus.OK,
                                           Unpooled.copiedBuffer(content, CharsetUtil.UTF_8)
                                       );
                                       
                                       // 设置响应头
                                       response.headers().set(HttpHeaderNames.CONTENT_TYPE, "text/plain;charset=UTF-8");
                                       response.headers().set(HttpHeaderNames.CONTENT_LENGTH, content.length());
                                       
                                       // 发送响应
                                       ctx.writeAndFlush(response);
                                   }
                               });
                         }
                     });
            
            ChannelFuture future = bootstrap.bind(8080).sync();
            System.out.println("HTTP 服务器启动成功,访问 http://localhost:8080");
            future.channel().closeFuture().sync();
        } finally {
            bossGroup.shutdownGracefully();
            workerGroup.shutdownGracefully();
        }
    }
}

测试: 浏览器访问 http://localhost:8080

5.2 WebSocket 服务器

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.http.HttpObjectAggregator;
import io.netty.handler.codec.http.HttpServerCodec;
import io.netty.handler.codec.http.websocketx.TextWebSocketFrame;
import io.netty.handler.codec.http.websocketx.WebSocketServerProtocolHandler;

public class WebSocketServer {
    
    public static void main(String[] args) throws Exception {
        EventLoopGroup bossGroup = new NioEventLoopGroup(1);
        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) {
                             ch.pipeline()
                               .addLast(new HttpServerCodec())
                               .addLast(new HttpObjectAggregator(65536))
                               .addLast(new WebSocketServerProtocolHandler("/ws"))  // WebSocket 路径
                               .addLast(new SimpleChannelInboundHandler<TextWebSocketFrame>() {
                                   @Override
                                   protected void channelRead0(ChannelHandlerContext ctx, TextWebSocketFrame frame) {
                                       String text = frame.text();
                                       System.out.println("收到 WebSocket 消息:" + text);
                                       
                                       // 回复消息
                                       ctx.writeAndFlush(new TextWebSocketFrame("服务器回复:" + text));
                                   }
                               });
                         }
                     });
            
            ChannelFuture future = bootstrap.bind(8080).sync();
            System.out.println("WebSocket 服务器启动成功");
            future.channel().closeFuture().sync();
        } finally {
            bossGroup.shutdownGracefully();
            workerGroup.shutdownGracefully();
        }
    }
}

测试: 使用 WebSocket 客户端连接 ws://localhost:8080/ws


六、使用场景与最佳实践

6.1 常见使用场景

场景 说明 示例
RPC 框架 远程过程调用 Dubbo、gRPC
消息队列 消息中间件 RocketMQ
即时通讯 聊天应用 微信、QQ
游戏服务器 游戏后端 Minecraft
Web 服务器 HTTP 服务 Tomcat

6.2 最佳实践

1. 合理配置线程数

// bossGroup:1 个线程即可
EventLoopGroup bossGroup = new NioEventLoopGroup(1);

// workerGroup:CPU 核心数 * 2
EventLoopGroup workerGroup = new NioEventLoopGroup(
    Runtime.getRuntime().availableProcessors() * 2
);

2. 使用零拷贝

// 使用 FileRegion 传输文件
FileRegion region = new DefaultFileRegion(
    new File("file.txt"), 0, file.length()
);
ctx.writeAndFlush(region);

3. 内存池化

// 使用池化 ByteBuf
ByteBuf buf = ctx.alloc().buffer(256);
try {
    buf.writeBytes("Hello".getBytes());
    ctx.writeAndFlush(buf);
} finally {
    buf.release();  // 及时释放
}

4. 优雅关闭

// 优雅关闭,等待任务完成
bossGroup.shutdownGracefully();
workerGroup.shutdownGracefully();

6.3 常见问题

问题1:内存泄漏

// 错误示例:ByteBuf 未释放
ByteBuf buf = Unpooled.buffer(256);
ctx.writeAndFlush(buf);
// buf 未释放,导致内存泄漏

// 正确示例:使用 try-finally
ByteBuf buf = Unpooled.buffer(256);
try {
    ctx.writeAndFlush(buf);
} finally {
    buf.release();
}

问题2:线程安全

// 错误示例:多线程访问共享数据
public class MyHandler extends ChannelInboundHandlerAdapter {
    private List<String> messages = new ArrayList<>();  // 非线程安全
    
    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) {
        messages.add(msg.toString());  // 多线程访问,不安全
    }
}

// 正确示例:使用线程安全集合
public class MyHandler extends ChannelInboundHandlerAdapter {
    private List<String> messages = new CopyOnWriteArrayList<>();  // 线程安全
    
    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) {
        messages.add(msg.toString());  // 安全
    }
}

七、总结

核心知识点回顾

  1. Netty 是什么:高性能、异步事件驱动的网络框架

  2. 核心组件:EventLoop、Channel、Handler、Pipeline、ByteBuf

  3. 为什么使用:简化开发、高性能、易于维护

  4. 使用场景:RPC、消息队列、即时通讯、游戏服务器

学习建议

  1. 先理解概念:不要急于深入细节

  2. 动手实践:从简单示例开始

  3. 阅读源码:理解 Netty 的实现原理

  4. 关注面试题:巩固知识

推荐资源


恭喜! 你已经掌握了 Netty 的核心知识。接下来,尝试在你的项目中实践吧!

如果你觉得这篇文章有帮助,欢迎点赞、收藏、关注!


附录:常用 Netty 配置

// 服务器配置
ServerBootstrap bootstrap = new ServerBootstrap();
bootstrap.group(bossGroup, workerGroup)
         .channel(NioServerSocketChannel.class)
         .option(ChannelOption.SO_BACKLOG, 128)          // 连接队列大小
         .option(ChannelOption.SO_REUSEADDR, true)       // 地址复用
         .childOption(ChannelOption.SO_KEEPALIVE, true)  // 保持连接
         .childOption(ChannelOption.TCP_NODELAY, true)   // 禁用 Nagle 算法
         .childOption(ChannelOption.SO_RCVBUF, 1024 * 1024)  // 接收缓冲区
         .childOption(ChannelOption.SO_SNDBUF, 1024 * 1024); // 发送缓冲区

附录:Netty 常用 Handler

Handler 作用
StringDecoder 字符串解码器
StringEncoder 字符串编码器
HttpServerCodec HTTP 编解码器
HttpObjectAggregator HTTP 消息聚合器
WebSocketServerProtocolHandler WebSocket 协议处理器
IdleStateHandler 空闲检测处理器
LengthFieldBasedFrameDecoder 长度字段解码器
DelimiterBasedFrameDecoder 分隔符解码器
Logo

openEuler 是由开放原子开源基金会孵化的全场景开源操作系统项目,面向数字基础设施四大核心场景(服务器、云计算、边缘计算、嵌入式),全面支持 ARM、x86、RISC-V、loongArch、PowerPC、SW-64 等多样性计算架构

更多推荐