Netty面试题

71 阅读7分钟

1、为什么选择Netty?

  • API使用简单,开发门槛低。
  • 功能强大,预置了多种编解码功能,支持多种主流协议。
  • 定制能力强,可用通过ChannelHandler对通信框架进行灵活的扩展。
  • 性能高,通过和其它业界主流的NIO框架对比,Netty性能最优。
  • 成熟稳定、社区活跃、经过了线上各种大规模商业应用的验证。

2、Netty的使用场景

  • 构建高性能、低延迟的各种Java中间件,例如MQ、分布式服务框架,Netty主要作为基础通信框架提供高性能、低时延的通信服务。
  • 公有或者私有协议栈的基础通信框架,例如可用基于Netty构建异步、高性能的WebSocket协议栈。
  • 各领域应用,例如大数据、游戏等。Netty作为高性能的通信框架用于内部各模块的数据分发、传输和汇总。实现模块之间高性能通信。

3、原生的NIO在JDK1.7中存在epoll bug

它会导致Selector空轮询,最终导致CPU100%。

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

1)要发送的数据大于TCP发送缓冲区剩余空间大小,将会发生拆包。

2)待发送数据大于MSS(最大报文长度),TCP在传输前将进行拆包。

3)要发送的数据小于TCP发送缓冲区大小,TCP将多次写入缓冲区的数据一次发送出去,将会发生拆包。

4)接收数据端的应用层没有及时读取接受缓冲区的数据,将发生粘包。

5、TCP粘包/拆包的解决办法

1)包首部添加报文长度:发送端给每个数据包添加包首部,首部中应该至少包含数据包的长度,这样接收端在接收到数据后,通过读取包首部的长度字段,便知道每一个数据包的实际长度了。

2)固定长度报文:发送端将每个数据包封装为固定长度(不够的进行包填充),这样接收端每次从接受缓冲区中读取固定长度的数据即可。

3)分隔符:在数据包之间设置边界,如添加特殊符号,这样,接收端通过这个边界就可以将不同数据包拆分开。

6、Netty线程模型

Netty是一个高性能、异步事件驱动的网络编程框架。它基于NIO提供了一套丰富的API,使得开发者可以轻松的构建可伸缩、可扩展的网络应用。Netty线程模型是其核心特性之一,定义了Netty如何处理网络请求和响应,对应用程序的可伸缩性和性能有着重要影响。

1)单Reactor单线程模型

  • 特点:所有IO操作(连接、接收、发送)都有一个线程来处理。
  • 使用场景:适用于并发连接数较小且业务处理简单的场景。
  • 优缺点:模型简单、没有线程上下文切换开销;但是一个线程处理所有IO操作,容易成为性能瓶颈。

2)单Reactor多线程模型

  •  特点:Reactor负责监听和分发事件,但不再处理具体的业务。业务逻辑处理由多个线程组成的线程池来完成。
  • 使用场景:适用于并发连接数较多但业务处理逻辑不是非常复杂的场景。
  • 优缺点:通过多线程处理业务逻辑,提高了系统的并发处理能力;但是Reactor仍然是一个潜在的性能瓶颈,特别是在高并发场景下。

3)主从Reactor多线程模型(Boss-Worker模型)

  • 特点:Netty主要基于这种模型,并做了一定的改进,它包含一个或者多个主Reactor和多个从Reactor。主Reactor负责监听和接收客户端的连接请求,并将接收到的连接分配给从Reactor处理。从Reactor负责具体的IO操作和业务处理逻辑。

  • 使用场景:适用于高并发、高负载的场景。

  • Netty实现: 

  • Boss Group专门负责客户端连接,包含多个NioEventLoop(事件循环),每个NioEventLoop负责监听和接收连接请求。

  • Worker Group专门负责网络读写,包含多个NioEventLoop,每个NioEventLoop负责处理分配给它的连接上的IO事件,并通过ChannelPipeline中的ChannelHandler进行业务逻辑处理。 

  • 优点:通过分离连接处理和IO操作,提高了系统的并发处理能力;灵活可扩展,可以根据需要调整线程池的大小。

总结:Netty的线程模型是其高性能和可扩展的关键所在。通过合理选择线程模型,并根据应用场景调整线程池的大小,可以充分利用多核CPU的计算能力,提高系统的并发处理能力和吞吐量。在实际应用中,建议根据应用的具体需求和预期负载情况选择合适的线程模型。

7、Netty的零拷贝

零拷贝指计算机操作过程中,CPU不需要将文件内容拷贝到用户空间而直接在内核空间中传输到网络的方式。

8、Netty内部执行流程

Netty内部执行流程可以概括为服务启动、建立连接、读取数据、业务处理、发送数据到关闭连接的一系列步骤。

1)服务启动

  • 创建EventLoopGroup:Netty使用EventLoopGroupIO操作,通常会有两个EventLoopGroup,一个是Boss Group,用于接收客户端的连接请求;另一个是worker Group,用于处理网络IO事件,如读写数据。
  • 配置ServerBootstrap:ServerBootstrap是Netty的启动辅助类,用于配置Netty服务器,允许设置EventLoopGroup、Channel类型、ChannelHandler等。
  • 绑定端口并启动:通过调用ServerBootstrap的bind()方法传入端口号,Netty服务器将开始监听该端口上的连接请求。

2)建立连接:

  • 接收连接请求:BootGroup中的NioEventLoop会不断轮询Selector,检查是否有新的连接请求,一旦检测到OP_ACCEPT事件,表示有新的客户端连接请求,BootGroup会处理这个连接请求。
  • 分配连接:boosGroup会创建一个新的NioSocketChannel来表示和客户端的连接,并将这个新的Channel注册到WorkerGroup中的一个NioEventLoop,以便后续处理数据读写事件。

3)读取数据

  • 处理读取事件:WorkerGroup中的NioEventLoop会不断轮询Selector,检查其管理的Channel上是否有读取事件(OP_READ)发生,一旦检测到读取事件,就会从Channel中读取数据。
  • 数据传播:读取到的数据会被封装成一个ByteBuf对象,并通过ChannelPipeline中的ChannelHandler进行传播,ChannelPipeline是一个ChannelHandler的链,每个ChannelHandler都可以对传递的数据进行处理。

4)业务处理

  • 自定义ChannelHandler:开发者可以自定义ChannelHandler来实现自己的业务逻辑。当数据通过ChannelPipeline传播时,会一次经过每一个ChannelHandler,直到被完全处理。
  • 数据转换:在ChannelHandler中,开发者可以对数据进行解码、处理、编码等操作,然后将处理后的数据发送给客户端或者继续向下传播。

5)发送数据

  • 写入数据:当需要向客户端发送数据时,可以调用Channel的write()或者writeAndFlush()方法,这些方法会将数据写入到Channel的缓冲区,并立即触发网络发送操作。
  • 资源释放:在Channel关闭过程中,Netty会释放与该Channel相关的所有资源。

6)关闭连接

  • 关闭Channel:当需要关闭与客户端的连接时,可以调用Channel的close()方法,这个方法会触发关闭操作,并将关闭事件传播到ChannelPipeline中的ChannelHandler进行处理。
  • 资源释放:在Channel关闭过程中,Netty会释放与该Channel相关的所有资源。