Netty相关问题

2 阅读14分钟

1、什么是Netty?

Netty是一个高性能的网络编程框架,基于NIO的非阻塞式IO模型,可以帮助开发者快速开发高性能、高可靠性的网络应用程序。 

2、Netty的核心组件有哪些? 

Netty的核心组件包括:Channel、EventLoop、ChannelFuture、ChannelHandler、ChannelPipeline等。 

3、什么是Channel? 

Channel是Netty中的一个核心组件,代表一个可以进行读写操作的通道,可以用于网络数据的读写、文件的读写等操作。

4、什么是EventLoop?

EventLoop是Netty中的另一个核心组件,它负责处理所有的事件,包括读事件、写事件、连接事件、断开连接事件等。

5、什么是ChannelFuture?

ChannelFuture是Netty中的一个异步操作类,用于获取异步操作的结果,例如:写操作、连接操作等。

6、什么是ChannelHandler?

ChannelHandler是Netty中的一个组件,用于处理各种事件,包括读事件、写事件、连接事件、断开连接事件等。

7、什么是ChannelPipeline?

ChannelPipeline是Netty中的一个核心组件,它负责管理各种ChannelHandler,用于处理网络数据的读写、协议解析等操作。

8、Netty的优势是什么?

Netty的优势主要包括:高性能、高可靠性、易于扩展、灵活性高、易于使用等。

9、Netty的应用场景有哪些?

Netty的应用场景非常广泛,主要包括:网络通信、分布式系统、高并发服务器、实时数据处理等。

10、Netty与其他网络编程框架的区别是什么?

与其他的网络编程框架相比,Netty具有更高的性能、更好的可扩展性和更丰富的功能,同时也易于使用和维护。

11、为什么要使用Netty?

因为Netty具有以下特点,并且相比于直接使用JDK自带的NIO相关的API来说更加易用。

  • 统一的API,支持多种传输类型,阻塞和非阻塞的。
  • 简单而强大的线程模型。
  • 自带编解码器解决TCP粘包/拆包问题。
  • 自带各种协议栈。
  • 真正的无连接数据包套接字支持。
  • 比直接使用Java核心API有更高的吞吐量、更低的延迟、更低的资源消耗和更少的内存复制。
  • 安全性不错,有完整的SSL/TLS以及StartTLS支持。
  • 社区活跃
  • 成熟稳定,经历了大型项目的使用和考核,而且很多开源项目都使用到了Netty,比如Dubbo、RocketMQ等。

12、Netty的优势有哪些?

  • 使用简单:封装了NIO的很多细节,使用更简单。
  • 功能强大:预置了多种编解码功能,支持多种主流协议。
  • 定制能力强:可以通过ChannelHandler对通信框架进行灵活地扩展。
  • 性能高:通过与其他业界主流的NIO框架对比,Netty的综合性最优。
  • 稳定:Netty修复了已经发现的所有NIO的bug,让开发人员可以专注于业务本身。
  • 社区活跃:Netty是活跃的开源项目,版本迭代周期短,bug修复速度快。

13、什么是TCP粘包/拆包?

TCP粘包/拆包就是基于TCP发送数据的时候,出现了多个字符串“粘”在一起或者一个字符串被“拆”开的问题。

14、如何在Netty中解决TCP粘包问题?

1)使用Netty自带的解码器

  •  LineBasedFrameDecoder:发送端发送数据包的时候,每个数据包之间以换行符作为分割,LineBasedFrameDecoder的工作原理是它依次遍历ByteBuf中的可读字节,判断是否有换行符,然后进行相应的截取。
  • DelimiterBasedFrameDecoder:可以自定义分隔符解码器,LineBasedFrameDecoder实际上是一种特殊的DelimiterBasedFrameDecoder解码器。
  • FixedLengthFrameDecoder:固定长度解码器,它能够按照指定的长度对消息进行相应的拆包。

2)自定义序列化编解码器

在Java中自带的有实现Serializable接口来实现序列化,但由于它性能、安全性等原因一般情况下是不会被使用到的。通常情况下,我们使用Protostuff、Hessian2、json序列化方式比较多,另外还有一些序列化性能非常好的序列化方式也是很好的选择:

  • 专门针对Java语言的:Kryo,FST等。
  • 跨语言的:Protostuff(基于protobuf发展而来 ),ProtoBuf、Thrift、Avro、MsgPack等。

15、TCP长连接和短连接了解吗?

我们知道TCO在进行读写之前,server与client之间必须提前建立一个连接。建立连接的过程,需要我们常说的三次握手,释放/关闭连接则需要四次握手。这个过程是比较消耗网络资源并且有时间延迟的。所谓短连接,就是server端与client端建立连接,读写完成之后就关闭连接,如果下一次再要互相发送消息就要重新连接。

短连接的优点很明显,就是管理和实现都比较简单,缺点也很明显,每一次的读写都要建立连接,这会带来大量的网络资源消耗,并且连接的建立也需要耗费时间。

长连接说的就是client向server双方建立连接之后,即使client与server完成一次读写,它们之间的连接并不会主动关闭,后续的读写操作会继续使用这个连接。

长连接可以省去比较多的TCP的建立和关闭操作,降低了对网络资源的依赖,能够节约时间。

16、为什么需要心跳机制?Netty中心跳机制了解吗?

在TCP保持长连接的过程中,可能会出现断网等网络异常出现,异常发生的时候,client与server之间如果没有交互的话,它们是无法发现对方已经掉线的。为了解决这个问题,我们就需要引入心跳机制。

心跳机制的工作原理是:在client与server之间在一定时间内没有数据交互时,即处于idle状态时,客户端或服务器就会发送一个特殊的数据包给对方,当接收方收到这个数据报文后,也立即发送一个特殊的数据报文回应发送方,此即一个PING-PONG交互。所以,当某一端收到心跳消息后,就知道对方仍然在线,这就确保TCP的连接有效性。TCP实际上自带的就有长连接选项,本身也是有心跳包机制,也就是:SO_KEEPALIVE。

但是,TCP协议层面的长连接灵活性不够。所以,一般情况下我们都是在应用层协议上实现自定义心跳机制的,也就是在Netty层面通过编码实现。通过Netty实现心跳机制,核心是IdleStateHandler。

17、什么是Netty的零拷贝?

零拷贝技术是指计算机执行操作时,CPU不需要先将数据从某处内存复制到另一个特定区域。这种技术通常用于通过网络传输文件时节省CPU周期和内存。

在OS层面上的零拷贝通常值避免在用户态与内核态之间来回拷贝数据。而在Netty层面,零拷贝主要体现在对于数据操作的优化。Netty中的零拷贝体现在以下几个方面:

  • 使用Netty提供的CompositeByteBuf类,可以将多个ByteBuf合并为一个逻辑上的ByteBuf,避免了各个ByteBuf之间的拷贝。
  • ByteBuf支持slice操作,因此可以将ByteBuf分解为多个共享同一个存储区域的ByteBuf,避免了内存的拷贝。
  • 通过FileRegion包装的FileChannel.tranferTo实现文件传输,可以直接将文件缓冲区的数据发送到目标Channel,避免了传统通过循环write方式导致的内存拷贝问题。

18、Netty和Tomcat的区别?

  • 作用不同:Tomcat是Servlet容器,可以视为Web服务器,而Netty是异步事件驱动的网络应用程序框架和工具用于简化网络编程,例如TCP和UDP套接字服务器。
  • 协议不同:Tomcat是基于http协议的Web服务器,而Netty能通过编程自定义各种协议,因为Netty自身自己能编解码字节流,所有Netty可以实现HTTP服务器、FTP服务器、UDP服务器、RPC服务器、WebSocket服务器、Redis的Proxy服务器、MySQL的Proxy服务器等。

19、Netty发送消息有几种方式?

Netty有两种发送消息的方式:

  • 直接写入Channel中,消息从ChannelPipeline当中尾部开始移动。
  • 写入和ChannelHandler绑定的ChannelHandlerContext中,消息从ChannelPipeline中的下一个ChannelHandler中移动。

20、BIO、NIO、AIO的区别是什么?

  • BIO:一个连接一个线程,客户端有连接请求时服务器端就需要启动一个线程进行处理。线程开销大。
  • 伪异步IO:将请求连接放入线程池,一对多,但线程还是很宝贵的资源。
  • NIO:一个请求一个线程,但客户端发送的连接请求都会注册到多路复用器上,多路复用器轮询到连接有I/O请求时才启动一个线程进行处理。
  • AIO:一个有效请求一个线程,客户端的I/O请求都是由OS先完成了再通知服务器应用去启动线程进行处理。

BIO是面向流的,NIO是面向缓冲区的。BIO的各种流是阻塞的,而NIO是非阻塞的。BIO的Stream是单向的,而NIO的channel是双向的。

NIO的特点:事件驱动模型、单线程处理多任务、非阻塞I/O,I/O读写不再阻塞,而是返回0、基于block的传输比基于流的传输更高效、更高级的IO函数zero-copy、IO多路复用器大大提高了Java网络应用的可伸缩性和实用性。基于Reactor线程模型。在Reactor模式中,事件分发器等待某个事件或者可应用某个操作的状态发生,事件分发器就把这个事件传给事先注册好的事件处理函数或者回调函数,由后者来做实际的读写操作。

如在Reactor中实现读:注册读就绪事件和相应的事件处理器、事件分发器等待事件,事件到来,激活分发器,分发器调用事件对应的处理器,事件处理器完成实际的读操作,处理读到的数据,注册新的事件,然后返还控制权。

21、Netty支持哪几种序列化协议?

序列化是将对象序列化为二进制形式,主要用于网络传输、数据持久化等;而反序列化则是将从网络。磁盘读取的字节数组还原成原始对象,主要用于网络传输对象的解码,以便完成远程调用。影响序列化性能的关键因素:序列化后的码流大小,序列化的性能;是否支持跨语言、Java默认提供的序列化:无法跨语言;序列化后的码流太大;序列化的性能差。

  • XML:

优点:人机可读性好,可指定元素或者特性的名称。

缺点:序列化数据只包含数据本身以及类的结构,不包括类型标识和程序集信息;只能序列化公共属性和字段,不能序列化方法;文件庞大,文件格式复制,传输占带宽。

适用场景:当做配置文件存储数据,实时数据转换。

  • JSON:是一种轻量级的数据交互格式。

优点:兼容性高、数据格式比较简单,易于读写、序列化后数据较小,可扩展性高,兼容性好、与XML相比,其协议比较简单,解析速度比较快。

缺点:数据的描述性比XML差,不适合性能要求为ms级别的情况、额外空间开销比较大。

适用场景:跨防火墙访问、可调式性要求高、基于Web browser的Ajax请求、传输数据量相对小,实时性要求相对低的服务。

  • Fastjson

采用一种“假定有序快速匹配”的算法。

优点:接口简单易用。

缺点:偏离了“标准”及功能性、代码质量不高,稳定不全。

适用场景:协议交互、Web输出、Android客户端。

  • Thrift

不仅是序列化协议,还是一个RPC框架。

优点:序列化后的体积小、速度快、支持多种语言和丰富的数据类型、对于数据字段的增删具有较强的兼容性、支持二进制压缩编码。

缺点:使用者较少、跨防火墙访问时不安全、不具有可读性,调试代码时相对困难、不能与其他传输层协议共同使用(例如HTTP)、无法支持向持久层直接读写数据,即不适合做数据持久化序列化协议。

适用场景:分布式系统的RPC解决方案。

  • Avro

Hadoop的一个子项目,解决了JSON的冗长和IDL的问题。

优点:支持丰富的数据类型、简单的动态语言结合功能、具有自我描述属性、提高了数据解析速度、快速可压缩的二进制数据形式、可以实现远程过程调用RPC、支持跨编程语言实现。

缺点:对于习惯于静态类型语言的用户不直观。

适用场景:在Hadoop中做Hive、Pig和MapReduce的持久化数据格式。

  • Protobuf

将数据结构以.proto文件进行描述,通过代码生成工具可以生成对应数据结构的POJO对象和Protobuf相关的方法和属性。

优点:序列化后码流小、性能高、结构化数据存储格式、通过标识字段的顺序,可以实现协议的前向兼容、结构化的文档更容易管理和维护。

缺点:需要依赖工具生成代码、支持的语言相对较少,官方只支持Java、C++、python。

适用场景:对性能要求高的RPC调用、具有良好的跨防火墙的访问属性、适合应用层对象的持久化。

22、什么是Bootstrao和ServerBootstrap?

  • Bootstrap通常使用connect()方法连接到远程的主机和端口,作为一个Netty TCP协议中的客户端。另外,Bootstrap也可以通过bind()方法绑定本地的一个端口,作为UDP协议通信中的一段。
  • ServerBootstrap通常使用bind()方法绑定本地的端口上,然后等待客户端的连接。
  • Bootstrap只需要配置一个线程组-EventLoopGroup,而ServerBootstrap需要配置两个线程组-EventLoopGroup,一个用于接收连接,一个用于具体的处理。

23、什么是Netty线程模型?

在Netty主要靠NioEventLoopGroup线程池来实现具体的线程模型,我们实现服务端的时候,一般会初始化两个线程组:bootGroup用于接收连接;workerGroup复制具体的处理,交由对应的Handler处理。

单线程模型:

一个线程需要执行所有的accept、read、decode、process、encode、send事件。对于高负载、高并发,并且对性能要求比较高的场景不适用。

多线程模型:

一个Acceptor线程只负责监听客户端的连接,一个NIO线程池负责具体处理accept、read、decode、process、encode、send事件。满足绝大部分应用场景,并发连接量不大的时候没啥问题,但是遇到并发连接大的时候就可能会出现问题,成为性能瓶颈。

主从多线程模型:

从一个主线程NIO线程池中选择一个线程作为Acceptor线程,绑定监听端口,接收客户端连接的连接,其他线程负责后续的接入认证等工作。连接建立完成后,SubNIO线程池负责具体处理I/O读写。如果多线程模型无法满足需求的时候,可以考虑使用主从多线程模型。