netty
Netty
Netty提供异步的、事件驱动的网络应用程序框架和工具,用以快速开发高性能、高可靠性的网络服务器和客户端程序。
Netty 是一个基于NIO的客户、服务器端编程框架,使用Netty 可以确保你快速和简单的开发出一个网络应用,例如实现了某种协议的客户,服务端应用。Netty相当简化和流线化了网络应用的编程开发过程,例如,TCP和UDP的socket服务开发。

Pipeline是一个双向链表
Channel
它代表一个到实体( 如一个硬件设备、一个文件、一个网络套接字或者一个能够执行一个或者多个不同的I/O操作的程序组件) 的开放连接,如读操作和写操作
会为每个Channel分配一个EventHandler
在netty中,每个Channel中都有且仅有一个ChannelPipeline与之对应.其持有一个 ChannelHandler的实例链。在默认的情况下, ChannelHandler 会把对它的方法的调用转发给链中的下一个 ChannelHandler。

ChannelPipeline
- 一个 Channel 包含了一个 ChannelPipeline, 而 ChannelPipeline 中又维护了一个由 ChannelHandlerContext组成的双向链表, 并且每个 ChannelHandlerContext 中又关联着一个 ChannelHandler
ChannelHandlerContext
- ChannelHandlerContext 参数贯穿 ChannelPipeline,将信息传递给每个 ChannelHandler,是个合格的“通讯员”。
channelHandler
- ChannelPipeline中可以有多个handler,多个channel共享一个handler可能会有并发安全问题,需要加入@shared注解
ChannelOption
- Netty 在创建 Channel 实例后,一般都需要设置 ChannelOption 参数。
- 常见的设置
- ChannelOption.SO_BACKLOG
对应 TCP/IP 协议 listen 函数中的 backlog 参数, 用来初始化服务器可连接队列大小。 服
务端处理客户端连接请求是顺序处理的, 所以同一时间只能处理一个客户端连接。 多个客户
端来的时候, 服务端将不能处理的客户端连接请求放在队列中等待处理, backlog 参数指定
了队列的大小。 - ChannelOption.SO_KEEPALIVE
一直保持连接活动状态
- ChannelOption.SO_BACKLOG
ChannelFuture
- 可以通过
ChannelFuture接口的addListener()方法注册一个ChannelFutureListener,当操作执行成功或者失败时,监听就会自动触发返回结果。 - 还可以通过
ChannelFuture接口的sync()方法让异步的操作变成同步的
EventLoop 和 EventLoopGroup
- 在 Netty 中每个 Channel 都会被分配到一个 EventLoop。一个 EventLoop 可以服务于多个 Channel。
- 每个 EventLoop 会占用一个 Thread,同时这个 Thread 会处理 这个EventLoop 上面发生的所有 IO 操作和事件(Netty 4.0)。
- EventLoopGroup 是用来生成 EventLoop,EventLoopGroup 要做的就是创建一个新的 Channel,并且给它分配一个 EventLoop。默认EventLoopGroup 的线程数为cpu核心数*2
任务队列
- scheduleTaskQueue.schedule
- taskQueue.execute
Netty心跳检测机制
- 心跳机制的工作原理是: 在 client 与 server 之间在一定时间内没有数据交互时, 即处于 idle 状态时, 客户端或服务器就会发送一个特殊的数据包给对方, 当接收方收到这个数据报文后, 也立即发送一个特殊的数据报文, 回应发送方, 此即一个 PING-PONG 交互。所以, 当某一端收到心跳消息后, 就知道了对方仍然在线, 这就确保 TCP 连接的有效性.
- 通过 Netty 实现心跳机制的话,核心类是
IdleStateHandler。
ByteBuf
- Netty 提供了两种 ByteBufAllocator 的实现
- PooledByteBufAllocator,实现了 ByteBuf 的对象的池化,提高性能减少内存碎片。
- Unpooled-ByteBufAllocator,没有实现对象的池化,每次会生成新的对象实例。
Netty 的 Bootstrap
- Bootstarp 和 ServerBootstrap 被称为引导类,指对应用程序进行配置,并使他运行起来的过程。Netty处理引导的方式是使你的应用程序和网络层相隔离。
Bootstrap
- Bootstrap 是客户端的引导类,Bootstrap 在调用 bind()(连接UDP)和 connect()(连接TCP)方法时,会新创建一个 Channel,仅创建一个单独的、没有父 Channel 的 Channel 来实现所有的网络交换。
ServerBootstrap
- ServerBootstrap 是服务端的引导类,ServerBootstarp 在调用 bind() 方法时会创建一个 ServerChannel 来接受来自客户端的连接,并且该 ServerChannel 管理了多个子 Channel 用于同客户端之间的通信。
Netty拆包粘包
使用netty自带的解码器
LineBasedFrameDecoder: 发送端发送数据包的时候,每个数据包之间以换行符作为分隔,LineBasedFrameDecoder的工作原理是它依次遍历ByteBuf中的可读字节,判断是否有换行符,然后进行相应的截取。DelimiterBasedFrameDecoder: 可以自定义分隔符解码器,**LineBasedFrameDecoder** 实际上是一种特殊的DelimiterBasedFrameDecoder解码器。FixedLengthFrameDecoder: 固定长度解码器,它能够按照指定的长度对消息进行相应的拆包。- **
LengthFieldBasedFrameDecoder**:
自定义序列化编码解码器
- 通常情况下,我们使用 Protostuff、Hessian2、json 序列方式比较多
Netty的零拷贝
- Netty的
Zero-coyp完全是在用户态(Java 层面)的, 它的Zero-copy的更多的是偏向于优化数据操作这样的概念.
特点
- Netty 提供了
CompositeByteBuf类, 它可以将多个 ByteBuf 合并为一个逻辑上的 ByteBuf, 避免了各个 ByteBuf 之间的拷贝. - 通过 wrap 操作, 我们可以将 byte[] 数组、ByteBuf、ByteBuffer等包装成一个 Netty ByteBuf 对象, 进而避免了拷贝操作.
- ByteBuf 支持 slice 操作, 因此可以将 ByteBuf 分解为多个共享同一个存储区域的 ByteBuf, 避免了内存的拷贝.
- 通过
FileRegion包装的FileChannel.tranferTo实现文件传输, 可以直接将文件缓冲区的数据发送到目标Channel, 避免了传统通过循环 write 方式导致的内存拷贝问题.
通过 CompositeByteBuf 实现零拷贝
ByteBuf header = ... ByteBuf body = ... CompositeByteBuf compositeByteBuf = Unpooled.compositeBuffer(); //第一个true表表示当添加新的 ByteBuf 时, 自动递增 CompositeByteBuf 的 writeIndex。如果不添加 //true参数, 因此此时我们就不可能从 compositeByteBuf 中读取到数据。 compositeByteBuf.addComponents(true, header, body); //还可以使用这种方法 ByteBuf allByteBuf = Unpooled.wrappedBuffer(header, body);1
2
3
4
5
6
7+ 虽然看起来 CompositeByteBuf 是由两个 ByteBuf 组合而成的, 不过在 CompositeByteBuf 内部, 这两个 ByteBuf 都是单独存在的, CompositeByteBuf 只是逻辑上是一个整体.
#### 通过 wrap 操作实现零拷贝
+ 例如我们有一个 byte 数组, 我们希望将它转换为一个 ByteBuf 对象, 以便于后续的操作, 那么传统的做法是将此 byte 数组拷贝到 ByteBuf 中, 即:
```java
byte[] bytes = ...
ByteBuf byteBuf = Unpooled.buffer();
byteBuf.writeBytes(bytes);- 显然这样的方式也是有一个额外的拷贝操作的, 我们可以使用 Unpooled 的相关方法, 包装这个 byte 数组, 生成一个新的 ByteBuf 实例, 而不需要进行拷贝操作. 上面的代码可以改为:
1
2byte[] bytes = ...
ByteBuf byteBuf = Unpooled.wrappedBuffer(bytes); - 我们通过
Unpooled.wrappedBuffer方法来将 bytes 包装成为一个 UnpooledHeapByteBuf 对象, 而在包装的过程中, 是不会有拷贝操作的. 即最后我们生成的生成的 ByteBuf 对象是和 bytes 数组共用了同一个存储空间, 对 bytes 的修改也会反映到 ByteBuf 对象中。
通过 slice 操作实现零拷贝
- slice 操作和 wrap 操作刚好相反,
Unpooled.wrappedBuffer可以将多个 ByteBuf 合并为一个, 而 slice 操作可以将一个 ByteBuf切片为多个共享一个存储区域的 ByteBuf 对象. //等同于 buf.slice(buf.readerIndex(), buf.readableBytes()) 调用 public ByteBuf slice(); // public ByteBuf slice(int index, int length);1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17+ 用 `slice` 方法产生 header 和 body 的过程是没有拷贝操作的, header 和 body 对象在内部其实是共享了 byteBuf 存储空间的不同部分而已.
#### 通过 FileRegion 实现零拷贝
+ Netty 中使用 FileRegion 实现文件传输的零拷贝, 不过在底层 FileRegion 是依赖于 Java NIO `FileChannel.transfer` 的零拷贝功能.
+ 使用了 `FileChannel` 后, 我们就可以直接将源文件的内容直接拷贝(`transferTo`) 到目的文件中
+ ```java
public static void copyFileWithFileChannel(String srcFileName, String destFileName) throws Exception {
RandomAccessFile srcFile = new RandomAccessFile(srcFileName, "r");
FileChannel srcFileChannel = srcFile.getChannel();
RandomAccessFile destFile = new RandomAccessFile(destFileName, "rw");
FileChannel destFileChannel = destFile.getChannel();
long position = 0;
long count = srcFileChannel.size();
srcFileChannel.transferTo(position, count, destFileChannel);
}- 具体使用
@Override public void channelRead0(ChannelHandlerContext ctx, String msg) throws Exception { RandomAccessFile raf = null; long length = -1; try { // 1. 通过 RandomAccessFile 打开一个文件. raf = new RandomAccessFile(msg, "r"); length = raf.length(); } catch (Exception e) { ctx.writeAndFlush("ERR: " + e.getClass().getSimpleName() + ": " + e.getMessage() + '\n'); return; } finally { if (length < 0 && raf != null) { raf.close(); } } ctx.write("OK: " + raf.length() + '\n'); if (ctx.pipeline().get(SslHandler.class) == null) { // SSL not enabled - can use zero-copy file transfer. // 2. 调用 raf.getChannel() 获取一个 FileChannel. // 3. 将 FileChannel 封装成一个 DefaultFileRegion ctx.write(new DefaultFileRegion(raf.getChannel(), 0, length)); } else { // SSL enabled - cannot use zero-copy file transfer. ctx.write(new ChunkedFile(raf)); } ctx.writeAndFlush("\n"); }
Protobuf
- 轻便高效的结构化数据存储格式,可以用于结构化数据串行化,很适合做数据存储或rpc的数据交换格式
netty
https://x-leonidas.github.io/2025/10/26/11技术栈/netty/