Netty常见面试题

大家好,我是山茶!

关于我:我是山茶,一个专注于技术的菜鸟。你懂的越多,就懂得不懂的越多。关注➕我,一起了解更多知识。

前言

Netty 是 一个异步事件驱动 的网络应用程序框架,用于快速开发可维护的高性能协议服务器和客户端。所以在常见的面试中总是会被问到其中的一二,因此在下面总结一些常见的Netty面试题目。

1.NIO、BIO、AIO 三者间的区别是什么?

NIO(Non-blocking I/O New I/O) :同步非阻塞IO,由三大核心组件Channel(通道)、ByteBuffer(缓冲区)、Selector(选择器)组成,使用selector多路复用技术,一个线程处理多个Channel请求,多个Channel注册在selector多路复用器上,selector轮询并根据四种标识(connect、accept、read、write)进行区分处理。JDK1.4开始引入使用,数据在通道中进行读取操作,线程在发送完或读写数据时,会继续做其他事情,不会等待阻塞。且Channel是双向的,可以进行双向操作,不似BIO,要么是输入流,要么是输出流。

BIO(Blocking I/O) :同步阻塞IO,线程发起IO请求后,一直阻塞IO,直到缓冲区数据就绪后,再进入下一步操作。一个连接对应一个线程,每创建一个连接就会同步创建一个线程。JDK1.4前使用,数据流形式数据传输方式,数据流以字节、字符流形式体现,且只能为输入流或输出流,为单向流形态。

AIO( Asynchronous I/O ) :异步非阻塞IO,是JDK7后引入的NIO 2.0版本。是真正的异步操作,既数据发送完成后直接返回,后续完成既失败或成功的信息由系统回调对应的线程,通知继续进行下一步的操作。本质是让内核系统完成,用户线程只需要告诉内核,当缓冲区就绪后,通知执行我交给你的回调函数。

参考:如何理解BIO、NIO、AIO的区别? - 掘金JAVA BIO与NIO、AIO的区别(容易理解)BIO NIO AIO区别NIO编程及其三大核心原理 | 远方的灯塔 - 专注于服务端技术分享

2.Netty是什么?

  • Netty是基于NIO(Nonblocking I/O,非阻塞IO)的网络应用程序框架,它可以提供对TCP、UDP和文件传输的支持,应用场景较为广泛。
  • 分布式系统中,各个节点的调用需要远程服务调用,高性能RPC框架必不可少,Netty作为异步高性能的通信框架,通常被作为基础通信组件使用,典型的有Dubbo框架******、RocketMQ、Elasticsearch、gRPC等
  • Netty更多的被使用在项目中的统计可以从官网中查看Netty.docs: Related projects

2.Netty有哪些应用场景?

因为Netty是基于NIO实现的,所以在使用场景上,NIO能够做到的Netty也同样可以做到,且青出于蓝胜于蓝,做的会更好

  • RPC框架 基础通信组件:比如阿里分布式服务框架 Dubbo 的 RPC 框架使用 Dubbo 协议进行节点间通信,Dubbo 协议默认使用 Netty 作为基础通信组件,用于实现各进程节点之间的内部通信
  • HTTP服务器开发:可以实现自己的HTTP服务器,FTP服务器,UDP服务器,RPC服务器,WebSocket服务器,Redis的Proxy服务器,MySQL的Proxy服务器等等
  • 即时通信系统 :使用Netty可以实现即时通信聊天的程序,在github、gitee上有很多存在,例如netty聊天室
  • 游戏通信推送:Netty本身提供了 TCP/UDP 和 HTTP 协议栈,便于定制和开发私有协议栈。游戏用户登录、用户聊天等都可以方便的通过 Netty 进行高性能通信

还有很多的其他使用场景,但在工作汇中最常见的场景就是作为一个RPC框架的通信组件以及游戏中的实时通信推送场景。

3.Netty的优势是什么?为什么使用Netty?

  • API 使用简单,开发门槛相对较低;
  • 功能强大,预置了多种编解码功能,支持多种主流协议;
  • 可扩展性强,可以通过 ChannelHandler 对通信框架进行灵活的扩展;
  • 高性能,Netty采用异步、事件驱动的方式处理网络请求。这种处理方式相比传统的同步阻塞I/O模型,能够更好地利用CPU资源,提高系统的吞吐量和响应速度;
  • 社区活跃,Netty是一个开源项目,拥有活跃的社区和广泛的用户群体,因此它在功能上得到了不断的更新和改进,以能够满足不断变化的需求;
  • 经历了大规模的商业应用考验,质量得到验证。在互联网、大数据、网络游戏、企业应用、电信软件等众多行业得到成功商用,证明了它完全满足不同行业的商用标准

综述,Netty能够提高应用程序的性能、可扩展性和可靠性,同时也能提高开发效率和代码质量。因此,Netty被广泛应用于构建高性能的网络应用程序,例如游戏服务器、聊天应用程序、实时数据传输等

4.BIO、NIO、AIO的区别

  • BIO(Blocking IO,一个连接一个线程) :同步阻塞IO,每一个读或者写操作均会启动一个线程进行处理,并且等待上一操作完成才会进行下一个操作。当在少量数据处理丽连接时,不会出现问题,但要处理的数据达到十万级、百万级、甚至千万级连接时,BIO会增加整体的性能功耗,这时BIO无法满足我们高并发的需求,我们需要更好的一种IO模式来处理。
  • NIO(Non Blocking IO或New IO,一个请求一个线程) :同步非阻塞IO,在Java1.4开始支持并使用,对应使用的包是java.nio。NIO使用通道和缓冲区。数据总是从缓冲区写入通道,并从通道读取到缓冲区,且使用selector选择器进行轮询监听。对于高并发、高负载的情况,使用NIO的非阻塞模式进行开发。
  • AIO(Asynchronous IO一个有效请求一个线程) :异步非阻塞IO,又称为NIO2.0,从JDK7开始支持,AIO的使用是基于事件的回调机制实现的,既一个事件发出请求,不会在该位置等待结果阻塞,会直接返回,当后台处理完成后,操作系统会通知对应的线程进行接下来的操作。AIO的使用目前暂时没有NIO的使用广泛,曾经Netty也是用AIO,后续则是切换为NIO

5 . Netty为什么选择NIO?不选择AIO

想知道这个的原因首先需要了解什么事NIO、什么是AIO,他们的底层使用是什么样的,底层调用的逻辑是什么样的。

  • NIO模型:同步非阻塞型IO, NIO有同步阻塞和同步非阻塞两种模式,一般讲的是同步非阻塞,服务器实现模式为一个请求一个线程,但客户端发送的连接请求都会注册到多路复用器上,多路复用器轮询到连接有I/O请求时才启动一个线程进行处理。
  • AIO模型:异步非阻塞型IO,( AIO又称NIO2.0,JDK7才开始支持)服务器实现模式为一个有效请求一个线程,客户端的I/O请求都是由OS先完成了再通知服务器应用去启动线程进行处理。

至于没有选择AIO的原因,作者原文

Not faster than NIO (epoll) on unix systems (which is true) :在unix上并没有比NIO快

There is no daragram suppport :不支持数据报

Unnecessary threading model (too much abstraction without usage):太多没有用的抽象

主要有三点:

    • Netty不注重windows的使用,而在linux上的使用,AIO在windows实现成熟,但windows很少用作服务器
    • Linux下AIO相比较NIO性能提升不明显
    • AIO还有个缺点是接收数据需要预先分配缓存, 而不是NIO那种需要接收时才需要分配缓存, 所以对连接数量非常大但流量小的情况, 内存会存在严重浪费的情况

综述,Netty没有选择AIO,而是选择了NIO。

6.Netty有哪些核心组件?

  • Bootstrap、ServerBootstrap(Netty启动类)

Bootstrap是客户端的引导类,ServerBootstrap是服务端引导类

  • Channel:Netty网络通信的组件,用于网络IO操作;Channel提供异步的网络IO操作,调用后立即返回ChannelFuture,通过注册监听,或者同步等待,最终获取结果;
  • ChannelHandler:属于业务的核心接口,用于处理IO事件或者拦截IO操作,并将其转发到ChannelPipeline(业务处理链)中的下一个处理程
  • ChannelPipeline:是一个handler的集合,它负责处理和拦截出站和入站的事件和操作;在Netty中,每个Channel都有且只有一个ChannelPipeline与之对
  • ChannelHandlerContext:保存Channel相关的所有上下文信息,同时关联一个ChannelHandler。
  • NioEventLoopGroupNioEventLoop:NioEventLoopGroup可以理解为线程池,NioEventLoop理解为一个线程,每个EventLoop对应一个Selector,负责处理多个Channel上的事件。

7.Netty的高性能表现在哪些地方?

  • 异步非阻塞的 IO 模型:Netty 使用了 Java NIO 框架,通过 Selector 实现了异步非阻塞 IO,避免了传统 IO 模型中的阻塞等待,从而大大提高了网络通信的效率和吞吐量。
  • 零拷贝优化:Netty 通过使用 Direct Memory Buffer 技术,避免了传统 IO 模型中的数据拷贝过程,减少了 CPU 和内存的开销,提高了网络通信的效率。
  • 线程模型优化:Netty 的线程模型采用了多线程 Reactor 模型,通过使用多个 Reactor 线程处理网络 IO 事件,避免了传统 IO 模型中的阻塞等待,提高了网络通信的吞吐量。
  • 可扩展性优化:Netty 提供了高度可扩展的设计,通过 ChannelPipeline、ChannelHandler 等组件,可以方便地扩展和定制网络通信的行为和处理逻辑。

8.什么是零拷贝?Netty是怎么实现零拷贝的?

  • 零拷贝(Zero-Copy)是指在数据传输过程中,避免将数据从一个内存区域拷贝到另一个内存区域的过程,从而减少了CPU和内存的开销,提高了数据传输的效率。
  • 在传统的IO模型中,数据从磁盘或网络中读取到内核缓冲区,然后再被拷贝到应用程序的用户缓冲区,最后再被拷贝到网络或磁盘中。这种数据拷贝的过程会产生多次内存复制和上下文切换,导致CPU和内存的开销增加,降低了数据传输的效率。而零拷贝技术则可以避免这些不必要的内存复制和上下文切换,提高了数据传输的效率。

Netty实现零拷贝

  • 使用 Direct Memory Buffer:Netty 通过使用 Direct Memory Buffer 来实现零拷贝。Direct Memory Buffer 是一种在堆外内存中分配的缓冲区,可以直接访问内存,而不需要将数据从内核缓冲区复制到用户缓冲区。因此,使用 Direct Memory Buffer 可以避免内存复制的过程,提高数据传输的效率。
  • 使用 FileRegion:Netty 还提供了 FileRegion 类,可以将磁盘上的文件映射到内存中,从而避免了数据从磁盘到内核缓冲区的复制过程。这种方式同样可以实现零拷贝。

9.Netty中线程模型是什么样的?

Netty中的线程模型是属于Reactor模型的,包括三种实现方式Reactor单线程模型、Reactor多线程模型、Reactor主从多线程模型,目前使用的方式是Reactor主从多线程模型,很好的解决了高并发的性能问题。如果给Reactor线程模式下一个定义,则可以理解为:Reactor线程模式 = Reactor(I/O多路复用)+ 线程,Reactor负责监听和分配事件,线程池负责处理事件。

Reactor单线程模型

Reactor内部通过 selector 监听连接事件,收到事件后通过dispatch进行分发。

  • 如果是连接建立的事件,通过accept接受连接,并创建一个Handler来处理连接后续的各种事件。
  • 如果是读写事件,直接调用连接对应的Handler来处理,Handler完成 read => (decode => compute => encode) => send 的全部流程

从始至终,整个过程都只有一个线程在执行操作。

不过改种模式下,在请求过多的时候,只有一个线程处理,无法发挥cpu多核处理性能,且一旦其中一个Handler阻塞,服务端整体则无法继续处理连接事物

Reactor多线程模型

为了提高性能,我们可以把复杂的事件处理handler交给线程池,那就可以演进为 「Reactor多线程模型」 。

通过select监听客户请求,如果是连接建立的事件,通过accept接受连接,并创建一个Handler来处理连接后续的读写事件。这里的Handler只负责响应事件、read和write事件,会将具体的业务处理交由Worker线程池处理。处理所有业务事件,包括(decode => compute => encode) 过程。能充分利用多核机器的资源,提高性能。

但是,在高并发情况下,一个Reactor线程负责监听和处理所有的客户端连接可能会存在性能问题。

Reactor主从多线程模型

为了充分利用多核能力,可以构建两个 Reactor,这就演进为 「Reactor主从多线程模型」 。

比起第二种模型,它是将Reactor分成两部分:

  1. mainReactor负责监听server socket,用来处理网络IO连接建立操作,将建立的socketChannel指定注册给subReactor。
  2. subReactor主要做和建立起来的socket做数据交互和事件业务处理操作。通常,subReactor个数上可与CPU个数等同。

Nginx、Swoole、Memcached和Netty都是采用这种实现。

Reactor模型优点:

  • 响应快,不必为单个同步时间所阻塞,虽然Reactor本身依然是同步的;
  • 编程相对简单,可以最大程度的避免复杂的多线程及同步问题,并且避免了多线程/进程的切换开销
  • 可扩展性,可以方便地通过增加Reactor实例个数来充分利用CPU资源
  • 可复用性,Reactor模型本身与具体事件处理逻辑无关,具有很高的复用性。

10.什么是TCP粘包和半包?

TCP是个"流"协议,所谓流,就是没有界限的一串数据。客户端发送数据时,实际时把数据写进了Tcp发送的缓冲区里面。

  • 半包:收到半个包。如送的包的大小比TCP发送缓冲区容量大或者发送的数据大于协议的最大传输单元(MTU),那么这个数据包就会被分成多个包,通过socket多次发送到服务端,服务端第一次从缓冲区获取到的数据时整个全包的一部分
  • 粘包:发送的包的大小比TCP发送缓冲区容量小,并且TCP缓冲区可以存放多个包,那么客户端和服务端的一次通信就可能传递了多个包,这时候服务端从接受缓存就可能一下读取了多个包

举例说明:比如发送2条数据:ABC、DEF ,服务端可能一次就收到ABCDEF(粘包现象) ,也可能收到AB、CD、DE(半包现象)

11.Netty如和解决粘包和拆包?

解决原理:找出消息的边界

NIO解决方式:

1)固定设置一个缓冲区空间大小,比如1024,但是存在一个很明显的问题,容易造成空间浪费

2)设置一个固定的分割符,设定缓存空间固定,根据分割符多次读取,比如设定分割符为"\n"

3)传送前先发送一个大小的信息给服务端,服务端创建对应大小缓冲区(HTTP的实现方式)

Netty的解决方式:

对三种常用封装成帧进行支持 (以下类都继承ByteToMessageDecoder,ByteToMessageDecoder又继承ChannelInboundHandlerAdapter)

消息固定长度 FixedLengthFrameDecoder
自定义分隔符分割 DelimiterBasedFrameDecoder
换行符进行分割 LineBasedFrameDecoder
消息分为消息头和消息体,消息头中包含表示消息总长度 LengthFieldBasedFrameDecoder
相关推荐
吾日三省吾码2 小时前
JVM 性能调优
java
弗拉唐3 小时前
springBoot,mp,ssm整合案例
java·spring boot·mybatis
oi773 小时前
使用itextpdf进行pdf模版填充中文文本时部分字不显示问题
java·服务器
少说多做3434 小时前
Android 不同情况下使用 runOnUiThread
android·java
知兀4 小时前
Java的方法、基本和引用数据类型
java·笔记·黑马程序员
蓝黑20204 小时前
IntelliJ IDEA常用快捷键
java·ide·intellij-idea
Ysjt | 深4 小时前
C++多线程编程入门教程(优质版)
java·开发语言·jvm·c++
shuangrenlong4 小时前
slice介绍slice查看器
java·ubuntu
牧竹子4 小时前
对原jar包解压后修改原class文件后重新打包为jar
java·jar