11月27号视频——面试遇到Netty,这样回答就完全对了!
面试官:介绍一下自己对 Netty 的认识吧
普通人:
Netty是一个高性能NIO框架,它可以用来解决高并发网络通信问题。
在很多中间件的网络通信层都是采用Netty,不过我对Netty接触得比较少,了解不是很多。
高手:
好的,我用三点来简单的介绍下Netty
第一:Netty 是一个 基于 NIO 模型的高性能网络通信框架,其实可以认为它是对NIO网络模。型的封装,提供了简单易用的API,我们可以利用这些封装好的API快速开发自己的网络程序。
第二:Netty在NIO的基础上做了很多优化,比如零拷贝机制、高性能无锁队列、内存池等,因此性能会比NIO更高。
第三:Netty可以支持多种通信协议,如Http、WebSocket等,并且针对数据通信的拆包黏包问题,Netty内置了拆包策略。
面试官:那你在说说为什么要用 Netty ?
高手:Nety相比于直接使用 JDK 自带的 NIO 相关的 API 来说更加易用。同时,它还具有以下特点:
1、统一的 API,支持多种传输类型,如阻塞、非阻塞, 以及epoll、poll等模型。
2、我们可以使用非常少的代码来实现,多线程Reactor模型以及主从多线程Reactor模型
3、自带编解码器解决 TCP 粘包/拆包问题。
4、自带各种协议栈。
5、比直接使用 Java 库中的NIO API 有更高的吞吐量、更低的延迟、更低的资源消耗和更少的内存复制。
6、安全性不错,有完整的 SSL/TLS 以及 StartTLS 支持。
7、社区活跃成熟稳定,经历了大型项目的使用和考验,而且很多开源项目都使用到了 Netty, 比如我们经常接触的 Dubbo、RocketMQ 等等。
面试官:那你在通俗地说一下 Netty 可以做什么事情?
高手:
我们之所以要用Netty,核心点还是在于解决服务器如何承载更多的用户同时访问的问题。
传统的BIO模型,由于阻塞的特性,使得在高并发场景中,很难获得更高的吞吐量。而后来基于NIO的多路复用模型虽然在阻塞方面进行了优化,但是它的API使用比较复杂,对于初学者来说使用不是很友好。而Netty是基于NIO的封装,提供了成熟且简单易用的API,降低了使用成本和学习成本。
本质上来说,Netty和NIO所扮演的角色是相同的,都是为了提升服务端的吞吐量,让用户获得更好的产品体验。
另外,Netty这个中间件经过很多年的验证,在目前主流的中间件如Zookeeper、Dubbo、RocketMQ中都有应用。
面试官:Netty 核心组件了解吗?分别有什么作用?
高手:Netty由三层结构构成:网络通信层、事件调度器与服务编排层
在网络通信层有三个核心组件:Bootstrap、ServerBootStrap、Channel
Bootstrap负责客户端启动并用来链接远程netty server
ServerBootStrap负责服务端监听,用来监听指定端口,
Channel是负责网络通信的载体
事件调度器有两个核心组件:EventLoopGroup与EventLoop
EventLoopGroup本质上是一个线程池,主要负责接收I/O请求,并分配线程执行处理请求。
EventLoop。相当于线程池中的线程
在服务编排层有三个核心组件ChannelPipeline、ChannelHandler、ChannelHandlerContext
ChannelPipeline负责将多个Channelhandler链接在一起
ChannelHandler针对IO数据的处理器,数据接收后,通过指定的Handler进行处理。
ChannelHandlerContext用来保存ChannelHandler的上下文信息
面试官:那,在说说Netty 有几种线程模型吧?
高手Netty提供了三种Reactor模型的支持
单线程单Reactor模型
多线程单Reactor模型
多线程多Reactor模型
面试官:这几种线程模型如何实现?
高手:Reactor模型有三个重要的组件:
Reactor :将I/O事件发派给对应的Handler
Acceptor :处理客户端连接请求
Handlers :执行非阻塞读/写
这是最基本的单Reactor单线程模型
我们来看这张图:

多线程单Reactor模型
单线程Reactor这种实现方式有存在着缺点,从实例代码中可以看出,handler的执行是串行的,如果其中一个handler处理线程阻塞将导致其他的业务处理阻塞。由于handler和reactor在同一个线程中的执行,这也将导致无法接收新的请求。因此有人做了一个实验:
在Reactor代码的DispatchHandler的run方法中,增加一个Thread.sleep()。
打开多个客户端窗口连接到Reactor Server端,其中一个窗口发送一个信息后被阻塞,另外一个窗口再发信息时由于前面的请求阻塞导致后续请求无法被处理。
为了解决这种问题,有人提出使用多线程的方式来处理业务,也就是在业务处理的地方加入线程池异步处理,将reactor和handler在不同的线程来执行,这就是多线程单Reactor模型

多线程多Reactor模型
在多线程单Reactor模型中,所有的I/O操作是由一个Reactor来完成,而Reactor运行在单个线程中,它需要处理包括Accept()/read()/write/connect操作,对于小容量的场景,影响不大。但是对于高负载、大并发或大数据量的应用场景时,容易成为瓶颈,主要原因如下:
一个NIO线程同时处理成百上千的链路,性能上无法支撑,即便NIO线程的CPU负荷达到100%,也无法满足海量消息的读取和发送;
当NIO线程负载过重之后,处理速度将变慢,这会导致大量客户端连接超时,超时之后往往会进行重发,这更加重了NIO线程的负载,最终会导致大量消息积压和处理超时,成为系统的性能瓶颈;
所以,我们还可以更进一步优化,引入多Reactor多线程模式:

Main Reactor,负责接收客户端的连接请求,然后把接收到的请求传递给SubReactor(其中subReactor可以有多个),具体的业务IO处理由SubReactor完成。
Acceptor,请求接收者,在实践时其职责类似服务器,并不真正负责连接请求的建立,而只将其请求委托 Main Reactor 线程池来实现,起到一个转发的作用。
Main Reactor,主 Reactor 线程组,主要负责连接事件,并将IO读写请求转发到 SubReactor 线程池。
Sub Reactor,Main Reactor 通常监听客户端连接后会将通道的读写转发到 Sub Reactor 线程池中一个线程(负载均衡),负责数据的读写。在 NIO 中 通常注册通道的读(OP_READ)、写事件(OP_WRITE)。
喜欢的朋友一键三连,加个关注不过分吧!