1 Star 0 Fork 31

邪恶的笨笨熊 / Ebooks

forked from Java精选 / Ebooks 
加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
该仓库未声明开源许可证文件(LICENSE),使用请关注具体项目描述及其代码上游依赖。
克隆/下载
2021年Netty面试题大汇总附答案.md 16.85 KB
一键复制 编辑 原始数据 按行查看 历史

2021年Netty面试题大汇总附答案

全部面试题答案,更新日期:01月30日,直接下载吧!

下载链接:高清500+份面试题资料及电子书,累计 10000+ 页大厂面试题 PDF

Netty

题1:EventloopGroup 和 EventLoop 有什么联系?

EventLoop可以理解为线程,那么Group就是线程池或者线程组,Netty的线程模型中,每一个channel需要绑定到一个固定的EventLoop进行后续的处理,Netty就是根据某一个算法从线程组中选择一个线程进行绑定的。

题2:Java 中 BIO、NIO、AIO 有什么区别?

BIO

Block IO是指同步阻塞式IO,就是平常使用的传统IO,它的特点是模式简单使用方便,并发处理能力低。

服务器实现模式为一个连接一个线程,即客户端有连接请求时服务器端就需要启动一个线程进行处理,如果这个连接不做任何事情会造成不必要的线程开销,当然可以通过线程池机制改善。

BIO方式适用于连接数目比较小且固定的架构,这种方式对服务器资源要求比较高,并发局限于应用中,JDK1.4以前的唯一选择,但程序直观简单易理解。

NIO Non IO是指同步非阻塞IO,是传统IO的升级,客户端和服务器端通过Channel(通道)通讯,实现了多路复用。

同步非阻塞,服务器实现模式为一个请求一个线程,即客户端发送的连接请求都会注册到多路复用器上,多路复用器轮询到连接有I/O请求时才启动一个线程进行处理。

NIO方式适用于连接数目多且连接比较短(轻操作)的架构,比如聊天服务器,并发局限于应用中,编程比较复杂,JDK1.4开始支持。

AIO

Asynchronous IO是指NIO的升级,也叫NIO2,实现了异步非堵塞IO,异步IO的操作基于事件和回调机制。

服务器实现模式为一个有效请求一个线程,客户端的I/O请求都是由OS先完成了再通知服务器应用去启动线程进行处理。

AIO方式适用于连接数目多且连接比较长(重操作)的架构,比如相册服务器,充分调用OS参与并发操作,编程比较复杂,JDK7开始支持。

题3:Reactor 模型中有哪几个关键组件?

1、Reactor Reactor在一个单独的线程中运行,负责监听和分发事件,分发给适当的处理程序来对IO事件做出反应。它就像公司的电话接线员,它接听来自客户的电话并将线路转移到适当的联系人

2、Handlers处理程序执行I/O事件要完成的实际事件,类似于客户想要与之交谈的公司中的实际官员。Reactor通过调度适当的处理程序来响应I/O事件,处理程序执行非阻塞操作。

题4:说一说 NIOEventLoopGroup 源码处理过程?

NioEventLoopGroup(其实是MultithreadEventExecutorGroup) 内部维护一个类型为 EventExecutor children [], 默认大小是处理器核数 * 2, 这样就构成了一个线程池,初始化EventExecutor时NioEventLoopGroup重载newChild方法,所以children元素的实际类型为NioEventLoop。

线程启动时调用SingleThreadEventExecutor的构造方法,执行NioEventLoop类的run方法,首先会调用hasTasks()方法判断当前taskQueue是否有元素。如果taskQueue中有元素,执行 selectNow() 方法,最终执行selector.selectNow(),该方法会立即返回。如果taskQueue没有元素,执行 select(oldWakenUp) 方法

select ( oldWakenUp) 方法解决了 Nio 中的 bug,selectCnt 用来记录selector.select方法的执行次数和标识是否执行过selector.selectNow(),若触发了epoll的空轮询bug,则会反复执行selector.select(timeoutMillis),变量selectCnt 会逐渐变大,当selectCnt 达到阈值(默认512),则执行rebuildSelector方法,进行selector重建,解决cpu占用100%的bug。

rebuildSelector方法先通过openSelector方法创建一个新的selector。然后将old selector的selectionKey执行cancel。最后将old selector的channel重新注册到新的selector中。rebuild后,需要重新执行方法selectNow,检查是否有已ready的selectionKey。

接下来调用processSelectedKeys 方法(处理I/O任务),当selectedKeys != null时,调用processSelectedKeysOptimized方法,迭代 selectedKeys 获取就绪的 IO 事件的selectkey存放在数组selectedKeys中, 然后为每个事件都调用 processSelectedKey 来处理它,processSelectedKey 中分别处理OP_READ;OP_WRITE;OP_CONNECT事件。

最后调用runAllTasks方法(非IO任务),该方法首先会调用fetchFromScheduledTaskQueue方法,把scheduledTaskQueue中已经超过延迟执行时间的任务移到taskQueue中等待被执行,然后依次从taskQueue中取任务执行,每执行64个任务,进行耗时检查,如果已执行时间超过预先设定的执行时间,则停止执行非IO任务,避免非IO任务太多,影响IO任务的执行。

每个NioEventLoop对应一个线程和一个Selector,NioServerSocketChannel会主动注册到某一个NioEventLoop的Selector上,NioEventLoop负责事件轮询。

Outbound事件都是请求事件, 发起者是Channel,处理者是unsafe,通过Outbound事件进行通知,传播方向是tail到head。Inbound 事件发起者是unsafe,事件的处理者是 Channel, 是通知事件,传播方向是从头到尾。

内存管理机制,首先会预申请一大块内存Arena,Arena由许多Chunk组成,而每个Chunk默认由2048个page组成。Chunk通过AVL树的形式组织Page,每个叶子节点表示一个Page,而中间节点表示内存区域,节点自己记录它在整个Arena中的偏移地址。当区域被分配出去后,中间节点上的标记位会被标记,这样就表示这个中间节点以下的所有节点都已被分配了。大于8k的内存分配在poolChunkList中,而PoolSubpage用于分配小于8k的内存,它会把一个page分割成多段,进行内存分配。

ByteBuf的特点:支持自动扩容(4M),保证put方法不会抛出异常、通过内置的复合缓冲类型,实现零拷贝(zero-copy);不需要调用flip()来切换读/写模式,读取和写入索引分开;方法链;引用计数基于AtomicIntegerFieldUpdater用于内存回收;PooledByteBuf采用二叉树来实现一个内存池,集中管理内存的分配和释放,不用每次使用都新建一个缓冲区对象。UnpooledHeapByteBuf每次都会新建一个缓冲区对象。

题5:Netty 和 Java NIO 有什么区别,为什么不直接使用 JDK NIO 类库?

1、NIO的类库和API繁杂,使用麻烦,你需要熟练掌握Selector,ServerSocketChannel、SocketChannel、ByteBuffer等。

2、需要具备其他的额外技能做铺垫,例如熟悉Java多线程编程。这是因为NIO编程涉及到Reactor模式,你必须对多线程和网络编程非常熟悉,才能写出高质量的NIO程序。

3、可靠性能力补齐,工作量和难度非常大。例如客户端面临断连重连、网络闪断、半包读写、失败缓存、网络拥塞和异常码流的处理等问题,NIO编程的特点是功能开发相对容易,但是可靠性能力补齐的工作量和难度都非常大。

4、JDK NIO的BUG,例如epoll bug,它会导致Selector空轮询,最终导致CPU 100%。官方验证例子基于以上原因,在大多数场景下,不建议直接使用JDK的NIO类库,除非你精通NIO编程或者有特殊的需求。在绝大多数的业务场景中,我们可以使用NIO框架Netty来进行NIO编程,它既可以作为客户端也可以作为服务端,同时支持UDP和异步文件传输,功能非常强大。

题6:Netty 高性能表现在哪些方面?

传输: IO模型在很大程度上决定了框架的性能,相比于bio,netty建议采用异步通信模式,因为nio一个线程可以并发处理N个客户端连接和读写操作,这从根本上解决了传统同步阻塞IO一连接一线程模型,架构的性能、弹性伸缩能力和可靠性都得到了极大的提升。正如代码中所示,使用的是NioEventLoopGroup和NioSocketChannel来提升传输效率。

协议: Netty默认提供了对Google Protobuf的支持,也可以通过扩展Netty的编解码接口,用户可以实现其它的高性能序列化框架。

线程: netty使用了Reactor线程模型,但Reactor模型不同,对性能的影响也非常大,下面介绍常用的Reactor线程模型有三种,分别如下:

1、Reactor单线程模型:单线程模型的线程即作为NIO服务端接收客户端的TCP连接,又作为NIO客户端向服务端发起TCP连接,即读取通信对端的请求或者应答消息,又向通信对端发送消息请求或者应答消息。理论上一个线程可以独立处理所有IO相关的操作,但一个NIO线程同时处理成百上千的链路,性能上无法支撑,即便NIO线程的CPU负荷达到100%,也无法满足海量消息的编码、解码、读取和发送,又因为当NIO线程负载过重之后,处理速度将变慢,这会导致大量客户端连接超时,超时之后往往会进行重发,这更加重了NIO线程的负载,最终会导致大量消息积压和处理超时,NIO线程会成为系统的性能瓶颈。

2、Reactor多线程模型:有专门一个NIO线程用于监听服务端,接收客户端的TCP连接请求;网络IO操作(读写)由一个NIO线程池负责,线程池可以采用标准的JDK线程池实现。但百万客户端并发连接时,一个nio线程用来监听和接受明显不够,因此有了主从多线程模型。

3、主从Reactor多线程模型:利用主从NIO线程模型,可以解决1个服务端监听线程无法有效处理所有客户端连接的性能不足问题,即把监听服务端,接收客户端的TCP连接请求分给一个线程池。因此,在代码中可以看到,我们在server端选择的就是这种方式,并且也推荐使用该线程模型。在启动类中创建不同的EventLoopGroup实例并通过适当的参数配置,就可以支持上述三种Reactor线程模型。

题7:Netty 支持哪些心跳类型设置?

readerIdleTime:为读超时时间(即测试端一定时间内未接受到被测试端消息)。

writerIdleTime:为写超时时间(即测试端一定时间内向被测试端发送消息)。

allIdleTime:所有类型的超时时间。

题8:Netty 都有哪些特点?

1、Netty是一款基于NIO(Nonblocking IO,非阻塞IO)开发的网络通信框架,对比于BIO(Blocking I/O,阻塞IO),他的并发性能得到了很大提高。

2、Netty的传输依赖于零拷贝特性,尽量减少不必要的内存拷贝,实现了更高效率的传输。

3、Netty封装了NIO操作的很多细节,统一的API,支持多种传输类型,阻塞和非阻塞的,提供了易于使用调用接口。

4、Netty简单而强大的线程模型。

5、Netty自带编解码器解决TCP粘包/拆包问题。

6、Netty自带各种协议栈。

7、Netty真正的无连接数据包套接字支持。

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

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

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

10、Netty成熟稳定,经历了大型项目的使用和考验,而且很多开源项目都使用到了Netty, 比如经常接触的Dubbo、RocketMQ等等。

题9:Netty 核⼼组件有哪些?分别有什么作⽤?

Channel:Netty对网络操作的抽象,包括了一些常见的网络IO操作,比如read,write等等,最常见的实现类:NioServerSocketChannel和NioSocketChannel,对应Bio中的ServerSocketChannel和SocketChannel。

EventLoop:负责监听网络事件并调用事件处理器进行相关的处理。

ChannelFuture:Netty是异步的,所有操作都可以通过ChannelFuture来实现绑定一个监听器然后执行结果成功与否的业务逻辑,或者把通过调用sync方法,把异步方法变成同步的。

ChannelHandler和ChannelPipeline:Netty底层的处理器是一个处理器链,链条上的每一个处理器都可以对消息进行处理,选择继续传递或者到此为止,一般我们业务会实现自己的解码器/心跳处理器和实际的业务处理Handler,然后按照顺序绑定到链条上。

题10:Java NIO 包括哪些组成部分?

Buffer:与Channel进行交互,数据是从Channel读入缓冲区,从缓冲区写入Channel中的。

flip方法 :反转此缓冲区,将position给limit,然后将position置为0,其实就是切换读写模式。

clear方法 :清除此缓冲区,将position置为0,把capacity的值给limit。

rewind方法 :重绕此缓冲区,将position置为0。

DirectByteBuffer可减少一次系统空间到用户空间的拷贝。但Buffer创建和销毁的成本更高,不可控,通常会用内存池来提高性能。直接缓冲区主要分配给那些易受基础系统的本机I/O操作影响的大型、持久的缓冲区。如果数据量比较小的中小应用情况下,可以考虑使用heapBuffer,由JVM进行管理。

Channel:表示IO源与目标打开的连接,是双向的,但不能直接访问数据,只能与Buffer 进行交互。通过源码可知,FileChannel的read方法和write方法都导致数据复制了两次。

Selector可使一个单独的线程管理多个Channel,open方法可创建Selector,register方法向多路复用器器注册通道,可以监听的事件类型:读、写、连接、accept。注册事件后会产生一个SelectionKey:它表示SelectableChannel 和Selector 之间的注册关系,wakeup方法:使尚未返回的第一个选择操作立即返回,唤醒的。

原因是:注册了新的channel或者事件;channel关闭,取消注册;优先级更高的事件触发(如定时器事件),希望及时处理。

Selector在Linux的实现类是EPollSelectorImpl,委托给EPollArrayWrapper实现,其中三个native方法是对epoll的封装,而EPollSelectorImpl. implRegister方法,通过调用epoll_ctl向epoll实例中注册事件,还将注册的文件描述符(fd)与SelectionKey的对应关系添加到fdToKey中,这个map维护了文件描述符与SelectionKey的映射。

fdToKey有时会变得非常大,因为注册到Selector上的Channel非常多(百万连接);过期或失效的Channel没有及时关闭。fdToKey总是串行读取的,而读取是在select方法中进行的,该方法是非线程安全的。

Pipe:两个线程之间的单向数据连接,数据会被写到sink通道,从source通道读取。

NIO的服务端建立过程:Selector.open():打开一个Selector;ServerSocketChannel.open():创建服务端的Channel;bind():绑定到某个端口上。并配置非阻塞模式;register():注册Channel和关注的事件到Selector上;select()轮询拿到已经就绪的事件。

题11:nioeventloopgroup-默认构造方法启动几个线程

题12:同步和异步有什么区别

题13:什么是长连接

题14:netty-粘包和拆包是如何处理的有哪些实现

题15:netty-中有那些重要组件

题16:jdk-原生-nio-程序有什么问题

题17:bootstrap-和-serverbootstrap-了解过吗

题18:netty-中有哪些线程模型

题19:reactor-线程模型消息处理流程

题20:netty-中如何解决-tcp-粘包和拆包问题

题21:netty-有哪些优势

题22:阻塞和非阻塞有什么区别

题23:reactor-线程模型有几种模式

题24:netty-有哪些应用场景

题25:默认情况-netty-起多少线程何时启动

大厂面试题

大厂面试题

大厂面试题

Java
1
https://gitee.com/zhuxinyu114/ebooks.git
git@gitee.com:zhuxinyu114/ebooks.git
zhuxinyu114
ebooks
Ebooks
master

搜索帮助

53164aa7 5694891 3bd8fe86 5694891