Netty:常见的面试题和答案

服务器 0

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 粘包问题?

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

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

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

15. TCP 长连接和短连接了解么?

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

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

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

长连接的可以省去较多的 TCP 建立和关闭的操作,降低对网络资源的依赖,节约时间。对于频繁请求资源的客户来说,非常适用长连接。

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

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

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

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

17. 讲讲 Netty 的零拷贝

答:零复制(英语:Zero-copy;也译零拷贝)技术是指计算机执行操作时,CPU不需要先将数据从某处内存复制到另一个特定区域。这种技术通常用于通过网络传输文件时节省CPU周期和内存带宽。
在 OS 层面上的 Zero-copy 通常指避免在 用户态(User-space) 与内核态(Kernel-space) 之间来回拷贝数据。而在 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支持哪几种序列化协议?

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

  • XML,
    优点:人机可读性好,可指定元素或特性的名称。
    缺点:序列化数据只包含数据本身
    以及类的结构,不包括类型标识和程序集信息;只能序列化公共属性和字段;不能序列化方
    法;文件庞大,文件格式复杂,传输占带宽。
    适用场景:当做配置文件存储数据,实时数据
    转换。

  • JSON,是一种轻量级的数据交换格式。
    优点:兼容性高、数据格式比较简单,易于读写、
    序列化后数据较小,可扩展性好,兼容性好、与 XML 相比,其协议比较简单,解析速度比
    较快。
    缺点:数据的描述性比 XML 差、不适合性能要求为 ms 级别的情况、额外空间开销比
    较大。
    适用场景(可替代XML):跨防火墙访问、可调式性要求高、基于 Web browser 的
    Ajax 请求、传输数据量相对小,实时性要求相对低(例如秒级别)的服务。

  • Fastjson,采用一种“假定有序快速匹配”的算法。
    优点:接口简单易用、目前 java 语言中
    最快的 json 库。
    缺点:过于注重快,而偏离了“标准”及功能性、代码质量不高,文档不
    全。
    适用场景:协议交互、Web 输出、Android 客户端

  • Thrift,不仅是序列化协议,还是一个 RPC 框架。
    优点:序列化后的体积小, 速度快、支持
    多种语言和丰富的数据类型、对于数据字段的增删具有较强的兼容性、支持二进制压缩编码。
    缺点:使用者较少、跨防火墙访问时,不安全、不具有可读性,调试代码时相对困难、不能
    与其他传输层协议共同使用(例如 HTTP)、无法支持向持久层直接读写数据,即不适合做数
    据持久化序列化协议。
    适用场景:分布式系统的 RPC 解决方案

  • Avro,Hadoop 的一个子项目,解决了 JSON 的冗长和没有 IDL 的问题。
    优点:支持丰富的数
    据类型、简单的动态语言结合功能、具有自我描述属性、提高了数据解析速度、快速可压缩
    的二进制数据形式、可以实现远程过程调用 RPC、支持跨编程语言实现。
    缺点:对于习惯于
    静态类型语言的用户不直观。
    适用场景:在 Hadoop 中做 Hive、Pig 和 MapReduce 的持久化
    数据格式。

  • Protobuf,将数据结构以.proto 文件进行描述,通过代码生成工具可以生成对应数据结构的
    POJO 对象和 Protobuf 相关的方法和属性。
    优点:序列化后码流小,性能高、结构化数据存
    储格式(XML JSON 等)、通过标识字段的顺序,可以实现协议的前向兼容、结构化的文档更
    容易管理和维护。
    缺点:需要依赖于工具生成代码、支持的语言相对较少,官方只支持 Java 、
    C++ 、python。
    适用场景:对性能要求高的 RPC 调用、具有良好的跨防火墙的访问属性、适
    合应用层对象的持久化

  • 其它
    protostuff 基于 protobuf 协议,但不需要配置 proto 文件,直接导包即可
    Jboss marshaling 可以直接序列化 java 类, 无须实 java.io.Serializable 接口
    Message pack 一个高效的二进制序列化格式
    Hessian 采用二进制协议的轻量级 remoting onhttp 工具
    kryo 基于 protobuf 协议,只支持 java 语言,需要注册(Registration),然后序列化(Output),
    反序列化(Input)

22. 请说下对 Bootstrap 和 ServerBootstrap 的了解?

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

23. 请说下 Netty 线程模型?

在 Netty 主要靠 NioEventLoopGroup 线程池来实现具体的线程模型的。我们实现服务端的时候,一般会初始化两个线程组:

  • bossGroup:接收连接。
  • workerGroup :负责具体的处理,交由对应的 Handler 处理。

单线程模型 :

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

多线程模型:

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

主从多线程模型:

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

24. select、poll、epoll 区别有哪些?

  • select:它仅仅知道了,有 I/O 事件发生了,却并不知道是哪那几个流(可能有一个,多个,甚至全部),我们只能无差别轮询所有流,找出能读出数据,或者写入数据的流,对他们进行操作。所以 select 具有 O(n)的无差别轮询复杂度,同时处理的流越多,无差别轮询时间就越长。

  • poll:poll 本质上和 select 没有区别,它将用户传入的数组拷贝到内核空间,然后查询每个fd 对应的设备状态, 但是它没有最大连接数的限制,原因是它是基于链表来存储的.

  • epoll:epoll 可以理解为 event poll,不同于忙轮询和无差别轮询,epoll 会把哪个流发生了怎样的 I/O 事件通知我们。所以我们说 epoll 实际上是事件驱动(每个事件关联上fd)的,此时我们对这些流的操作都是有意义的。(复杂度降低到了 O(1)),通过红黑树和双链表数据结构,并结合回调机制,造就了 epoll 的高效,epoll_create(),epoll_ctl()和epoll_wait()系统调用。

在这里插入图片描述

也许您对下面的内容还感兴趣: