Netty面试题
Posted 百里浅暮
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Netty面试题相关的知识,希望对你有一定的参考价值。
----
覆盖90%面试! 2020最新Netty面试题汇总
Netty面试题(2020最新)
-
高并发:Netty 是一款基于 NIO(Nonblocking IO,非阻塞IO)开发的网络通信框架,对比于 BIO(Blocking I/O,阻塞IO),他的并发性能得到了很大提高。 -
传输快:Netty 的传输依赖于零拷贝特性,尽量减少不必要的内存拷贝,实现了更高效率的传输。 -
封装好:Netty 封装了 NIO 操作的很多细节,提供了易于使用调用接口。
-
使用简单:封装了 NIO 的很多细节,使用更简单。 -
功能强大:预置了多种编解码功能,支持多种主流协议。 -
定制能力强:可以通过 ChannelHandler 对通信框架进行灵活地扩展。 -
性能高:通过与其他业界主流的 NIO 框架对比,Netty 的综合性能最优。 -
稳定:Netty 修复了已经发现的所有 NIO 的 bug,让开发人员可以专注于业务本身。 -
社区活跃:Netty 是活跃的开源项目,版本迭代周期短,bug 修复速度快。
-
IO 线程模型:同步非阻塞,用最少的资源做更多的事。 -
内存零拷贝:尽量减少不必要的内存拷贝,实现了更高效率的传输。 -
内存池设计:申请的内存可以重用,主要指直接内存。内部实现是用一颗二叉查找树管理内存分配情况。 -
串形化处理读写:避免使用锁带来的性能开销。 -
高性能序列化协议:支持 protobuf 等高性能序列化协议。
-
行分隔符类:LineBasedFrameDecoder -
或自定义分隔符类 :DelimiterBasedFrameDecoder
-
Netty 的接收和发送 ByteBuffer 采用 DIRECT BUFFERS,使用堆外直接内存进行 Socket 读写,不需要进行字节缓冲区的二次拷贝。如果使用传统的堆内存(HEAP BUFFERS)进行 Socket 读写,JVM 会将堆内存 Buffer 拷贝一份到直接内存中,然后才写入 Socket 中。相比于堆外直接内存,消息在发送过程中多了一次缓冲区的内存拷贝。 -
Netty 提供了组合 Buffer 对象,可以聚合多个 ByteBuffer 对象,用户可以像操作一个 Buffer 那样方便的对组合 Buffer 进行操作,避免了传统通过内存拷贝的方式将几个小 Buffer 合并成一个大的 Buffer。 -
Netty 的文件传输采用了 transferTo 方法,它可以直接将文件缓冲区的数据发送到目标 Channel,避免了传统通过循环 write 方式导致的内存拷贝问题。 -
-
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,这个关联是永久性的。 -
-
直接写入 Channel 中,消息从 ChannelPipeline 当中尾部开始移动; -
写入和 ChannelHandler 绑定的 ChannelHandlerContext 中,消息从 ChannelPipeline 中的下一个 ChannelHandler 中移动。
-
readerIdleTime:为读超时时间(即测试端一定时间内未接受到被测试端消息)。 -
writerIdleTime:为写超时时间(即测试端一定时间内向被测试端发送消息)。 -
allIdleTime:所有类型的超时时间。
-
作用不同:Tomcat 是 Servlet 容器,可以视为 Web 服务器,而 Netty 是异步事件驱动的网络应用程序框架和工具用于简化网络编程,例如TCP和UDP套接字服务器。 -
协议不同:Tomcat 是基于 http 协议的 Web 服务器,而 Netty 能通过编程自定义各种协议,因为 Netty 本身自己能编码/解码字节流,所有 Netty 可以实现,HTTP 服务器、FTP 服务器、UDP 服务器、RPC 服务器、WebSocket 服务器、Redis 的 Proxy 服务器、MySQL 的 Proxy 服务器等等。
Netty简介
JDK原生NIO程序的问题
-
NIO的类库和API繁杂,使用麻烦,你需要熟练掌握Selector、ServerSocketChannel、SocketChannel、ByteBuffer等 -
需要具备其它的额外技能做铺垫,例如熟悉Java多线程编程,因为NIO编程涉及到Reactor模式,你必须对多线程和网路编程非常熟悉,才能编写出高质量的NIO程序 -
可靠性能力补齐,开发工作量和难度都非常大。例如客户端面临断连重连、网络闪断、半包读写、失败缓存、网络拥塞和异常码流的处理等等,NIO编程的特点是功能开发相对容易,但是可靠性能力补齐工作量和难度都非常大 -
JDK NIO的BUG,例如臭名昭著的epoll bug,它会导致Selector空轮询,最终导致CPU 100%。官方声称在JDK1.6版本的update18修复了该问题,但是直到JDK1.7版本该问题仍旧存在,只不过该bug发生概率降低了一些而已,它并没有被根本解决
Netty的特点
-
设计优雅 适用于各种传输类型的统一API - 阻塞和非阻塞Socket 基于灵活且可扩展的事件模型,可以清晰地分离关注点 高度可定制的线程模型 - 单线程,一个或多个线程池 真正的无连接数据报套接字支持(自3.1起) -
使用方便 详细记录的Javadoc,用户指南和示例 没有其他依赖项,JDK 5(Netty 3.x)或6(Netty 4.x)就足够了 -
高性能 吞吐量更高,延迟更低 减少资源消耗 最小化不必要的内存复制 -
安全 完整的SSL / TLS和StartTLS支持 -
社区活跃,不断更新 社区活跃,版本迭代周期短,发现的BUG可以被及时修复,同时,更多的新功能会被加入
Netty常见使用场景
-
互联网行业 在分布式系统中,各个节点之间需要远程服务调用,高性能的RPC框架必不可少,Netty作为异步高新能的通信框架,往往作为基础通信组件被这些RPC框架使用。典型的应用有:阿里分布式服务框架Dubbo的RPC框架使用Dubbo协议进行节点间通信,Dubbo协议默认使用Netty作为基础通信组件,用于实现各进程节点之间的内部通信。 -
游戏行业 无论是手游服务端还是大型的网络游戏,Java语言得到了越来越广泛的应用。Netty作为高性能的基础通信组件,它本身提供了TCP/UDP和HTTP协议栈。非常方便定制和开发私有协议栈,账号登录服务器,地图服务器之间可以方便的通过Netty进行高性能的通信 -
大数据领域 经典的Hadoop的高性能通信和序列化组件Avro的RPC框架,默认采用Netty进行跨界点通信,它的Netty Service基于Netty框架二次封装实现
-
每个请求都需要独立的线程完成数据read,业务处理,数据write的完整操作
-
当并发数较大时,需要创建大量线程来处理连接,系统资源占用较大 -
连接建立后,如果当前线程暂时没有数据可读,则线程就阻塞在read操作上,造成线程资源浪费
I/O复用模型
基于buffer
线程模型
事件驱动模型
-
轮询方式 线程不断轮询访问相关事件发生源有没有发生事件,有发生事件就调用事件处理逻辑。 -
事件驱动方式 发生事件,主线程把事件放入事件队列,在另外线程不断循环消费事件列表中的事件,调用事件对应的处理逻辑处理事件。事件驱动方式也被称为消息通知方式,其实是设计模式中观察者模式的思路。
-
轮询方式 线程不断轮询是否发生按钮点击事件,如果发生,调用处理逻辑 -
事件驱动方式 发生点击事件把事件放入事件队列,在另外线程消费的事件列表中的事件,根据事件类型调用相关事件处理逻辑
-
事件队列(event queue):接收事件的入口,存储待处理事件 -
分发器(event mediator):将不同的事件分发到不同的业务逻辑单元 -
事件通道(event channel):分发器与处理器之间的联系渠道 -
事件处理器(event processor):实现业务逻辑,处理完成后会发出事件,触发下一步操作
-
可扩展性好,分布式的异步架构,事件处理器之间高度解耦,可以方便扩展事件处理逻辑 -
高性能,基于队列暂存事件,能方便并行异步处理事件
-
Reactor Reactor在一个单独的线程中运行,负责监听和分发事件,分发给适当的处理程序来对IO事件做出反应。它就像公司的电话接线员,它接听来自客户的电话并将线路转移到适当的联系人 -
Handlers 处理程序执行I/O事件要完成的实际事件,类似于客户想要与之交谈的公司中的实际官员。Reactor通过调度适当的处理程序来响应I/O事件,处理程序执行非阻塞操作
-
单Reactor单线程 -
单Reactor多线程 -
主从Reactor多线程
Netty线程模型
-
MainReactor负责客户端的连接请求,并将请求转交给SubReactor -
SubReactor负责相应通道的IO读写请求 -
非IO请求(具体逻辑处理)的任务则会直接写入队列,等待worker threads进行处理
EventLoopGroup bossGroup = new NioEventLoopGroup(); EventLoopGroup workerGroup = new NioEventLoopGroup(); ServerBootstrap server = new ServerBootstrap(); server.group(bossGroup, workerGroup) .channel(NioServerSocketChannel.class)
-
bossGroup线程池则只是在bind某个端口后,获得其中一个线程作为MainReactor,专门处理端口的accept事件,每个端口对应一个boss线程 -
workerGroup线程池会被各个SubReactor和worker线程充分利用
-
通过isDone方法来判断当前操作是否完成 -
通过isSuccess方法来判断已完成的当前操作是否成功 -
通过getCause方法来获取已完成的当前操作失败的原因 -
通过isCancelled方法来判断已完成的当前操作是否被取消 -
通过addListener方法来注册监听器,当操作已完成(isDone方法返回完成),将会通知指定的监听器;如果future对象已完成,则理解通知指定的监听器
serverBootstrap.bind(port).addListener(future -> { if (future.isSuccess()) { System.out.println(new Date() + ": 端口[" + port + "]绑定成功!"); } else { System.err.println("端口[" + port + "]绑定失败!"); } });
Netty架构设计
功能特性
-
传输服务 支持BIO和NIO -
容器集成 支持OSGI、JBossMC、Spring、Guice容器 -
协议支持 HTTP、Protobuf、二进制、文本、WebSocket等一系列常见协议都支持。还支持通过实行编码解码逻辑来实现自定义协议 -
Core核心 可扩展事件模型、通用通信API、支持零拷贝的ByteBuf缓冲对象
模块组件
Bootstrap、ServerBootstrap
Channel
-
当前网络连接的通道的状态(例如是否打开?是否已连接?) -
网络连接的配置参数 (例如接收缓冲区大小) -
提供异步的网络I/O操作(如建立连接,读写,绑定端口),异步调用意味着任何I / O调用都将立即返回,并且不保证在调用结束时所请求的I / O操作已完成。调用立即返回一个ChannelFuture实例,通过注册监听器到ChannelFuture上,可以I / O操作成功、失败或取消时回调通知调用方。 -
支持关联I/O操作与对应的处理程序
-
NioSocketChannel,异步的客户端 TCP Socket 连接 -
NioServerSocketChannel,异步的服务器端 TCP Socket 连接 -
NioDatagramChannel,异步的 UDP 连接 -
NioSctpChannel,异步的客户端 Sctp 连接 -
NioSctpServerChannel,异步的 Sctp 服务器端连接 这些通道涵盖了 UDP 和 TCP网络 IO以及文件 IO.
NioEventLoop
-
I/O任务 即selectionKey中ready的事件,如accept、connect、read、write等,由processSelectedKeys方法触发。 -
非IO任务 添加到taskQueue中的任务,如register0、bind0等任务,由runAllTasks方法触发。
NioEventLoopGroup
-
ChannelInboundHandler用于处理入站I / O事件 -
ChannelOutboundHandler用于处理出站I / O操作
-
ChannelInboundHandlerAdapter用于处理入站I / O事件 -
ChannelOutboundHandlerAdapter用于处理出站I / O操作 -
ChannelDuplexHandler用于处理入站和出站事件
ChannelHandlerContext
I/O Request via Channel or ChannelHandlerContext | +---------------------------------------------------+---------------+ | ChannelPipeline | | | \|/ | | +---------------------+ +-----------+----------+ | | | Inbound Handler N | | Outbound Handler 1 | | | +----------+----------+ +-----------+----------+ | | /|\ | | | | \|/ | | +----------+----------+ +-----------+----------+ | | | Inbound Handler N-1 | | Outbound Handler 2 | | | +----------+----------+ +-----------+----------+ | | /|\ . | | . . | | ChannelHandlerContext.fireIN_EVT() ChannelHandlerContext.OUT_EVT()| | [ method call] [method call] | | . . | | . \|/ | | +----------+----------+ +-----------+----------+ | | | Inbound Handler 2 | | Outbound Handler M-1 | | | +----------+----------+ +-----------+----------+ | | /|\ | | | | \|/ | | +----------+----------+ +-----------+----------+ | | | Inbound Handler 1 | | Outbound Handler M | | | +----------+----------+ +-----------+----------+ | | /|\ | | +---------------+-----------------------------------+---------------+ | \|/ +---------------+-----------------------------------+---------------+ | | | | | [ Socket.read() ] [ Socket.write() ] | | | | Netty Internal I/O Threads (Transport Implementation) | +-------------------------------------------------------------------+
工作原理架构
public static void main(String[] args) { // 创建mainReactor NioEventLoopGroup boosGroup = new NioEventLoopGroup(); // 创建工作线程组 NioEventLoopGroup workerGroup = new NioEventLoopGroup(); final ServerBootstrap serverBootstrap = new ServerBootstrap(); serverBootstrap // 组装NioEventLoopGroup .group(boosGroup, workerGroup) // 设置channel类型为NIO类型 .channel(NioServerSocketChannel.class) // 设置连接配置参数 .option(ChannelOption.SO_BACKLOG, 1024) .childOption(ChannelOption.SO_KEEPALIVE, true) .childOption(ChannelOption.TCP_NODELAY, true) // 配置入站、出站事件handler .childHandler(new ChannelInitializer<NioSocketChannel>() { @Override protected void initChannel(NioSocketChannel ch) { // 配置入站、出站事件channel ch.pipeline().addLast(...); ch.pipeline().addLast(...); } }); // 绑定端口 int port = 8080; serverBootstrap.bind(port).addListener(future -> { if (future.isSuccess()) { System.out.println(new Date() + ": 端口[" + port + "]绑定成功!"); } else { System.err.println("端口[" + port + "]绑定失败!"); } }); }
-
1 初始化创建2个NioEventLoopGroup,其中boosGroup用于Accetpt连接建立事件并分发请求, workerGroup用于处理I/O读写事件和业务逻辑 -
2 基于ServerBootstrap(服务端启动引导类),配置EventLoopGroup、Channel类型,连接参数、配置入站、出站事件handler -
3 绑定端口,开始工作
-
1 轮询accept事件 -
2 处理accept I/O事件,与Client建立连接,生成NioSocketChannel,并将NioSocketChannel注册到某个Worker NioEventLoop的Selector上 -
3 处理任务队列中的任务,runAllTasks。任务队列中的任务包括用户调用eventloop.execute或schedule执行的任务,或者其它线程提交到该eventloop的任务。
-
1 轮询read、write事件; -
2 处I/O事件,即read、write事件,在NioSocketChannel可读、可写事件发生时进行处理 -
3 处理任务队列中的任务,runAllTasks。
-
1 用户程序自定义的普通任务
ctx.channel().eventLoop().execute(new Runnable() { @Override public void run() { //... } });
-
2 非当前reactor线程调用channel的各种方法 例如在推送系统的业务线程里面,根据用户的标识,找到对应的channel引用,然后调用write类方法向该用户推送消息,就会进入到这种场景。最终的write会提交到任务队列中后被异步消费。 -
3 用户自定义定时任务
ctx.channel().eventLoop().schedule(new Runnable() { @Override public void run() { } }, 60, TimeUnit.SECONDS);
Netty面试题(2020最新版)
文档版获取:
云盘链接:https://pan.baidu.cm/s/1fs3cBY74
qmTwHUOQ 提取码:jh
最新2020汇总1000道 互联网大厂Java工程师面试题
文档版获取:
复制这段内容后打开百度网盘手机Apphttps://pan.baidu.com/s/1x1_wCVGDv52FEiJl2a6bfg
提取码:42uo
·得到大牛分享的编程干货
·免费获取最新的 ·系统视频录播资源
·紧跟行业热点
·参与 “脱单活动”
更多干货资源请加管理资源小姐姐微信:gupaott1995
备注:获取资料即可 24小时内通过
以上是关于Netty面试题的主要内容,如果未能解决你的问题,请参考以下文章