Skip to content

Netty 面试题

1.Netty 是什么?

Netty 是一个异步事件驱动的网络应用程序框架,用于快速开发可维护的高性能协议服务器和客户端。netty 是基于 nio 的,它封装了 jdk 的 nio,让我们用起来更加方便灵活。

2.Netty 的特点是什么?

  • 高并发:Netty 是一款基于 NIO(Nonblocking IO,非阻塞 IO),开发网络通信框架,对于 BIO(Blocking IO),他的并发性能得到了很大提高。
  • 传输快:Netty 的传输依赖于零拷贝特性,尽量减少不必要的内存拷贝,实现了更高效率的传输。
  • 封装好:Netty 封装了 NIO 操作的很多细节,提供了易于使用调用接口。

3.Netty 的优势有哪些?

  • 使用简单:封装了 NIO 的很多细节,使用更简单。
  • 功能强大:预置了多种编码功能,支持多种主流协议。
  • 定制能力强:可以通过 ChannelHandler 对通信框架进行灵活地扩展。
  • 性能高:通过与其他业界主流的 NIO 框架对比,Netty 的综合性能最优。
  • 稳定:Netty 修复了已经发现的所有 NIO 的 bug,让开发人员可以专注于业务本身。
  • 社区活跃:Netty 是活跃的开源项目,版本迭代周期短,bug 修复速度快。
  • 健壮性: netty 已经历各大 rpc 框架,消息中间件,分布式通信中间件线上的广泛验证,健壮性无比强大 。

4.Netty 的应用场景有哪些?

netty 的使用场景:

  • 分布式系统中,各个节点之间需要远程服务调用,高性能的 RPC 框架必不可少,Netty 作为异步高性能的通信框架,经常被当作基础通信组件被 RPC 框架使用.典型的应用有: 阿里分布式服务框架 Dubbo 的 RPC 框架使用 Dubbo 协议进行节点之间的通信,Dubbo 协议默认使用 netty 作为基础通信组件,用于实现各进程节点之间的内部通信.
  • 大数据领域: 大数据计算都会采用多个计算节点和一个/N 个汇总节点进行分布式部署,各节点之间存在海量的数据交换。 经典的 Hadoop 的高性能通信和序列化组件 Avro 的 RPC 框架,默认采用 Netty 进行跨界点通信,它的 Netty Service 基于 Netty 框架二次封装实现
  • 游戏行业 无论是手游服务端还是大型的网络游戏,Java 语言得到了越来越广泛的应用。Netty 作为高性能的基础通信组件,它本身提供了 TCP/UDP 和 HTTP 协议栈。 非常方便定制和开发私有协议栈,账号登录服务器,地图服务器之间可以方便的通过 Netty 进行高性能的通信

5.Netty 高性能表现在哪些方面?

  • IO 线程模型:同步非阻塞,用最少的资源做更多的事。
  • 内存零拷贝:尽量减少不必要的内存拷贝,实现了更高效率的传输。
  • 内存池设计:申请的内存可以重用,主要指直接内存。内部实现是用一颗二叉查找树管理内存分配情况。
  • 串形化处理读写:避免使用锁带来的性能开销。
  • 高性能序列化协议:支持 protobuf 等高性能序列化协议。

6.BIO、NIO 和 AIO 的区别?

BIO 同步阻塞 IO(Blocking IO) 指的是用户空间 (或者线程) 主动发起,需要等待内核 IO 操作彻底完成后才能返回到用户空间得 IO 操作。在 IO 操作过程中,发起 IO 请求得用户进程 (或者线程) 处于阻塞状态。

NIO 非阻塞 IO(Non-Blocking IO,NIO)指的是用户空间不需要等待内核 IO 操作彻底完成,可以立即返回用户空间去执行后续的指令,即发起 IO 请求的用户进程 (或者线程) 处于非阻塞状态,与此同时,内核会立即返回给用户一个 IO 状态值。

阻塞 BIO 和非阻塞 NIO 的区别

阻塞是指用户进程 (或者线程) 一只在等待,而不能做别的事情

非阻塞指的是用户进程 (或者线程) 获得内核返回的状态值就返回自己的空间,可以去做别的事情。在 java 中,非阻塞 IO 的 socket 被设置为 NONBLOCK 模式。

AIO 异步 IO(Asynchronous IO,AIO)指的是用户空间的线程变为被动接受者,而内核空间成为主动调用者。在异步 IO 模型中,当用户线程收到通知时,数据已经被内核读取完毕放在了用户缓冲区内,内核在 IO 完成后通知用户线程直接使用即可。异步 IO 类似于 java 中典型的回调模式,用户进程 (或者线程) 向内核空间注册了各种 IO 事件的回调函数,由内核去主动调用

7.NIO 的组成?

Java NIO 类库包含以下三个核心组件:

Channel(通道)

- Channel 是一个对象,可以从中读取数据并向其写入数据
- 类似于 Stream , 去任何地方(或来自任何地方)的数据都必须通过 Channel 对象
- 数据可以从 channel 写入buffers ,也可以从 buffers 读取到 channels , channels 的 读写均为单向
- 通道中的数据总是要先读到一个Buffer,或者总是要从一个Buffer中写入
- 通道可以异步地读写

Buffer(缓冲区)

- Buffer 是一个 Java 对象
- 缓冲区本质上是一个数组 , 通常是字节数组 , 也可以是其他数组
- 缓冲区提供对数据的结构化访问,并且跟踪系统的读/写进程。
- 发送到通道的所有数据必须首先放置在缓冲区中; 同样,从通道读取的任何数据都被读入缓冲区

Selector(选择器)

- Selector允许单线程处理多个 Channel , 当打开了多个连接(通道),但每个连接的流量都很低,使用Selector就会很方便 。
- 要使用Selector,得向Selector注册Channel,然后调用它的select()方法。
- 这个方法会一直阻塞到某个注册的通道有事件就绪。
- 一旦这个方法返回,线程就可以处理这些事件,事件的例子有如新连接进来,数据接收

8.Netty 的线程模型?

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 只负责接入认证、握手等操作;

9.TCP 粘包/拆包的原因及解决方法?

TCP 是以流的方式来处理数据,一个完整的包可能会被 TCP 拆分成多个包进行发送,也可 能把小的封装成一个大的数据包发送。

TCP 粘包/分包的原因: 应用程序写入的字节大小大于套接字发送缓冲区的大小,会发生拆包现象而应用程序写 入数据小于套接字缓冲区大小,网卡将应用多次写入的数据发送到网络上,这将会发生粘包现象; 进行 MSS 大小的 TCP 分段,当 TCP 报文长度-TCP 头部长度>MSS 的时候将发生拆包 以太网帧的 payload(净荷)大于 MTU(1500 字节)进行 ip 分片。

解决方法 消息定长:FixedLengthFrameDecoder 类包尾增加特殊字符分割:行分隔符类:LineBasedFrameDecoder 或自定义分隔符类 : DelimiterBasedFrameDecoder 将消息分为消息头和消息体:LengthFieldBasedFrameDecoder 类。分为有头部的拆包与粘包、长度字段在前且有头部的拆包与粘包、多扩展头部的拆包与粘包。

10.什么是 Netty 的零拷贝?

Netty 的零拷贝主要包含三个方面:

  • Netty 的接收和发送 ByteBuffer 采用 direct buffers,使用堆外直接内存进行 Socket 读写,不需要进行字节缓冲区的二次拷贝。如果使用传统的堆内存(heap buffers)进行 Socket 读写,JVM 会将堆内存 Buffer 拷贝一份到直接内存中,然后才写入 Socket 中。相比于堆外直接内存,消息在发送过程中多了一次缓冲区的内存拷贝。
  • Netty 提供了组合 Buffer 对象,可以聚合多个 ByteBuffer 对象,用户可以像操作一个 Buffer 那样方便的对组合 Buffer 进行操作,避免了传统通过内存拷贝的方式将几个小 Buffer 合并成一个大的 Buffer。
  • Netty 的文件传输采用了 transferTo 方法,它可以直接将文件缓冲区的数据发送到目标 Channel,避免了传统通过循环 write 方式导致的内存拷贝问题。

11.Netty 中有哪种重要组件?

ServerBootstrap**、Bootrap:**服务端和客户端配置整个 Netty 程序,串联各个组件。

Channel**:**能够用于执行网络 I/O 操作,Channel 为用户提供了,1、当前网络链接的通道的状态、2、网络链接的配置参数、3、提供网络 IO 操作(如建立端口、读写、绑定端口),以及 IO 操作处理程序。

NioEventLoopGroup**:**管理 EventLoop 的生命周期,可以理解为一个线程池,内部维护了一组线程,每个线程(NioEventLoop)负责处理多个 Channel 上的事件,而一个 Channel 只对应一个线程。

ChannelHandler**:**处理 IO 事件或拦截 IO 操作,并将其准发到 ChannelPipeline(业务处理链)的下一个处理程序。

**ChannelPipline:**保存了 ChannleHandler 的 List,用于处理或拦截 Channel 的入栈事件和出站操作,使用户可以完全控制事件的处理方式,以及 Channel 中各个的 ChannelHandler 如何相互交互。

12.Netty 发送消息有几种方式?

Netty 发送消息的方式有两种:

  1. 直接写入 Channel 中,消息从 ChannelPipeline 当中尾部开始移动;
  2. 写入和 ChannelHandler 绑定的 ChannelHandlerContext 中,消息从 ChannelPipeline 中的下一个 ChannelHandler 中移动。

13.默认情况 Netty 起多少线程?何时启动?

Netty 默认是 CPU 处理器数的两倍,bind 完之后启动。

14.了解哪几种序列化协议?

序列化(编码)是将对象序列化为二进制形式(字节数组),主要用于网络传输、数据持久 化等;而反序列化(解码)则是将从网络、磁盘等读取的字节数组还原成原始对象,主要用于网络传输对象的解码,以便完成远程调用。

影响序列化性能的关键因素:

  • 序列化后的码流大小(网络带宽的占用)
  • 序列化的性能 (CPU 资源占用);
  • 是否支持跨语言(异构系统的对接和开发语言切换)。

Java 默认提供的序列化:无法跨语言、序列化后的码流太大、序列化的性能差。

1.XML

优点:人机可读性好,可指定元素或特性的名称。

缺点:序列化数据只包含数据本 身以及类的结构,不包括类型标识和程序集信息;只能序列化公共属性和字段;不能序列化方法;文件庞大,文件格式复杂,传输占带宽。

适用场景:当做配置文件存储数据,实时数据转换。

2.JSON

json 是一种轻量级的数据交换格式

优点:兼容性高、数据格式比较简单,易于读写、 序列化后数据较小,可扩展性好,兼容性好、与 XML 相比,其协议比较简单,解析速度比较快。

缺点:数据的描述性比 XML 差、不适合性能要求为 ms 级别的情况、额外空间开销 比较大。

适用场景(可替代XML):跨防火墙访问、可调式性要求高、基于 Web browser 的 Ajax 请求、传输数据量相对小,实时性要求相对低(例如秒级别)的服务。

3.Fastjson

fastjson 采用一种“假定有序快速匹配”的算法。

优点:接口简单易用、目前 java 语言中 最快的 json 库。

缺点:过于注重快,而偏离了“标准”及功能性、代码质量不高,文档不全。

适用场景:协议交互、Web 输出、Android 客户端。

4.Thrift

Thrift 不仅是序列化协议,还是一个 RPC 框架。

优点:序列化后的体积小, 速度快、支持多种语言和丰富的数据类型、对于数据字段的增删具有较强的兼容性、支持二进制压缩编码。

缺点:使用者较少、跨防火墙访问时,不安全、不具有可读性,调试代码时相对困 难、不能与其他传输层协议共同使用(例如 HTTP)、无法支持向持久层直接读写数据,即不适合做数据持久化序列化协议。

适用场景:分布式系统的 RPC 解决方案。

5.Avro

Avro,Hadoop 的一个子项目,解决了 JSON 的冗长和没有 IDL 的问题。

优点:支持丰富的 数据类型、简单的动态语言结合功能、具有自我描述属性、提高了数据解析速度、快速可 压缩的二进制数据形式、可以实现远程过程调用 RPC、支持跨编程语言实现。

缺点:对于习惯于静态类型语言的用户不直观。

适用场景:在 Hadoop 中做 Hive、Pig 和 MapReduce 的持久化数据格式。

6.Protobuf

Protobuf 将数据结构以 .proto 文件进行描述,通过代码生成工具可以生成对应数据结构的 POJO 对象和 Protobuf 相关的方法和属性。

优点:序列化后码流小,性能高、结构化数据存储格式(XML JSON 等)、通过标识字段的顺序,可以实现协议的前向兼容、结构化的文档 更容易管理和维护。

缺点:需要依赖于工具生成代码、支持的语言相对较少,官方只支持 Java 、C++ 、python。

适用场景:对性能要求高的 RPC 调用、具有良好的跨防火墙的访问 属性、适合应用层对象的持久化。

7.其它

  • protostuff 基于 protobuf 协议,但不需要配置 proto 文件,直接导包即可
  • Jboss marshaling 可以直接序列化 java 类, 无须实 java.io.Serializable 接口
  • Message pack 一个高效的二进制序列化格式
  • Hessian 采用二进制协议的轻量级 remoting onhttp 工具
  • kryo 基于 protobuf 协议,只支持 java 语言,需要注册(Registration),然后序列化 (Output),反序列化(Input)

15.如何选择序列化协议?

具体场景

对于公司间的系统调用,如果性能要求在 100ms 以上的服务,基于 XML 的 SOAP 协议是一个值得考虑的方案。
基于 Web browser 的 Ajax,以及 Mobile app 与服务端之间的通讯,JSON 协议是首选。对于性能要求不太高,或者以动态类型语言为主,或者传输数据载荷很小的的运用场景,JSON 也是非常不错的选择。
对于调试环境比较恶劣的场景,采用 JSON 或 XML 能够极大的提高调试效率,降低系统开发成本。
当对性能和简洁性有极高要求的场景,Protobuf,Thrift,Avro 之间具有一定的竞争关系。
对于 T 级别的数据的持久化应用场景,Protobuf 和 Avro 是首要选择。如果持久化后的数据存储在 hadoop 子项目里,Avro 会是更好的选择。

对于持久层非 Hadoop 项目,以静态类型语言为主的应用场景,Protobuf 会更符合静态类型语言工程师的开发习惯。由于 Avro 的设计理念偏向于动态类型语言,对于动态语言为主的应用场景,Avro 是更好的选择。
如果需要提供一个完整的 RPC 解决方案,Thrift 是一个好的选择。
如果序列化之后需要支持不同的传输层协议,或者需要跨防火墙访问的高性能场景,Protobuf 可以优先考虑。
protobuf 的数据类型有多种:bool、double、float、int32、int64、string、bytes、enum、message。protobuf 的限定符:required: 必须赋值,不能为空、optional: 字段可以赋值,也可以不赋值、repeated: 该字段可以重复任意次数(包括 0 次)、枚举;只能用指定的常量集中的一个值作为其值;

protobuf 的基本规则:每个消息中必须至少留有一个 required 类型的字段、包含 0 个或多个 optional 类型的字段;repeated 表示的字段可以包含 0 个或多个数据;[1,15] 之内的标识号在编码的时候会占用一个字节(常用),[16,2047] 之内的标识号则占用 2 个字节,标识号一定不能重复、使用消息类型,也可以将消息嵌套任意多层,可用嵌套消息类型来代替组。

protobuf 的消息升级原则:不要更改任何已有的字段的数值标识;不能移除已经存在的 required 字段,optional 和 repeated 类型的字段可以被移除,但要保留标号不能被重用。新添加的字段必须是 optional 或 repeated。因为旧版本程序无法读取或写入新增的 required 限定符的字段。

编译器为每一个消息类型生成了一个.java 文件,以及一个特殊的 Builder 类(该类是用来创建消息类接口的)。如:UserProto.User.Builder builder = UserProto.User.newBuilder();builder.build();

Netty 中的使用:ProtobufVarint32FrameDecoder 是用于处理半包消息的解码类;ProtobufDecoder(UserProto.User.getDefaultInstance()) 这是创建的 UserProto.java 文件中的解码类;ProtobufVarint32LengthFieldPrepender 对 protobuf 协议的消息头上加上一个长度为 32 的整形字段,用于标志这个消息的长度的类;ProtobufEncoder 是编码类

将 StringBuilder 转换为 ByteBuf 类型:copiedBuffer() 方法

16.Netty 支持哪些心跳类型设置?

  • readerIdleTime:为读超时时间(即测试端一定时间内未接受到被测试端消息)。
  • writerIdleTime:为写超时时间(即测试端一定时间内向被测试端发送消息)。
  • allIdleTime:所有类型的超时时间。

17.Netty 和 Tomcat 的区别?

  • 作用不同:Tomcat 是 Servlet 容器,可以视为 Web 服务器,而 Netty 是异步事件驱动的网络应用程序框架和工具用于简化网络编程,例如 TCP 和 UDP 套接字服务器。
  • 协议不同:Tomcat 是基于 http 协议的 Web 服务器,而 Netty 能通过编程自定义各种协议,因为 Netty 本身自己能编码/解码字节流,所有 Netty 可以实现,HTTP 服务器、FTP 服务器、UDP 服务器、RPC 服务器、WebSocket 服务器、Redis 的 Proxy 服务器、MySQL 的 Proxy 服务器等等
最近更新