Netty经典高频面试题!
《Java面试必知必会》系列继续更新,该系列已经更新完Java基础、数据库、Spring、SpringBoot和消息中间件、计算机网络和操作系统部分。公众号:代码界的小白,会更新互联网消息、动态、高频八股、实战项目等。
牛客也可以看到往期更新
《Java面试必知必会》往期推荐阅读!
关于Netty这部分,如果面试被问到,那就有点难度了,不过一些基础的知识还是可以掌握一下的,如果自己做的项目里牵涉到了Netty相关的知识,那你就一定要好好准备了!
1、Netty是什么?
Netty提供异步的、事件驱动的网络应用程序框架和工具,用以快速开发高性能、高可靠性的网络服务器和客户端程序。
也就是说,Netty 是一个基于NIO的客户、服务器端的编程框架,使用Netty 可以确保你快速和简单的开发出一个网络应用,例如实现了某种协议的客户、服务端应用。Netty相当于简化和流线化了网络应用的编程开发过程,例如:基于TCP和UDP的socket服务开发。
“快速”和“简单”并不用产生维护性或性能上的问题。Netty 是一个吸收了多种协议(包括FTP、SMTP、HTTP等各种二进制文本协议)的实现经验,并经过相当精心设计的项目。最终,Netty 成功的找到了一种方式,在保证易于开发的同时还保证了其应用的性能,稳定性和伸缩性。【百度百科】
总结:Netty是 一个异步事件驱动的网络应用程序框架,用于快速开发可维护的高性能协议服务器和客户端。Netty是基于Nio的,封装了jdk的Nio,使用起 来更加方法灵活。
追问:Netty 特点是什么?
回答:三大特点:高并发、传输快和封装好
高并发:Netty 是一款基于 NIO(Nonblocking IO,非阻塞IO)开发的网络通 信框架,对比于 BIO(Blocking I/O,阻塞IO),他的并发性能得到了很大提高。
传输快:Netty 的传输依赖于零拷贝特性,尽量减少不必要的内存拷贝,实现了 更高效率的传输。
封装好:Netty 封装了 NIO 操作的很多细节,提供了易于使用调用接口。
追问:这里面试官可能会问你零拷贝!
零拷贝是非常重要的知识点,建议看看阿里调优手册!【微信公众号:代码界的小白】后台回复:阿里调优】
零拷贝是一种避免多次内存复制的技术,用来优化读写I/O操作。
追问:Netty 的应用场景有哪些
回答:Netty可构建高性能、低延时的各种Java中间件,例如MQ、分布式服务框架、ESB消息总线等,Netty主要作为基础通信框架提供高性能、低子延时的通信服务;(阿里分布式服务框架 Dubbo,默认使用 Netty 作为基础通信组 件,还有 RocketMQ 也是使用 Netty 作为通讯的基础。)
2、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线程模型。
- BIO (Blocking I/O): 同步阻塞 I/O 模式,数据的读取写入必须阻塞在一个线程内等待其完成。在活动连接数不是特别高(小于单机 1000)的情况下,这种模型是比较不错的,可以让每一个连接专注于自己的 I/O 并且编程模型简单,也不用过多考虑系统的过载、限流等问题。线程池本身就是一个天然的漏斗,可以缓冲一些系统处理不了的连接或请求。但是,当面对十万甚至百万级连接的时候,传统的 BIO 模型是无能为力的。因此,我们需要一种更高效的 I/O 处理模型来应对更高的并发量。
- NIO (Non-blocking/New I/O): NIO 是一种同步非阻塞的 I/O 模型,在 Java 1.4 中引入了 NIO 框架,对应 java.nio 包,提供了 Channel , Selector,Buffer 等抽象。NIO 中的 N 可以理解为 Non-blocking,不单纯是 New。它支持面向缓冲的,基于通道的 I/O 操作方法。 NIO 提供了与传统 BIO 模型中的
Socket
和ServerSocket
相对应的SocketChannel
和ServerSocketChannel
两种不同的套接字通道实现,两种通道都支持阻塞和非阻塞两种模式。阻塞模式使用就像传统中的支持一样,比较简单,但是性能和可靠性都不好;非阻塞模式正好与之相反。对于低负载、低并发的应用程序,可以使用同步阻塞 I/O 来提升开发速率和更好的维护性;对于高负载、高并发的(网络)应用,应使用 NIO 的非阻塞模式来开发- AIO (Asynchronous I/O): AIO 也就是 NIO 2。在 Java 7 中引入了 NIO 的改进版 NIO 2,它是异步非阻塞的 IO 模型。异步 IO 是基于事件和回调机制实现的,也就是应用操作之后会直接返回,不会堵塞在那里,当后台处理完成,操作系统会通知相应的线程进行后续的操作。AIO 是异步 IO 的缩写,虽然 NIO 在网络操作中,提供了非阻塞的方法,但是 NIO 的 IO 行为还是同步的。对于 NIO 来说,我们的业务线程是在 IO 操作准备好时,得到通知,接着就由这个线程自行进行 IO 操作,IO 操作本身是同步的。查阅网上相关资料,我发现就目前来说 AIO 的应用还不是很广泛,Netty 之前也尝试使用过 AIO,不过又放弃了。【guide哥】
3、Netty的线程模型知道吗?
回答:Reactor模式是基于事件驱动开发的,核心组成部分包括Reactor和线程池,其中Reactor负责监听和分配事件,线程池负责处理事件,而根据Reactor的数量和线程池的数量,又将Reactor分为三种模型:
- 单线程模型 (单Reactor单线程)
- 多线程模型 (单Reactor多线程)
- 主从多线程模型 (多Reactor多线程)
Netty通过Reactor模型基于多路复用器接收并处理用户请求,内部实现了两个 线程池,boss线程池和work线程池,其中boss线程池的线程负责处理请求的 accept事件,当接收到accept事件的请求时,把对应的socket封装到一个NioSocketChannel中,并交给work线程池,其中work线程池负责请求的read 和write事件,由对应的Handler处理。
三种模型详细介绍(挑重点记忆,我面试没遇到过让详细说的。)
- 单线程模型:所有I/O操作都由一个线程完成,即多路复用、事件分发和处理都 是在一个Reactor线程上完成的。既要接收客户端的连接请求,向服务端发起连 接,又要发送/读取请求或应答/响应消息。一个NIO 线程同时处理成百上千的 链路,性能上无法支撑,速度慢,若线程进入死循环,整个程序不可用,对于高 负载、大并发的应用场景不合适。
- 多线程模型:有一个NIO 线程(Acceptor) 只负责监听服务端,接收客户端的 TCP 连接请求;NIO 线程池负责网络IO 的操作,即消息的读取、解码、编码和 发送;1 个NIO 线程可以同时处理N 条链路,但是1 个链路只对应1 个NIO 线 程,这是为了防止发生并发操作问题。但在并发百万客户端连接或需要安全认证 时,一个Acceptor 线程可能会存在性能不足问题。
- 主从多线程模型:Acceptor 线程用于绑定监听端口,接收客户端连接,将 SocketChannel 从主线程池的Reactor 线程的多路复用器上移除,重新注册到 Sub 线程池的线程上, 用于处理I/O 的读写等操作,从而保证mainReactor只负 责接入认证、握手等操作;
4、TCP 粘包/拆包的原因及解决方法?
回答:TCP是以流的方式来处理数据,一个完整的包可能会被TCP拆分成多个包进行发送,也可能把小的封装成一个大的数据包发送。
TCP粘包/分包的原因:
应用程序写入的字节大小大于套接字发送缓冲区的大小,会发生拆包现象,而应用程序写入数据小于套接字缓冲区大小,网卡将应用多次写入的数据发送到网络 上,这将会发生粘包现象;
进行MSS大小的TCP分段,当TCP报文长度-TCP头部长度>MSS的时候将发生拆 包;以太网帧的payload(净荷)大于MTU(1500字节)进行ip分片。
解决方法
消息定长:FixedLengthFrameDecoder类包尾增加特殊字符分割:
- 行分隔符类:LineBasedFrameDecoder
- 或自定义分隔符类 :DelimiterBasedFrameDecoder
将消息分为消息头和消息体:LengthFieldBasedFrameDecoder类。分为有头 部的拆包与粘包、长度字段在前且有头部的拆包与粘包、多扩展头部的拆包与粘 包。
5、说说Netty的零拷贝?
回答:
- Netty的接收和发送ByteBuffer采用DIRECT BUFFERS,使用堆外直接内存进行Socket读写,不需要进行字节缓冲区的二次拷贝。如果使用传统的堆内存(HEAP BUFFERS)进行Socket读写,JVM会将堆内存Buffer拷贝一份到直接内存中,然后才写入Socket中。相比于堆外直接内存,消息在发送过程中多了一次缓冲区的内存拷贝。
- Netty提供了组合Buffer对象,可以聚合多个ByteBuffer对象,用户可以像操作一个Buffer那样方便的对组合Buffer进行操作,避免了传统通过内存拷贝的方式将几个小Buffer合并成一个大的Buffer。
- Netty的文件传输采用了transferTo方法,它可以直接将文件缓冲区的数据发送到
看到这里,记得帮忙点赞、收藏和转发~
#Java开发##Java##学习路径#