收集几十位大厂面试者的面试题及见解-Netty模块

2021-07-18  本文已影响0人  想回家种地的程序员

1 简述BIO、NIO和AIO。

BIO:客户端并发数和后端的线程数是一比一,线程的创建和销毁很消耗系统资源。并发量大,服务器性能下降,会出现栈和堆溢出错误。当前线程创建连接后,如果没有操作进入阻塞操作,浪费服务器资源。
NIO:通过多路复用器一个线程处理多个通道,避免多线程之间的上下文切换导致系统开销过大。并且通道中有了事件,才能进行读写操作。减少系统开销。
AIO:异步IO,用户线程通过系统调用,告知内核启动某个IO操作(数据准备、数据复制),完成后通知用户程序,执行后续的操作。但是依赖操作系统底层,而且linux系统支持不完善。

2 为什么使用Netty进行项目开发?

  1. Netty是基于NIO实现的,实现了阻塞和非阻塞Socket。而且对各种协议API进行统一封装,简化开发。
  2. Netty只依赖JDK底层api,不需要引入额外依赖。
  3. 社区活跃,发现bug可以快速修复。
  4. 高度可定制线程模型,单线程、一个或多个线程池。
  5. 网络通信更加高性能、低延迟、尽可能的减少不必要的内存拷贝。
  6. NIO可能会导致Selector空轮询问题,官网JDK1.6修复该问题,只是概率低点。

3 什么是Reactor线程模型?

不是Java和Netty专有的,是一种并发编程模型,是一种思想。
Reactor有三种角色,Reactor负责监听和分配事件。Acceptor处理客户端新连接,并分派请求到处理链中。Handler将自身和事件绑定,执行读写操作。
Reactor线程模型有三种,单线程、多线程、主从多线程。

4 介绍下单线程、多线程、主从多线程模型。

单线程:
Reactor充当多路复⽤器⻆⾊,监听多路连接的请求,由单线程完成 。Reactor收到客户端发来的请求时,如果是新建连接通过Acceptor完成,其他的请求由Handler完成(读写操作)。
试用一些简单业务逻辑和并发量不高的地方。没有线程之间切换和线程之间通信问题。但是Reactor负担过重,导致系统性能下降,如果当前线程进入死循环,整个系统不可用。
多线程:
和单线程之间区别是,Handler只负责响应用户请求,真正读写操作在其他线程完成。降低Reactor的性能开销,充分利⽤CPU资源,从⽽更专注的做事件分发⼯作。提升整 个应⽤的吞吐。
多线程数据共享和访问⽐较复杂。如果⼦线程完成业务处理后,把结果传递给主线程Reactor进⾏ 发送,就会涉及共享数据的互斥和保护机制。 Reactor承担所有事件的监听和响应,只在主线程中运⾏,可能会存在性能问题。例如并发百万客户端连接,或者服务端需要对客户端握⼿进⾏安全认证,但是认证本身⾮常损耗性能。
主从多线程模型:
MainReactor负责监听server socket,处理⽹络IO连接建⽴,将建⽴的socketChannel指 定注册给SubReactor。
SubReactor主要完成和建⽴起来的socket的数据交互和事件业务处理操作。
响应快,不必为单个同步事件所阻塞,虽然Reactor本身依然是同步的。 可扩展性强,可以⽅便地通过增加SubReactor实例个数来充分利⽤CPU资源。 可复⽤性⾼,Reactor模型本身与具体事件处理逻辑⽆关,具有很⾼的复⽤性。

总结:在netty中多Reactor三种模型都有很好的支持,一般情况下,服务端采用主从架构模型。

5 IO模型:阻塞IO模型、非阻塞IO模型、select、epoll线程模型。

阻塞IO模型
创建socket,fd绑定监听,调用系统的accept()函数等待系统内核响应,如果内核没有接受连接,一直阻塞中,接受客户端数据函数也是阻塞中,发送信息也是等待客户端信息发送。
非阻塞IO模型
调用系统的连接函数、接受客户端数据函数、发送数据函数都变成异步了,不用等待内核响应,直接执行下一步。应用程序要得到响应不断的轮询调用。
select、epoll线程模型
redis模块中已总结过,https://www.jianshu.com/p/fe6d9f7d9e9b

6 Netty中的核心组件

ChannerHandler、Channel、EventLoop、EventLoopGroup、ChannelPipeline、Bootstrap、Future
Channel:每个客户端连接都会创建一个Channel,它负责基本的IO操作。
EventLoop:用于监控和协调事件,每个EventLoop会占用一个Thread,当前线程处理发生在EventLoop上所有IO操作。
EventLoopGroup:用于生成EventLoop。服务端需要两个,客户端需要一个。因为服务端需要建立连接和业务处理。
ChannelHandler:对于数据的⼊站和出站的业务逻辑的编写都是在ChannelHandler中完成的。
ChannelPipeline:channel进行数据传递时候,需要进行编码和节码等一系列操作操作。ChannelPipeline是对ChannelHandler管理。
Bootstrap:将各个组件串起来,并绑定端口,启动服务。
Future:了⼀种在操作完成时通知应⽤程序。

7 Netty零拷贝解释下?

Bytebuf 使⽤的是⽤池化的Direct Buffer类型使⽤的堆外内存,不需要进⾏字节缓冲区的⼆次拷 ⻉,如果使⽤堆内存,JVM会先拷⻉到堆内,再写⼊Socket,就多了⼀次拷⻉。 CompositeByteBuf将多个ByteBuf封装成⼀个ByteBuf,在添加ByteBuf时不需要进程拷⻉。 Netty的⽂件传输类DefaultFileRegion的transferTo⽅法将⽂件发送到⽬标channel中,不需要进 ⾏循环拷⻉,提升了性能。

8 ByteBuf 释放问题

手动释放:ReferenceCountUtil.release(byteBuf); 进⾏释放。

自动释放:
HeadHandler出站自动释放。
⼊站的TailHandler,TailHandler放在最后面,每个处理器将消息下穿。
继承SimpleChannelInboundHandler自动释放。

9 知道哪些编解码器?

在netty开发中定义编解码器需要实现ByteToMessageDecoder、MessageToByteEncoder。

对象编解码器:JDk自带的编解码器、Hessian编解码器。
基于Redis协议的编解码器。字符串编解码器。

10 Netty 拆包粘包的实质?TCP通信的粘包和拆包怎么处理?

在Netty的应用层,按照 ByteBuf 为单位来发送/读取数据,但是到了底层操作系统仍然是按照字节流发送数据。因此,从底层到应用层,需要进行二次拼装。操作系统底层,是按照字节流的方式读入,到了 Netty 应用层面,需要二次拼装成 ByteBuf。在Netty 层面,拼装成ByteBuf时,就是对底层TCP缓冲的读取,这里就有问题了。上层应用层每次读取底层缓冲的数据容量是有限制的,当TCP底层缓冲数据包比较大时,将被分成多次读取,造成断包,在应用层来说,就是半包。其次,如果上层应用层一次读到多个底层缓冲数据包,就是粘包。

在发送消息包时候,在头部加上两个字节,存储发送数据字节长度。在bytebuf获取数据时候,先读取两个字节,即字节长度len1,然后从bytebuf中读取len1长度的字节。

11 Netty服务启动流程?

创建服务端channel,


在这里插入图片描述 在这里插入图片描述

初始化服务端channel,


image
注册selector,
在这里插入图片描述

绑定端口。


在这里插入图片描述
在这里插入图片描述

12 连接过程刨析?

新连接接入:


在这里插入图片描述
在这里插入图片描述

注册读事件


在这里插入图片描述
在这里插入图片描述

13 使用Netty如何优化?

2. 使⽤EventLoop的任务调度
直接放⼊channel所对应的EventLoop的执⾏队列,⽽直接使⽤channel.writeAndFlush(data),会导致线程的切换。
3. 减少ChannelPipline的调⽤长度
ctx.channel().writeAndFlush(msg); ,所有的handler都会执行一遍。ctx.writeAndFlush(msg),从当前的handler一直执行到pipline尾部。
4. 减少ChannelHandler的创建
如果channelhandler是⽆状态的(即不需要保存任何状态参数),那么使⽤Sharable注解,并在 bootstrap时只创建⼀个实例,减少GC。否则每次连接都会new出handler对象。
5. ⼀些配置参数的设置
bossGroup只是需要配置一个,因为ServerSocketChannel只会注册到一个eventLoop上,⽽这个eventLoop只会有⼀个线程在运⾏。workGroup集器核数两倍,即发挥虚拟机内核优势,又防止线程上下文切换开销。
响应时间有⾼要求的场景,禁⽤nagle算法,不等待,⽴即发送。

14 我说Netty不是真正的“异步IO”,你认同吗?

认同。因为使用的IO模型是采用epoll,应用端不断循环调用epoll对象的消息队列,当消息队列中有事件消息,然后应用端对事件消息一个个处理。所以是同步操作。但是由于也是一个线程处理多个客户端的读写操作,类似异步感觉。

15 Netty的IO模型,Reactor模式,Handler线程一定要新起一个线程来进行数据库操作吗?

不一定,因为handler进行业务操作是在workeGroup线程池操作,这个线程池线程数是内核的两倍。所以Handler进行业务操作,如果线程数没有达到核心线程数,则会创建一个新的线程执行。如果达到了核心线程数,则会使用之前的线程执行。

16 Netty 中责任链?

就是ChannelPipeline,用来将每一个handler串成链表,然后进行管理。保证了进行消息传递时候,执行handler顺序。

17 长连接和短链接描述下。

TCP在进行读写前,需要server和client建立连接。建立连接的过程就是三次握手四次挥手。消耗网络资源和有事件延迟。
短链接:就是客户端和服务端读写完毕后,释放连接。再次读取,需要从新建立连接。
长连接:每次读写之后,连接不断开。后续读写依旧使用这个连接。

18 Netty的长连接/心跳机制有了解?

在TCP保持长连接时候,由于网络原理或者其他异常。client和server没有交互,无法发现对方掉线。引入心跳机制。
心跳机制原理,在client和server在一定时间内没有数据交互,服务端或者客户端发送一个特殊数据包给对象,对方立刻回应,代表连接中。
TCP实际上自带长连接选项,本身就有心跳包机制。但是TCP协议层面长连接灵活性不够,所以一般是在应用层自定义心跳机制,在netty中实现,则是通过IdleStateHandler核心类实现。

19 一个请求进入netty具体用了哪些类?

如果是一个连接请求,首先是被EventLoopGroup产生的EventLoop监控到是连接事件,然后由Acceptor进行连接创建。
如果是一个读/写请求,被工作的EventLoopGroup产生EventLoop监控到,然后进入Channel,进行执行链ChannelPipeline,执行每一个ChannelHandler。

20 Netty怎么处理http请求,tcp和http区别,http和https区别,怎么加密,每次都需要这样吗?

21 netty怎么解决IO阻塞在业务代码上?

22 在项目中有没有作过限流?

23 Netty guice?

24 Netty线程池中的线程建立连接之后,这条连接是不是始终属于这个请求,对于Netty来说是不是只占用服务端的一个套接字,了解zero copy嘛?

25 Netty 的实现(nio 实现)?

26 ProtoBuf?

27 Netty主流程源码描述一下 和NIO有什么区别?

28 Netty异步编程怎么做的?

29 项目里用到channle重用,是怎么实现?

30 Java Reactor在执行阻塞I/O时该怎么操作?

31 Netty的容错性?

32 Netty在不同操作系统上有何不同?

33 NIO是怎么调度的?

34 Netty的I/O线程堵住了会怎么样?怎么解决?

上一篇下一篇

猜你喜欢

热点阅读