Netty的Buffer为什么好用

典型回答 在网络编程中,基本都是基于TCP报文的字节流的操作,所以Java的NIO又新增了ByteBuffer,只不过Java原生的ByteBuffer,非常难操作,也不能扩缩容,所以Netty又重新封装了自己的Bytebuf,除了性能上的优势之外,Netty的Buffer在使用上相对于NIO也非常简洁,有如下特点: 动态扩缩容 顾名思义,Netty中的ByteBuffer可以像Java中的ArrayList一样,根据写入数据的字节数量,自动扩容。代码如下所示: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 final void ensureWritable0(int minWritableBytes) { final int writerIndex = writerIndex(); final int targetCapacity = writerIndex + minWritableBytes; // using non-short-circuit & to reduce branching - this is a hot path and targetCapacity should rarely overflow if (targetCapacity >= 0 & targetCapacity <= capacity()) { ensureAccessible(); return; } if (checkBounds && (targetCapacity < 0 || targetCapacity > maxCapacity)) { ensureAccessible(); throw new IndexOutOfBoundsException(String.format( "writerIndex(%d) + minWritableBytes(%d) exceeds maxCapacity(%d): %s", writerIndex, minWritableBytes, maxCapacity, this)); } // Normalize the target capacity to the power of 2. final int fastWritable = maxFastWritableBytes(); int newCapacity = fastWritable >= minWritableBytes ? writerIndex + fastWritable : alloc().calculateNewCapacity(targetCapacity, maxCapacity); // Adjust to the new capacity. 【此处进行扩容】 capacity(newCapacity); } 这个在编写代码的时候,满足ByteBuf最大缓冲区的情况下,我们可以毫无顾忌地调用#write方法增加字节,而不用手动去check容量满足,然后去重新申请 ...

March 22, 2026 · 1 min · santu

Netty的线程模型是怎么样的?

典型回答 Netty 通过 Reactor 模型基于多路复用器接收并处理用户请求的。多路复用IO模型参考: 操作系统的IO模型有哪些? 多路复用就是首先去阻塞的调用系统,询问内核数据是否准备好,如果准备好,再重新进行系统调用,进行数据拷贝。常见的实现有select,epoll和poll三种。 Netty 的线程模型并不是一成不变的,它实际取决于用户的启动参数配置。通过设置不同的启动参数,Netty支持三种模型,分别是**Reactor单线程模型、Reactor多线程模型、Reactor主从多线程模型。** 扩展知识 单Reactor单线程模型 这是最简单的Reactor模型,当有多个客户端连接到服务器的时候,服务器会先通过线程A和客户端建立连接, 有连接请求后,线程A会将不同的事件(如连接事件,读事件,写事件)进行分发,譬如有IO读写事件之后,会把该事件交给具体的Handler进行处理。 而线程A,就是我们说的Reactor模型中的Reactor,Reactor内部有一个dispatch(分发器)。【注意,这里的Reactor单线程,主要负责事件的监听和分发】 此时一个Reactor既负责处理连接请求,又要负责处理读写请求,一般来说处理连接请求是很快的,但是处理具体的读写请求就要涉及字节的复制,相对慢太多了。Reactor正在处理读写请求的时候,其他请求只能等着,只有等处理完了,才可以处理下一个请求。 通过一个Reactor线程,只能对应一个CPU,发挥不出来多核CPU的优势。所以,一个Reactor线程处理简单的小容量场景,还是OK的,但是对于高负载来说,还需要进一步升级。 单Reactor多线程模型 为了利用多核CPU的优势,也为了防止在Reactor线程等待读写事件时候浪费CPU,所以可以增加一个Worker的线程池,由此升级为单Reactor多线程模式。 整体流程如下: 当多个客户端进入服务器后,Reactor线程会监听多种事件(如连接事件,读事件,写事件),如果监听到连接事件,则把该事件分配给acceptor处理,如果监听到读事件,那么则会发起系统调用,将数据写入内存,之后再把数据交给工作线程池进行业务处理。 这个时候我们会发现,业务处理的逻辑已经变成多线程处理了。不过一个Reactor既要负责连接事件,又要负责读写事件,同时还要负责数据准备的过程。因为拷贝数据是阻塞的,假如说Reactor阻塞到拷贝数据的时候,服务器进来了很多连接,这个时候,这些连接是很有可能会被服务器拒绝掉的。 所以,单个Reactor看来是不够的,我们需要使用多个Reactor来处理。 主从Reactor模型 在主从Reactor模型中,主Reactor线程只负责连接事件的处理,它把读写事件全部交给了子Reactor线程。这样即使在数据准备阶段子线程被阻塞,主Reactor还是可以处理连接事件。巧妙的解决了高负载下的连接问题。

March 22, 2026 · 1 min · santu

Netty的零拷贝是怎么实现的?

典型回答 在操作系统中,零拷贝指的是避免在用户态(User-space)与内核态(Kernel-space)之间来回拷贝数据。 什么是零拷贝? 而Netty的零拷贝模型和操作系统中的零拷贝模型并不完全一样。他主要指的是在操作数据时, 不需要将数据 buffer从 一个内存区域拷贝到另一个内存区域。少了一次内存的拷贝,CPU 效率就得到的提升。 Netty的零拷贝主要体现在以下 5 个方面: 直接使用堆外内存,避免 JVM 堆内存到堆外内存的数据拷贝。 CompositeByteBuf 类,可以组合多个 Buffer 对象合并成一个逻辑上的对象,避免通过传统内存拷贝的方式将几个 Buffer 合并成一个大的 Buffer。 通过** Unpooled.wrappedBuffer** 可以将 byte 数组包装成 ByteBuf 对象,包装过程中不会产生内存拷贝。 ByteBuf.slice 操作与 Unpooled.wrappedBuffer 相反,slice 操作可以将一个 ByteBuf 对象切分成多个 ByteBuf 对象,切分过程中不会产生内存拷贝,底层共享一个 byte 数组的存储空间。 使用 FileRegion 实现文件传输,FileRegion 底层封装了 FileChannel#transferTo() 方法,可以将文件缓冲区的数据直接传输到目标 Channel,避免内核缓冲区和用户态缓冲区之间的数据拷贝,这属于操作系统级别的零拷贝。 扩展知识 堆外内存 ✅什么是堆外内存?如何使用堆外内存? 我们都知道。Java在将数据发送出去的时候,会先将数据从堆内存拷贝到堆外内存,然后才会将堆外内存再拷贝到内核态,进行消息的收发,代码如下: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 static int write(FileDescriptor paramFileDescriptor, ByteBuffer paramByteBuffer, long paramLong, NativeDispatcher paramNativeDispatcher) throws IOException{ // 如果是直接内存,则直接写入 if((paramByteBuffer instanceof DirectBuffer)) { return writeFromNativeBuffer(paramFileDescriptor, paramByteBuffer, paramLong, paramNativeDispatcher); } // ...否则,先把数据拷贝到直接内存中 ByteBuffer localByteBuffer = Util.getTemporaryDirectBuffer(k); try { localByteBuffer.put(paramByteBuffer); localByteBuffer.filp(); paramByteBuffer.position(i); int m = writeFromNativeBuffer(paramFileDescriptor, localByteBuffer, paramLong, paramNativeDispatcher); } } 所以,我们发现,假如我们在收发报文的时候使用直接内存,那么就可以减少一次内存拷贝,Netty就是这么做的。 ...

March 22, 2026 · 2 min · santu

Netty性能好的原因是什么?

典型回答 Netty作为一个高性能的网络通信框架,性能是他重要优势,Netty中主要做了以下事情来全方面的提升Netty的性能: 非阻塞IO模型:Netty采用了IO多路复用技术,让多个IO的阻塞复用到一个select线程阻塞上,能够有效的应对大量的并发请求 高效的Reactor线程模型:支持多种Reactor线程模型,可以根据业务场景的性能诉求,自行选择 零拷贝:尽量避免不必要的内存拷贝 内存池设计:使用直接内存,并且可重复利用 无锁串行化设计:避免使用锁带来的额外开销 高性能序列化协议:支持 protobuf 等高性能序列化协议

March 22, 2026 · 1 min · santu

为什么Netty适合做网络编程?

典型回答 Netty 是由 JBOSS 提供的一个 Java 开源框架。Netty 提供异步的、基于事件驱动的网络应用程序框架,用以快速开发高性能、高可靠性的网络 IO 程序。Netty 主要用来做网络通信,一般可以用来作RPC框架的通信工具、实现即时通讯系统以及实时消息推送系统等。 相比于Java中自带的NIO来说,Netty有很多好处,比如开箱即用,非常方便;性能高,能承载大量并发;功能更加强大并且社区也比较活跃。 使用简单:封装了 Java 原生 NIO 类库繁琐的 API,使用起来更加高效; 功能强大:预置多种编码能力,支持多种主流协议。同时通过 ChannelHandler 可以进行灵活的拓展,支持很强的定制能力; 高性能:与其它业界主流 NIO 框架相比,Netty 综合更优。主要体现在吞吐量更高、延迟更低、减少资源消耗以及最小化不必要的内存复制; 社区活跃:版本更新周期短,BUG 修复速度快,让开发者可以专注业务本身。

March 22, 2026 · 1 min · santu

Netty如何解决TCP粘包、拆包的问题的?

典型回答 什么是TCP的粘包、拆包问题? 针对TCP的粘包、拆包问题,Netty有自己的解决方式。 Netty通过预先指定的数据流编解码器,按照预先约定好的规则进行数据的解析,即可解决对应的粘包、拆包问题。 具体到代码层面,主要有以下几种解码器: 定长解码器 FixedLengthFrameDecoder 这个解码器按照指定的长度对接收到的数据进行分割。如果接收到的数据长度小于指定长度,它会等待直到收集到足够的数据。 行分隔符解码器 LineBasedFrameDecoder 它根据行分隔符(如 \n 或 \r\n)来处理粘包和拆包问题。这个解码器会按行分隔数据。 分隔符解码器 DelimiterBasedFrameDecoder 这个解码器允许指定一个或多个分隔符。当遇到分隔符时,它将数据分割成一个个完整的帧。 长度字段解码器 LengthFieldBasedFrameDecoder 这是一种更复杂的解决方案,适用于更复杂的协议。在这种方式中,消息中包含表示消息长度的字段。解码器根据这个长度字段来确定每个消息的边界。 自定义解码器 如果预定义的解码器不满足需求,Netty 还允许开发者实现自定义的解码器。通过扩展 ByteToMessageDecoder** **类,可以处理任何复杂的粘包和拆包场景。 除了解码器之外,Netty 也提供了编码器,用于在发送数据前按照特定规则格式化消息。例如,对于 LengthFieldBasedFrameDecoder,可以使用 LengthFieldPrepender 编码器来在消息前添加长度字段。 这些解决方案全被封装到了handler中,我们可以基于Netty的责任链模式,进行如下调用即可: 1 2 3 4 5 6 7 8 9 10 serverBootstrap.group(bossGroup, workerGroup) // channel fact .channel(NioServerSocketChannel.class) .childHandler(new ChannelInitializer<SocketChannel>() { @Override public void initChannel(SocketChannel ch) { ch.pipeline.addLast(new FixedLenghtFrameDecoder()); } } );

March 22, 2026 · 1 min · santu

能不能说一说Netty的无锁化设计?

典型问题 在解决多线程的问题的时候,锁是最常用的方案,但是也是开销最大的一种方案,同时也会带来死锁的问题,所以,Netty为了避免这些问题引入了无锁化设计。 那么,不用锁的话,怎么解决并发的问题呢,Netty主要做了以下几个事情: 首先,Netty基于Reactor线程模式实现并发请求处理,避免了线程阻塞与锁的竞争。 ✅Netty的线程模型是怎么样的? 其次,Netty实现了对象池,用来减少对象的创建和销毁,从而也能避免了锁的竞争。 ✅说说 Netty 的对象池技术? 而且在Netty中,还有许多组件都被设计为线程安全的,例如,每个Channel都有一个唯一的EventLoop,用于处理所有事件。这样可以避免锁竞争和线程切换带来的开销。

March 22, 2026 · 1 min · santu

Netty有哪些序列化协议?

典型回答 Netty支持很多种序列化协议,基本上市面上常见的序列化协议他都支持的。如: Java原生序列化:Java原生的序列化协议,可以序列化所有实现了Serializable接口的对象。Java序列化虽然简单易用,但是序列化后的字节数较大,序列化性能较差,且不具备跨语言的能力,因此不太常用。 JSON序列化:JSON是一种轻量级的数据交换格式,易于阅读和编写,同时也具备跨语言的能力,因此在分布式系统中广泛使用。Netty内置了多种JSON序列化库,包括Jackson、FastJSON等。 XML序列化:XML也是一种常用的数据交换格式,可以用于跨语言的数据交换。Netty内置了多种XML序列化库,包括JAXB、XStream等。 Protobuf序列化:Protobuf是Google开源的一种高效、灵活的二进制序列化协议,具有良好的跨语言能力和高效的序列化性能,被广泛应用于分布式系统中。Netty内置了对Protobuf的支持。 Thrift序列化:Thrift也是一种由Apache开源的二进制序列化协议,具有跨语言、高效等特点,被广泛应用于分布式系统中。Netty也提供了对Thrift的支持。

March 22, 2026 · 1 min · santu

说说 Netty 的对象池技术?

典型回答 Netty 内置了对象池,用于重复利用一些已经创建过的对象,避免频繁地创建和销毁对象,从而提升系统的性能和可靠性。 对象池是一种非常常见的设计模式,它在多线程的环境中特别有用,能够有效地减少线程的上下文切换和资源的浪费,同时也有利于避免内存泄漏等问题。Java中的字符串池,其实也就是一种对象池技术 当我们使用Netty编写一个网络应用程序时,可能需要频繁地创建和释放ByteBuf对象来处理输入和输出数据。如果我们每次需要时都创建新的ByteBuf对象,会导致频繁的垃圾回收和内存分配,降低性能。为了避免这种情况,Netty提供了对象池技术,通过对象池来重用ByteBuf对象,从而减少垃圾回收和内存分配。 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 // 创建对象池 ObjectPool<ByteBuf> pool = new DefaultObjectPool<>(new ByteBufAllocator() { // 创建ByteBuf对象 @Override public ByteBuf buffer(int initialCapacity, int maxCapacity) { return Unpooled.buffer(initialCapacity, maxCapacity); } }); // 从对象池中获取ByteBuf对象 ByteBuf buf = pool.borrowObject(); // 使用ByteBuf对象处理数据 // 将ByteBuf对象归还到对象池中 pool.returnObject(buf); 我们使用DefaultObjectPool类创建了一个对象池,并通过实现ByteBufAllocator接口的方式来指定如何创建ByteBuf对象。 ...

March 22, 2026 · 1 min · santu

Netty 中用了哪些设计模式?

典型回答 Netty作为一个使用广泛的网络通信框架,他的代码也是值得学习的,在Netty中也用到了很多种设计模式。比如单例模式、工厂模式、责任链模式、观察者模式、策略模式、装饰者模式等。 扩展知识 单例模式 NioEventLoop 通过核心方法 select() 不断轮询注册的 I/O 事件,Netty 提供了选择策略 SelectStrategy 对象,这个对象就是个单例: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 final class DefaultSelectStrategy implements SelectStrategy { static final SelectStrategy INSTANCE = new DefaultSelectStrategy(); private DefaultSelectStrategy() { } @Override public int calculateStrategy(IntSupplier selectSupplier, boolean hasTasks) throws Exception { return hasTasks ? selectSupplier.get() : SelectStrategy.SELECT; } } 还有其中定义的异常: ...

March 22, 2026 · 2 min · santu

留言给博主