Netty⾯试题总结(含答案)
Netty⾯试题及答案,每道都是认真筛选出的⾼频⾯试题,助⼒⼤家能到满意的⼯作!
下载链接:
Netty是⼀个异步事件驱动的⽹络应⽤程序框架,⽤于快速开发可维护的⾼性能协议服务器和客户端。Netty是基于nio的,它封装了jdk的nio,让我们使⽤起来更加⽅法灵活。
⼀个⾼性能、异步事件驱动的 NIO 框架,它提供了对 TCP、UDP 和⽂件传输的⽀持
使⽤更⾼效的 socket 底层,对 epoll 空轮询引起的 cpu 占⽤飙升在内部进⾏了处理,避免了直接使⽤ NIO 的陷阱,简化了 NIO 的处理⽅式。
采⽤多种 decoder/encoder ⽀持,对 TCP 粘包/分包进⾏⾃动化处理
可使⽤接受/处理线程池,提⾼连接效率,对重连、⼼跳检测的简单⽀持
可配置 IO 线程数、TCP 参数, TCP 接收和发送缓冲区使⽤直接内存代替堆内存,通过内存池的⽅式循环利⽤ ByteBuf
通过引⽤计数器及时申请释放不再引⽤的对象,降低了 GC 频率使⽤单线程串⾏化的⽅式,⾼效的 Reactor 线程模型
⼤量使⽤了 volitale、使⽤了 CAS 和原⼦类、线程安全类的使⽤、读写锁的使⽤
1.I/O线程模型:同步⾮阻塞,⽤最少的资源做更多的事
2.内存零拷贝:尽量减少不必要的内存拷贝,实现了更⾼效率的传输。
3.内存池设计:申请的内存可以重⽤,主要指直接内存。内部实现是⽤⼀颗⼆叉查树管理内存分配情况。
4.串形化处理读写:避免使⽤锁带来的性能开销。
5.⾼性能序列化协议:⽀持protobuf等⾼性能序列化协议。
BIO:⼀个连接⼀个线程,客户端有连接请求时服务器端就需要启动⼀个线程进⾏处理。线程开销⼤。
伪异步IO:将请求连接放⼊线程池,⼀对多,但线程还是很宝贵的资源。
NIO:⼀个请求⼀个线程,但客户端发送的连接请求都会注册到多路复⽤器上,多路复⽤器轮询到连接
有I/O请求时才启动⼀个线程进⾏处理。
jvm面试题总结及答案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 线程模型。
在 Reactor 模式中,事件分发器等待某个事件或者可应⽤或个操作的状态发⽣,事件分发器就把这个事件传给事先注册的事件处理函数或者回调函数,由后者来做实际的读写操作。如在 Reactor 中实现读:注册读就绪事件和相应的事件处理器、事件分发器等待事件、事件到来,激活分发器,分发器调⽤事件对应的处理器、事件处理器完成实际的读操作,处理读到的数据,注册新的事件,然后返还控制权。
Buffer:与 Channel 进⾏交互,数据是从 Channel 读⼊缓冲区,从缓冲区写⼊ Channel 中的
flip ⽅法:反转此缓冲区,将 position 给 limit,然后将 position 置为 0,其实就是切换读写模式
clear ⽅法:清除此缓冲区,将 position 置为 0,把 capacity 的值给 limit。
rewind ⽅法:重绕此缓冲区,将 position 置为 0
DirectByteBuffer 可减少⼀次系统空间到⽤户空间的拷贝。但 Buffer 创建和销毁的成本更⾼,不可控,通常会⽤内存池来提⾼性能。直接缓冲区主要分配给那些易受基础系统的本机 I/O 操作影响的⼤型、持久的缓冲区。如果数据量⽐较⼩的中⼩应⽤情况下,可以考虑使⽤heapBuffer,由 JVM 进⾏管理。
Channel:表⽰ IO 源与⽬标打开的连接,是双向的,但不能直接访问数据,只能与 Buffer
进⾏交互。通过源码可知,FileChannel 的 read ⽅法和 write ⽅法都导致数据复制了两次!
Selector 可使⼀个单独的线程管理多个 Channel,open ⽅法可创建 Selector,register ⽅法向多路复⽤器器注册通道,可以监听的事件类型:读、写、连接、accept。注册事件后会产⽣⼀个 SelectionKey:它表⽰ SelectableChannel 和 Selector 之间的注册关系,wakeup ⽅法:使尚未返回的第⼀个选择操作⽴即返回,唤醒的原因是:注册了新的 channel 或者事件;channel 关闭,取消注册;优先级更⾼的事件触发(如定时器事件),希望及时处理。
Selector 在 Linux 的实现类是 EPollSelectorImpl,委托给 EPollArrayWrapper 实现,其中三个
native ⽅法是对 epoll 的封装,⽽ EPollSelectorImpl. implRegister ⽅法,通过调⽤ epoll_ctl 向 epoll 实例中注册事件,还将注册的⽂件描述符(fd)与 SelectionKey 的对应关系添加到fdToKey 中,这个 map 维护了⽂件描述符与 SelectionKey 的映射。
fdToKey 有时会变得⾮常⼤,因为注册到 Selector 上的 Channel ⾮常多(百万连接);过期或失效的 Channel 没有及时关闭。fdToKey 总是串⾏读取的,⽽读取是在 select ⽅法中进⾏的,该⽅法是⾮线程安全的。
Pipe:两个线程之间的单向数据连接,数据会被写到 sink 通道,从 source 通道读取
NIO 的服务端建⽴过程:
Selector.open():打开⼀个 Selector;
ServerSocketChannel.open():创建服务端的 Channel;
bind():绑定到某个端⼝上。并配置⾮阻塞模式;
register():注册Channel 和关注的事件到 Selector 上;
select()轮询拿到已经就绪的事件
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 只负责接⼊认证、握⼿等操作;
TCP 是以流的⽅式来处理数据,⼀个完整的包可能会被 TCP 拆分成多个包进⾏发送,也可能把⼩的封装成⼀个⼤的数据包发送。
TCP 粘包/分包的原因:
应⽤程序写⼊的字节⼤⼩⼤于套接字发送缓冲区的⼤⼩,会发⽣拆包现象,⽽应⽤程序写⼊数据⼩于套接字缓冲区⼤⼩,⽹卡将应⽤多次写⼊的数据发送到⽹络上,这将会发⽣粘包现象;
进⾏ MSS ⼤⼩的 TCP 分段,当 TCP 报⽂长度-TCP 头部长度>MSS 的时候将发⽣拆包以太⽹帧的 payload(净荷)⼤于 MTU(1500 字节)进⾏ ip 分⽚。
解决⽅法
消息定长:FixedLengthFrameDecoder 类
包尾增加特殊字符分割:⾏分隔符类:LineBasedFrameDecoder 或⾃定义分隔符类:
DelimiterBasedFrameDecoder
将消息分为消息头和消息体:LengthFieldBasedFrameDecoder 类。
分为有头部的拆包与粘包、长度字段在前且有头部的拆包与粘包、多扩展头部的拆包与粘包。
序列化(编码)是将对象序列化为⼆进制形式(字节数组),主要⽤于⽹络传输、数据持久化等;⽽反序列化(解码)则是将从⽹络、磁盘等读取的字节数组还原成原始对象,主要⽤于⽹络传输对象的解码,以便完成远程调⽤。
影响序列化性能的关键因素:序列化后的码流⼤⼩(⽹络带宽的占⽤)、序列化的性能
(CPU 资源占⽤);是否⽀持跨语⾔(异构系统的对接和开发语⾔切换)。
Java 默认提供的序列化:⽆法跨语⾔、序列化后的码流太⼤、序列化的性能差
XML,优点:⼈机可读性好,可指定元素或特性的名称。缺点:序列化数据只包含数据本⾝以及类的结构,不包括类型标识和程序集信息;只能序列化公共属性和字段;不能序列化⽅法;⽂件庞⼤,⽂件格式复杂,传输占带宽。适⽤场景:当做配置⽂件存储数据,实时数据转换。
JSON,是⼀种轻量级的数据交换格式,优点:兼容性⾼、数据格式⽐较简单,易于读写、序列化后数据较⼩,可扩展性好,兼容性好、与XML 相⽐,其协议⽐较简单,解析速度⽐较快。缺点:数据的描述性⽐ XML 差、不适合性能要求为 ms 级别的情况、额外空间开销⽐较⼤。适⽤场景(可替代XML):跨防⽕墙访问、可调式性要求⾼、基于 Web browser 的 Ajax 请求、传输数据量相对⼩,实时性
要求相对
低(例如秒级别)的服务。
Fastjson,采⽤⼀种“假定有序快速匹配”的算法。优点:接⼝简单易⽤、⽬前 java 语⾔中最快的 json 库。缺点:过于注重快,⽽偏离了“标准”及功能性、代码质量不⾼,⽂档不全。适⽤场景:协议交互、Web 输出、Android 客户端
Thrift,不仅是序列化协议,还是⼀个 RPC 框架。优点:序列化后的体积⼩, 速度快、⽀持多种语⾔和丰富的数据类型、对于数据字段的增删具有较强的兼容性、⽀持⼆进制压缩编码。缺点:使⽤者较少、跨防⽕墙访问时,不安全、不具有可读性,调试代码时相对困难、不能与其他传输层协议共同使⽤(例如 HTTP)、⽆法⽀持向持久层直接读写数据,即不适合做数据持久化序列化协议。适⽤场景:分布式系统的RPC 解决⽅案
Avro,Hadoop 的⼀个⼦项⽬,解决了 JSON 的冗长和没有 IDL 的问题。优点:⽀持丰富的数据类型、简单的动态语⾔结合功能、具有⾃我描述属性、提⾼了数据解析速度、快速可压缩的⼆进制数据形式、可以实现远程过程调⽤ RPC、⽀持跨编程语⾔实现。缺点:对于习惯于静态类型语⾔的⽤户不直观。适⽤场景:在 Hadoop 中做 Hive、Pig 和 MapReduce 的持久化数据格式。
Protobuf,将数据结构以.proto ⽂件进⾏描述,通过代码⽣成⼯具可以⽣成对应数据结构的POJO 对象和 Protobuf 相关的⽅法和属性。优点:序列化后码流⼩,性能⾼、结构化数据存储格式(XML JSON 等)、通过标识字段的顺序,可以实现协议的前向兼容、结构化的⽂档更容易管理和维护。缺点:需要依赖于⼯具⽣成代码、⽀持的语⾔相对较少,官⽅只⽀持Java 、C++ 、python。适⽤场景:对性能要求⾼的RPC 调⽤、具有良好的跨防⽕墙的访问属性、适合应⽤层对象的持久化
其它
protostuff 基于 protobuf 协议,但不需要配置 proto ⽂件,直接导包即可Jboss marshaling 可以直接序列化 java 类,⽆须实
java.io.Serializable 接⼝Message pack ⼀个⾼效的⼆进制序列化格式
Hessian 采⽤⼆进制协议的轻量级 remoting onhttp ⼯具
kryo 基于 protobuf 协议,只⽀持 java 语⾔,需要注册(Registration),然后序列化
(Output),反序列化(Input)
下载链接:
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论