Netty面试经典问题

目录

Netty是怎么实现高性能设计的?

简单介绍一下对于Netty的了解

Netty的高性能表现在哪些方面

介绍一下Java中的几种IO模型

一个通俗例子读懂BIO、NIO、AIO

BIO与NIO的区别

Netty的线程模型

什么是零拷贝

Netty中的模块组件:

Netty 中有哪种重要组件?

简单聊聊:Netty的线程模型的三种使用方式? Rectory


Netty 已经有了成百上千的分布式中间件、各种开源项目以及各种商业项目的应用。例如火爆 的 Kafka、RocketMQ 等消息中间件、火热的 ElasticSearch 开源搜索引擎、大数据处理 Hadoop 的 RPC 框架 Avro、主流的分布式通信框架 Dubbo,它们都使用了 Netty。

Netty是怎么实现高性能设计的?

高性能设计的核心: 巧妙的结合高性能IO模型 和 线程模型 ,相得益彰,达到了 高性能 、高吞吐、低延迟、低消耗的目标。

其I/O模型 高性能epoll/select 模型

其 线程模型为 多线程 reactor 反应器模型

简单介绍一下对于Netty的了解

1.Netty 是一个异步的、基于事件驱动的网络应用框架

2.对JDK中的NIO做了增强

3.有很多特点:零拷贝、多种通信协议HTTP/WEBSocker,粘包分包自动化处理等

Netty的高性能表现在哪些方面

1.i/o线程模型:同步非阻塞,用最少的资源做更多的事情

2.内存零拷贝

3.内存池、线程池的设计

介绍一下Java中的几种IO模型

BIO:一个连接一个线程,客户端有连接请求时服务器端就需要启动一个线程进行处理。线程开销大。

伪异步IO:将请求连接放入线程池,一对多,但线程还是很宝贵的资源。

NIO:一个请求一个线程,但客户端发送的连接请求都会注册到多路复用器上,多路复用器轮询到连接有I/O请求时才启动一个线程进行处理。

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

一个通俗例子读懂BIO、NIO、AIO

同步阻塞(blocking-IO)简称BIO
同步非阻塞(non-blocking-IO)简称NIO
异步非阻塞(asynchronous-non-blocking-IO)简称AIO
一个经典生活的例子:

小明去吃同仁四季的椰子鸡,就这样在那里排队,等了一小时,然后才开始吃火锅。(BIO)
小红也去同仁四季的椰子鸡,她一看要等挺久的,于是去逛会商场,每次逛一下,就跑回来看看,是不是轮到她了。于是最后她既购了物,又吃上椰子鸡了。(NIO)
小华一样,去吃椰子鸡,由于他是高级会员,所以店长说,你去商场随便逛会吧,等下有位置,我立马打电话给你。于是小华不用干巴巴坐着等,也不用每过一会儿就跑回来看有没有等到,最后也吃上了美味的椰子鸡(AIO)

BIO与NIO的区别

BIO是面向流的,NIO是面向缓冲区的;

BIO的各种流是阻塞的。而NIO是非阻塞的;

BIO的Stream是单向的,而NIO的channel是双向的。

Netty的线程模型

netty通过Reactor模型基于多路复用器接收并处理用户请求,内部实现了两个线程池,Boss线程池和Work线程池,其中Boss线程池的线程负责处理请求accept事件,当接收到accept事件的请求是,把对应的socket封装到一个channel中,并交给work线程池,其中work线程池负责Read和Write事件,由对应的Handler处理

Netty主要基于主从Reactors多线程模型,做了一定的修改,其中主从Reactor多线程模型有多 个Reactor:MainReactor和SubReactor:

  • MainReactor负责客户端的连接请求,并将请求转交给SubReactor
  • SubReactor负责相应通道的IO读写请求
  • 非IO请求(具体逻辑处理)的任务则会直接写入队列,等待worker threads进行处理

什么是零拷贝

Netty 的零拷贝主要包含三个方面:
1、Nerty 的接收和发送 ByteBuffer采用 DIRECT BUFFERS,使用堆外直接内存Socket
读写,不需要进行字节缓冲区的二次拷贝。如果使用传统的堆内存(HEAP BUFFERS) 进行
Socket读写,JVM会将堆内存Buffer拷贝一次到直接内存中,然后才写入Socket中。相比于堆外直接内存,消息在发送过程中多了一次缓冲区的内存拷贝。

2、Netty提供了组合Buffer对象,可以聚合多个ByteBuffer对象,用户可以像操作一个Buffer那样方便的对组合 Buffer 进行操作,避免了传统通过内存拷贝的方式将几个小Buufer 合并成一个大的 Buffer。
3、Netty的文件传输采用transferTo 方法,它可以直接将文件缓沖区的数据发送到目标 Channel,避免了传统通过循环 write 方式导致的内存拷贝问题。

Netty中的模块组件:

BootStrap/ServerBootStrap

Future/ChannelFuture

Channel

Selector

EventLoop

Pipline

Netty 中有哪种重要组件?

Channel:Netty 网络操作抽象类,它除了包括基本的 I/O 操作,如 bind、connect、read、write 等。

EventLoop:主要是配合 Channel 处理 I/O 操作,用来处理连接的生命周期中所发生的事情。

ChannelFuture:Netty 框架中所有的 I/O 操作都为异步的,因此我们需要 ChannelFuture 的 addListener()注册一个 ChannelFutureListener 监听事件,当操作执行成功或者失败时,监听就会自动触发返回结果。

ChannelHandler:充当了所有处理入站和出站数据的逻辑容器。ChannelHandler 主要用来处理各种事件,这里的事件很广泛,比如可以是连接、数据接收、异常、数据转换等。

ChannelPipeline:为 ChannelHandler 链提供了容器,当 channel 创建时,就会被自动分配到它专属的 ChannelPipeline,这个关联是永久性的。

简单聊聊:Netty的线程模型的三种使用方式? Rectory

单线程模型 :

一个线程需要执行处理所有的 accept、read、decode、process、encode、send 事件。

对于高负载、高并发,并且对性能要求比较高的场景不适用。

对应到 Netty 代码是下面这样的

使用 NioEventLoopGroup 类的无参构造函数设置线程数量的默认值就是 **CPU 核心数 2

//1.eventGroup既用于处理客户端连接,又负责具体的处理。
EventLoopGroup eventGroup = new NioEventLoopGroup(1); 

//2.创建服务端启动引导/辅助类:
ServerBootstrap ServerBootstrap b = new ServerBootstrap(); 
    boobtstrap.group(eventGroup, eventGroup) 
    //......

多线程模型:

一个 Acceptor 线程只负责监听客户端的连接,一个 NIO 线程池负责具体处理:accept、read、

decode、process、encode、send 事件。满足绝大部分应用场景,并发连接量不大的时候没啥问题,但是遇到并发连接大的时候就可能会出现问题,成为性能瓶颈。

对应到 Netty 代码是下面这样的:

// 1.bossGroup 用于接收连接,workerGroup 用于具体的处理 
EventLoopGroup bossGroup = new NioEventLoopGroup(1); 
EventLoopGroup workerGroup = new NioEventLoopGroup(); 
    try { 
        //2.创建服务端启动引导/辅助类:
        ServerBootstrap ServerBootstrap b = new ServerBootstrap(); 
        //3.给引导类配置两大线程组,确定了线程模型 
        b.group(bossGroup, workerGroup) 
            //......

主从多线程模型:

从一个 主线程 NIO 线程池中选择一个线程作为 Acceptor 线程,绑定监听端口,接收客户端连接的连接,其他线程负责后续的接入认证等工作。连接建立完成后,Sub NIO 线程池负责具体处理 I/O 读写。

如果多线程模型无法满足你的需求的时候,可以考虑使用主从多线程模型 。

// 1.bossGroup 用于接收连接,workerGroup 用于具体的处理 
EventLoopGroup bossGroup = new NioEventLoopGroup(); 
EventLoopGroup workerGroup = new NioEventLoopGroup();
    try { 
        //2.创建服务端启动引导/辅助类:
        ServerBootstrap ServerBootstrap b = new ServerBootstrap(); 
        //3.给引导类配置两大线程组,确定了线程模型 
        b.group(bossGroup, workerGroup) 
            //......

未完待续~ 

猜你喜欢

转载自blog.csdn.net/promsing/article/details/128064517