2021大厂Java面试真题(Netty )

2021大厂Java面试真题(Netty

1、为什么要用 Netty 呢?

因为 Netty 具有下面这些优点,并且相比于直接使用 JDK 自带的 NIO 相关的 API 来说

更加易用。

  • 统一的 API,支持多种传输类型,阻塞和非阻塞的。

  • 简单而强大的线程模型。

  • 自带编解码器解决 TCP 粘包/拆包问题。

  • 自带各种协议栈。

  • 真正的无连接数据包套接字支持。

  • 比直接使用 Java 核心 API 有更高的吞吐量、更低的延迟、更低的资源消耗和更少的内存

复制。

  • 安全性不错,有完整的 SSL/TLS 以及 StartTLS 支持。

  • 社区活跃

  • 成熟稳定,经历了大型项目的使用和考验,而且很多开源项目都使用到了 Netty, 比如我

们经常接触的 Dubbo、RocketMQ 等等。

2、Netty 有哪些应用场景?

理论上来说,NIO 可以做的事情 ,使用 Netty 都可以做并且更好。Netty 主要用来做网

络通信 :

  • 作为 RPC 框架的网络通信工具 :我们在分布式系统中,不同服务节点之间经常需要相互

调用,这个时候就需要 RPC 框架了。不同服务节点之间的通信是如何做的呢?可以使用

Netty 来做。比如我调用另外一个节点的方法的话,至少是要让对方知道我调用的是哪个

类中的哪个方法以及相关参数吧!

  • 实现一个自己的 HTTP 服务器 :通过 Netty 我们可以自己实现一个简单的 HTTP 服务器,

这个大家应该不陌生。说到 HTTP 服务器的话,作为 Java 后端开发,我们一般使用

Tomcat 比较多。一个最基本的 HTTP 服务器可要以处理常见的 HTTP Method 的请求,

比如 POST 请求、GET 请求等等。

  • 实现一个即时通讯系统 :使用 Netty 我们可以实现一个可以聊天类似微信的即时通讯系

统,这方面的开源项目还蛮多的,可以自行去 Github 找一找。

  • 实现消息推送系统 :市面上有很多消息推送系统都是基于 Netty 来做的。

3、Netty 的优势有哪些?

  • 使用简单:封装了 NIO 的很多细节,使用更简单。

  • 功能强大:预置了多种编解码功能,支持多种主流协议。

  • 定制能力强:可以通过 ChannelHandler 对通信框架进行灵活地扩展。

  • 性能高:通过与其他业界主流的 NIO 框架对比,Netty 的综合性能最优。

  • 稳定:Netty 修复了已经发现的所有 NIO 的 bug,让开发人员可以专注于业务本身。

  • 社区活跃:Netty 是活跃的开源项目,版本迭代周期短,bug 修复速度快。

4**、Netty 核心组件有哪些?分别有什么作用?**

Channel

Channel 接口是 Netty 对网络操作抽象类,它除了包括基本的 I/O 操作,如 bind()、

connect()、read()、write() 等。

比较常用的 Channel 接口实现类是 NioServerSocketChannel(服务端)和

NioSocketChannel(客户端),这两个 Channel 可以和 BIO 编程模型中的 ServerSocket

以及 Socket 两个概念对应上。Netty 的 Channel 接口所提供的 API,大大地降低了直接使

用 Socket 类的复杂性。

EventLoop

EventLoop 的主要作用实际就是负责监听网络事件并调用事件处理器进行相关 I/O 操作

的处理。

ChannelFuture

Netty 是异步非阻塞的,所有的 I/O 操作都为异步的。因此,我们不能立刻得到操作是否

执行成功,但是,你可以通过 ChannelFuture 接口的 addListener() 方法注册一

个 ChannelFutureListener,当操作执行成功或者失败时,监听就会自动触发返回结果。

ChannelHandler 和 ChannelPipeline

ChannelHandler 是消息的具体处理器。他负责处理读写操作、客户端连接等事情。

ChannelPipeline 为 ChannelHandler 的链,提供了一个容器并定义了用于沿着链传播入

站和出站事件流的 API 。当 Channel 被创建时,它会被自动地分配到它专属的

ChannelPipeline。

我们可以在 ChannelPipeline 上通过 addLast() 方法添加一个或者多个

ChannelHandler ,因为一个数据或者事件可能会被多个 Handler 处理。当一

个 ChannelHandler 处理完之后就将数据交给下一个 ChannelHandler 。

5、EventloopGroup 了解么?和 EventLoop 啥关系?

EventLoopGroup 包含多个 EventLoop(每一个 EventLoop 通常内部包含一个线程),

上面我们已经说了 EventLoop 的主要作用实际就是负责监听网络事件并调用事件处理器进行

相关 I/O 操作的处理。

并且 EventLoop 处理的 I/O 事件都将在它专有的 Thread 上被处理,即 Thread 和

EventLoop 属于 1 : 1 的关系,从而保证线程安全。

Boss EventloopGroup 用于接收连接,Worker EventloopGroup 用于具体的处理(消

息的读写以及其他逻辑处理)。

当客户端通过 connect 方法连接服务端时,bossGroup 处理客户端连接请求。当客户端

处理完成后,会将这个连接提交给 workerGroup 来处理,然后 workerGroup 负责处理其

IO 相关操作。

6、请说下对 Bootstrap 和 ServerBootstrap 的了解?

  • Bootstrap 通常使用 connet() 方法连接到远程的主机和端口,作为一个 Netty TCP 协议

通信中的客户端。另外,Bootstrap 也可以通过 bind() 方法绑定本地的一个端口,作为

UDP 协议通信中的一端。

  • ServerBootstrap 通常使用 bind() 方法绑定本地的端口上,然后等待客户端的连接。

  • Bootstrap 只需要配置一个线程组— EventLoopGroup ,而 ServerBootstrap 需要配置

两个线程组— EventLoopGroup ,一个用于接收连接,一个用于具体的处理。

7、请说下 Netty 线程模型?

在 Netty 主要靠 NioEventLoopGroup 线程池来实现具体的线程模型的 。

我们实现服务端的时候,一般会初始化两个线程组:

  • bossGroup:接收连接。

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

单线程模型 :

一个线程需要执行处理所有的 accept、read、decode、process、encode、send 事件。

对于高负载、高并发,并且对性能要求比较高的场景不适用。

多线程模型

一个 Acceptor 线程只负责监听客户端的连接,一个 NIO 线程池负责具体处理 accept、

read、decode、process、encode、send 事件。满足绝大部分应用场景,并发连接量不大

的时候没啥问题,但是遇到并发连接大的时候就可能会出现问题,成为性能瓶颈。

主从多线程模型

从一个 主线程 NIO 线程池中选择一个线程作为 Acceptor 线程,绑定监听端口,接收客

户端连接的连接,其他线程负责后续的接入认证等工作。连接建立完成后,Sub NIO 线程池

负责具体处理 I/O 读写。如果多线程模型无法满足你的需求的时候,可以考虑使用主从多线程

模型 。

8、Netty 服务端和客户端的启动过程是怎样的?

服务端

\1. 首先你创建了两个 NioEventLoopGroup 对象实例:bossGroup 和 workerGroup。

  • bossGroup : 用于处理客户端的 TCP 连接请求。

  • workerGroup :负责每一条连接的具体读写数据的处理逻辑,真正负责 I/O 读写操作,

交由对应的 Handler 处理。

\2. 接下来 我们创建了一个服务端启动引导/辅助类:ServerBootstrap,这个类将引导我们

进行服务端的启动工作。

\3. 通过 .group() 方法给引导类 ServerBootstrap 配置两大线程组,确定了线程模型。

\4. 通过 channel()方法给引导类 ServerBootstrap 指定了 IO 模型为 NIO

  • NioServerSocketChannel :指定服务端的 IO 模型为 NIO,与 BIO 编程模型中的

ServerSocket 对应。

  • NioSocketChannel : 指定客户端的 IO 模型为 NIO, 与 BIO 编程模型中的 Socket 对

应 5.通过 .childHandler()给引导类创建一个 ChannelInitializer ,然后制定了服务端消

息的业务处理逻辑 HelloServerHandler 对象 6.调用 ServerBootstrap 类的 bind()方法

绑定端口

客户端

  • 创建一个 NioEventLoopGroup 对象实例

  • 创建客户端启动的引导类是 Bootstrap

  • 通过 .group() 方法给引导类 Bootstrap 配置一个线程组

  • 通过 channel()方法给引导类 Bootstrap 指定了 IO 模型为 NIO

  • 通过 .childHandler()给引导类创建一个 ChannelInitializer ,然后制定了客户端消息的业

务处理逻辑 HelloClientHandler 对象

  • 调用 Bootstrap 类的 connect()方法进行连接,这个方法需要指定两个参数:

  • inetHost : ip 地址

  • inetPort : 端口号

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

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

者一个字符串被“拆”开的问题。

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

\1. 使用 Netty 自带的解码器

  • LineBasedFrameDecoder : 发送端发送数据包的时候,每个数据包之间以换行符作为分

隔,LineBasedFrameDecoder 的工作原理是它依次遍历 ByteBuf 中的可读字节,判断

是否有换行符,然后进行相应的截取。

  • DelimiterBasedFrameDecoder : 可以自定义分隔符解码器,

LineBasedFrameDecoder 实际上是一种特殊的 DelimiterBasedFrameDecoder 解码

器。

  • FixedLengthFrameDecoder: 固定长度解码器,它能够按照指定的长度对消息进行相应

的拆包。LengthFieldBasedFrameDecoder:

\2. 自定义序列化编解码器

在 Java 中自带的有实现 Serializable 接口来实现序列化,但由于它性能、安全性等原因

一般情况下是不会被使用到的。

通常情况下,我们使用 Protostuff、Hessian2、json 序列方式比较多,另外还有一些序

列化性能非常好的序列化方式也是很好的选择:

  • 专门针对 Java 语言的:Kryo,FST 等等

  • 跨语言的:Protostuff(基于 protobuf 发展而来),ProtoBuf,Thrift,Avro,

MsgPack 等等

**11、**TCP 长连接和短连接了解么?

我们知道 TCP 在进行读写之前,server 与 client 之间必须提前建立一个连接。建立连接

的过程,需要我们常说的三次握手,释放/关闭连接的话需要四次挥手。这个过程是比较消耗

网络资源并且有时间延迟的。

所谓,短连接说的就是 server 端 与 client 端建立连接之后,读写完成之后就关闭掉连接,

如果下一次再要互相发送消息,就要重新连接。短连接的优点很明显,就是管理和实现都比较

简单,缺点也很明显,每一次的读写都要建立连接必然会带来大量网络资源的消耗,并且连接

的建立也需要耗费时间。

长连接说的就是 client 向 server 双方建立连接之后,即使 client 与 server 完成一次读

写,它们之间的连接并不会主动关闭,后续的读写操作会继续使用这个连接。长连接的可以省

去较多的 TCP 建立和关闭的操作,降低对网络资源的依赖,节约时间。对于频繁请求资源的

客户来说,非常适用长连接。

**12、**为什么需要心跳机制?Netty 中心跳机制了解么?

在 TCP 保持长连接的过程中,可能会出现断网等网络异常出现,异常发生的时候,

client 与 server 之间如果没有交互的话,他们是无法发现对方已经掉线的。为了解决这个问

题, 我们就需要引入 心跳机制 。

心跳机制的工作原理是: 在 client 与 server 之间在一定时间内没有数据交互时, 即处于

idle 状态时, 客户端或服务器就会发送一个特殊的数据包给对方, 当接收方收到这个数据报文

后, 也立即发送一个特殊的数据报文, 回应发送方, 此即一个 PING-PONG 交互。所以, 当某一

端收到心跳消息后, 就知道了对方仍然在线, 这就确保 TCP 连接的有效性.

TCP 实际上自带的就有长连接选项,本身是也有心跳包机制,也就是 TCP 的选项:

SO_KEEPALIVE。但是,TCP 协议层面的长连接灵活性不够。所以,一般情况下我们都是在

应用层协议上实现自定义信跳机制的,也就是在 Netty 层面通过编码实现。通过 Netty 实现

心跳机制的话,核心类是 IdleStateHandler 。

**13、**讲讲 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 方式导致的内存拷贝问题.

14、Netty 和 Tomcat 的区别?

  • 作用不同:Tomcat 是 Servlet 容器,可以视为 Web 服务器,而 Netty 是异步事件驱动

的网络应用程序框架和工具用于简化网络编程,例如 TCP 和 UDP 套接字服务器。

  • 协议不同:Tomcat 是基于 http 协议的 Web 服务器,而 Netty 能通过编程自定义各种

协议,因为 Netty 本身自己能编码/解码字节流,所有 Netty 可以实现,HTTP 服务器、

FTP 服务器、UDP 服务器、RPC 服务器、WebSocket 服务器、Redis 的 Proxy 服务器、

MySQL 的 Proxy 服务器等等。

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

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

  • 直接写入 Channel 中,消息从 ChannelPipeline 当中尾部开始移动;

  • 写入和 ChannelHandler 绑定的 ChannelHandlerContext 中,消息从 ChannelPipeline

中的下一个 ChannelHandler 中移动。

你可能感兴趣的:(面试,java,面试,rpc)