前语
大家好,我是田螺。
咱们去面试的时分,经常被问到netty
的标题。我整理了netty
的32
连问,小伙伴们,收藏起来渐渐看吧。
- 公众号:捡田螺的小男孩 (有田螺精心预备的面试PDF)
- github地址,感谢每颗star:github
1. Netty是什么,它的首要特色是什么?
Netty
是一个高功用、异步事情驱动的网络编程结构,它依据NIO
技能完结,供给了简略易用的 API
,用于构建各种类型的网络运用程序。其首要特色包含:
- 高功用:
Netty
运用异步I/O
,非堵塞式处理办法,可处理很多并发衔接,进步体系功用。 - 易于运用:
Netty
供给了高度笼统的API
,能够快速构建各种类型的网络运用程序,如Web
服务、音讯推送、实时游戏等。 - 灵敏可扩展:
Netty
供给了许多可插拔的组件,能够依据需求自由组合,以满足各种事务场景。
2. Netty 运用场景了解么?
Netty
在网络编程中运用非常广泛,常用于开发高功用、高吞吐量、低推迟的网络运用程序,运用场景如下:
- 服务器间高功用通讯,比方
RPC、HTTP、WebSocket
等协议的完结 - 分布式体系的音讯传输,比方
Kafka、ActiveMQ
等音讯行列 - 游戏服务器,支撑高并发的游戏服务端开发
- 实时流数据的处理,比方音视频流处理、实时数据传输等
- 其他高功用的网络运用程序开发
阿里分布式服务结构 Dubbo, 音讯中心件RocketMQ都是运用 Netty 作为通讯的根底。
3. Netty 中心组件有哪些?别离有什么效果?
Netty的中心组件包含以下几个部分:
- Channel:用于网络通讯的通道,能够理解为
Java NIO
中的SocketChannel
。 - ChannelFuture:异步操作的成果,能够增加监听器以便在操作完结时得到告诉。
- EventLoop:事情循环器,用于处理一切
I/O
事情和恳求。Netty
的I/O
操作都是异步非堵塞的,它们由EventLoop
处理并以事情的办法触发回调函数。 - EventLoopGroup:由一个或多个
EventLoop
组成的组,用于处理一切的Channel
的I/O
操作,能够将其看作是一个线程池。 - ChannelHandler:用于处理
Channel
上的I/O
事情和恳求,包含编码、解码、事务逻辑等,能够理解为NIO
中的ChannelHandler
。 - ChannelPipeline:由一组
ChannelHandler
组成的管道,用于处理Channel
上的一切I/O
事情和恳求,Netty
中的数据处理一般是经过将一个数据包装成一个ByteBuf
目标,而且经过一个ChannelPipeline
来传递处理,以达到事务逻辑与网络通讯的解耦。 - ByteBuf:
Netty
供给的字节容器,能够对字节进行高效操作,包含读写、查找等。 - Codec:用于在
ChannelPipeline
中进行数据编码和解码的组件,如字符串编解码器、目标序列化编解码器等。
这些中心组件一起构成了Netty的中心架构,能够协助开发人员快速地完结高功用、高并发的网络运用程序。
4. Netty的线程模型是怎样的?怎么优化功用?
Netty
的线程模型是依据事情驱动的Reactor
模型,它运用少数的线程来处理很多的衔接和数据传输,以进步功用和吞吐量。在Netty
中,每个衔接都分配了一个独自的EventLoop
线程,该线程担任处理一切与该衔接相关的事情,包含数据传输、握手和封闭等。多个衔接能够同享同一个EventLoop
线程,然后削减线程的创立和毁掉开支,进步资源利用率。
为了进一步优化功用,Netty
供给了一些线程模型和线程池装备选项,以适应不同的运用场景和功用要求。例如,能够运用不同的EventLoopGroup
完结不同的线程模型,如单线程模型、多线程模型和主从线程模型等。一起,还能够设置不同的线程池参数,如线程数、使命行列巨细、线程优先级等,以调整线程池的作业负载和功用体现。
在实践运用中,还能够经过优化网络协议、数据结构、事务逻辑等方面来进步Netty的功用。例如,能够运用零复制技能防止数据复制,运用内存池削减内存分配和收回的开支,防止运用堵塞IO和同步操作等,然后进步运用的吞吐量和功用体现。
5. EventloopGroup了解么?和 EventLoop 啥联系?
EventLoopGroup
和EventLoop
是 Netty
中两个重要的组件。
EventLoopGroup
表明一组EventLoop
,它们一起担任处理客户端衔接的I/O
事情。在 Netty
中,一般会为不同的 I/O
操作创立不同的 EventLoopGroup
。
EventLoop
是 Netty
中的一个中心组件,它代表了一个不断循环的 I/O
线程。它担任处理一个或多个 Channel
的 I/O
操作,包含数据的读取、写入和状况的更改。一个EventLoop
能够处理多个 Channel
,而一个 Channel
只会被一个 EventLoop
所处理。
在 Netty
中,一个运用程序一般会创立两个 EventLoopGroup
:一个用于处理客户端衔接,一个用于处理服务器端衔接。当客户端衔接到服务器时,服务器端的EventLoopGroup
会将衔接分配给一个 EventLoop
进行处理,以便确保一切的 I/O
操作都能得到及时、高效地处理。
6. Netty 的零复制了解么?
零复制(Zero Copy)
是一种技能,能够防止在数据传输进程中对数据的屡次复制操作,然后进步数据传输的功率和功用。在网络编程中,零复制技能能够削减数据在内核空间和用户空间之间的复制次数,然后进步数据传输功率和下降 CPU
的运用率。
Netty
经过运用 Direct Memory
和 FileChannel
的办法完结零复制。当运用程序将数据写入 Channel
时,Netty
会将数据直接写入到内存缓冲区中,然后经过操作体系供给的 sendfile
或许 writev
等零复制技能,将数据从内存缓冲区中传输到网络中,然后防止了中心的屡次复制操作。相同,当运用程序从 Channel
中读取数据时,Netty
也会将数据直接读取到内存缓冲区中,然后经过零复制技能将数据从内存缓冲区传输到用户空间。
经过运用零复制技能,Netty
能够防止在数据传输进程中对数据进行屡次的复制操作,然后进步数据传输的功率和功用。特别是在处理很多数据传输的场景中,零复制技能能够大幅度削减 CPU
的运用率,下降体系的负载。
7. Netty 长衔接、心跳机制了解么?
在网络编程中,长衔接是指客户端与服务器之间树立的衔接能够坚持一段时刻,以便在需求时能够快速地进行数据交换。与短衔接比较,长衔接能够防止频频树立和封闭衔接的开支,然后进步数据传输的功率和功用。
Netty 供给了一种长衔接的完结办法,即经过 Channel
的 keepalive
选项来坚持衔接的状况。当启用了 keepalive
选项后,客户端和服务器之间的衔接将会自动坚持一段时刻,假如在这段时刻内没有数据交换,客户端和服务器之间的衔接将会被封闭。经过这种办法,能够完结长衔接,防止频频树立和封闭衔接的开支。
除了 keepalive
选项之外,Netty
还供给了一种心跳机制来坚持衔接的状况。心跳机制能够经过定时向对方发送心跳音讯,来检测衔接是否正常。假如在一段时刻内没有收到心跳音讯,就以为衔接现已断开,并进行从头衔接。Netty
供给了一个 IdleStateHandler
类,能够用来完结心跳机制。IdleStateHandler
能够设置多个超时时刻,当衔接闲暇时刻超越设定的时刻时,会触发一个事情,能够在事情处理办法中进行相应的处理,比方发送心跳音讯。
经过运用长衔接和心跳机制,能够确保客户端与服务器之间的衔接处于正常的状况,然后进步数据传输的功率和性能。特别是在处理很多数据传输的场景中,长衔接和心跳机制能够下降树立和封闭衔接的开支,削减网络负载,进步体系的安稳性。
8. Netty 服务端和客户端的发动进程了解么?
Netty
是一个依据 NIO
的异步事情驱动结构,它的服务端和客户端的发动进程大致相同,都需求完结以下几个进程:
- 创立
EventLoopGroup
目标。EventLoopGroup
是Netty
的中心组件之一,它用于管理和调度事情的处理。Netty
经过EventLoopGroup
来创立多个EventLoop
目标,并将每个EventLoop
与一个线程绑定。在服务端中,一般会创立两个EventLoopGroup
目标,别离用于接纳客户端的衔接恳求和处理客户端的数据。 - 创立
ServerBootstrap
或Bootstrap
目标。ServerBootstrap 和 Bootstrap
是Netty
供给的服务端和客户端发动器,它们封装了发动进程中的各种参数和装备,便利运用者进行设置。在创立ServerBootstrap
或Bootstrap
目标时,需求指定相应的EventLoopGroup
目标,并进行一些根本的装备,比方传输协议、端口号、处理器等。 - 装备
Channel
的参数。Channel
是Netty
中的一个笼统概念,它代表了一个网络衔接。在发动进程中,需求对Channel
的一些参数进行装备,比方传输协议、缓冲区巨细、心跳检测等。 - 绑定
ChannelHandler。ChannelHandler
是Netty
中用于处理事情的组件,它能够处理客户端的衔接恳求、接纳客户端的数据、发送数据给客户端等。在发动进程中,需求将ChannelHandler
绑定到相应的Channel
上,以便处理相应的事情。 - 发动服务端或客户端。在完结以上装备后,就能够发动服务端或客户端了。在发动进程中,会创立相应的
Channel
,并对其进行一些根本的初始化,比方注册监听器、绑定端口等。发动完结后,就能够开端接纳客户端的恳求或向服务器发送数据了。
总的来说,Netty
的服务端和客户端发动进程比较简略,只需求进行一些根本的装备和设置,就能够完结相应的功用。经过运用 Netty
,能够便利地开发高功用、高可靠性的网络运用程序。
9. Netty 的 Channel 和 EventLoop 之间的联系是什么?
在Netty
中,Channel
代表一个敞开的网络衔接,它能够用来读取和写入数据。而EventLoop
则代表一个履行使命的线程,它担任处理Channel
上的一切事情和操作。
每个Channel
都与一个EventLoop
相关,而一个EventLoop
能够相关多个Channel
。当一个Channel
上有事情产生时,比方数据可读或许可写,它会将该事情提交给相关的EventLoop
来处理。EventLoop
会将该事情参加到它自己的使命行列中,然后依照次序处理行列中的使命。
值得留意的是,一个EventLoop
实例可能会被多个Channel
所同享,因而它需求能够处理多个Channel
上的事情,并确保在处理每个Channel
的事情时不会被堵塞。为此,Netty选用了事情循环(EventLoop
)模型,它经过异步I/O和事情驱动的办法,完结了高效、可扩展的网络编程。
10. 什么是 Netty 的 ChannelPipeline,它是怎么作业的?
在Netty
中,每个Channel
都有一个与之相关的ChannelPipeline
,用于处理该Channel
上的事情和恳求。ChannelPipeline
是一种依据事情驱动的处理机制,它由多个处理器(Handler
)组成,每个处理器担任处理一个或多个事情类型,将事情转换为下一个处理器所需的数据格式。
当一个事情被触发时,它将从ChannelPipeline
的第一个处理器(称为第一个InboundHandler
)开端流经一切的处理器,直到抵达最终一个处理器或许被中途阻拦(经过抛出反常或调用ChannelHandlerContext.fireXXX()
办法完结)。在这个进程中,每个处理器都能够对事情进行处理,也能够修正事情的传递办法,比方在处理完事情后将其转发到下一个处理器,或许直接将事情发送回到该Channel
的对端。
ChannelPipeline
的作业办法能够用以下三个概念来描绘:
- 入站(
Inbound
)事情:由Channel
接纳到的事情,例如读取到新的数据、衔接树立完结等等。入站事情将从ChannelPipeline
的第一个InboundHandler
开端流动,直到最终一个InboundHandler
。 - 出站(
Outbound
)事情:由Channel
发送出去的事情,例如向对端发送数据、封闭衔接等等。出站事情将从ChannelPipeline
的最终一个OutboundHandler
开端流动,直到第一个OutboundHandler
。 -
ChannelHandlerContext
:表明处理器和ChannelPipeline
之间的相相联系。每个ChannelHandler
都有一个ChannelHandlerContext
,经过该目标能够完结在ChannelPipeline
中的事情流中向前或向后传递事情,也能够经过该目标访问Channel、ChannelPipeline和其他ChannelHandler
等。
经过运用ChannelPipeline,Netty完结了高度可装备和可扩展的网络通讯模型,使得开发人员能够依据自己的需求挑选和组合不同的处理器,以构建出高效、安稳、安全的网络通讯体系。
11. Netty 中的 ByteBuf 是什么,它和 Java 的 ByteBuffer 有什么差异?
Netty
的 ByteBuf
是一个可扩展的字节容器,它供给了许多高级的 API
,用于便利地处理字节数据。ByteBuf
与 Java NIO
的 ByteBuffer
比较,有以下差异:
- 容量可扩展:
ByteBuf
的容量能够动态扩展,而ByteBuffer
的容量是固定的。 - 内存分配:
ByteBuf
内部选用了内存池的办法,能够有效地削减内存分配和开释的开支。 - 读写操作:
ByteBuf
供给了多个读写指针,能够便利地读写字节数据。 - 零复制:
ByteBuf
支撑零复制技能,能够削减数据复制的次数。
ByteBuf buffer = Unpooled.buffer(10);
buffer.writeBytes("hello".getBytes());
while (buffer.isReadable()) {
System.out.print((char) buffer.readByte());
}
在上面的示例代码中,咱们运用 Unpooled.buffer()
办法创立了一个ByteBuf
目标 buffer
,并运用 writeBytes()
办法将字符串 "hello"
写入该目标。然后,咱们经过 isReadable()
办法判断该目标是否可读,运用 readByte()
办法读取其间的字节数据,并将其转换为字符输出。
12. Netty 中的 ChannelHandlerContext 是什么,它的效果是什么?
在Netty
中,ChannelHandlerContext
表明衔接到ChannelPipeline
中的一个Handler
上下文。在Netty的IO
事情模型中,ChannelHandlerContext
充当了处理I/O
事情的处理器和ChannelPipeline
之间的桥梁,使处理器能够彼此交互并访问ChannelPipeline
中的其他处理器。
每逢ChannelPipeline
中增加一个Handler
时,Netty
会创立一个ChannelHandlerContext
目标,并将其与该Handler
相关。这个目标包含了该Handler
的相关信息,如所在的ChannelPipeline
、所属的Channel
等。在处理I/O
事情时,Netty
会将I/O
事情转发给与该事情相应的ChannelHandlerContext
,该上下文目标能够使Handler
访问与该事情相关的任何信息,也能够在管道中转发事情。
总之,ChannelHandlerContext
是一个重要的Netty
组件,它供给了一种简略的机制,让开发者在处理网络I/O事情时能够愈加灵敏和高效地操作管道中的Handler
。
13. 什么是 Netty 的 ChannelFuture,它的效果是什么?
在Netty
中,ChannelFuture
表明异步的I/O
操作的成果。当履行一个异步操作(如发送数据到一个长途服务器)时,ChannelFuture
会立即回来,并在将来的某个时分告诉操作的成果,而不是等候操作完结。这种异步操作的特色使得Netty能够在一起处理多个衔接时完结高功用和低推迟的网络运用程序。
详细来说,ChannelFuture
用于在异步操作完结后告诉运用程序成果。在异步操作履行后,Netty
将一个ChannelFuture
目标回来给调用方。调用方能够经过增加一个回调(ChannelFutureListener
)来处理成果。例如,当异步写操作完结时,能够增加一个ChannelFutureListener
以查看操作的状况并采取相应的办法。
ChannelFuture
还供给了许多有用的办法,如查看操作是否成功、等候操作完结、增加监听器等。经过这些办法,运用程序能够更好地操控异步操作的状况和成果。
总之,ChannelFuture
是Netty
中异步I/O
操作的根底,它供给了一种简略而有效的机制,使得开发者能够便利地处理I/O
操作的成果。
14. Netty 中的 ChannelHandler 是什么,它的效果是什么?
在 Netty
中,ChannelHandler
是一个接口,用于处理入站和出站数据流。它能够经过完结以下办法来处理数据流:
-
channelRead(ChannelHandlerContext ctx, Object msg)
: 处理接纳到的数据,这个办法一般会被用于解码数据并将其转换为实践的事务目标。 -
channelReadComplete(ChannelHandlerContext ctx)
: 读取数据完结时被调用,能够用于向长途节点发送数据。 -
exceptionCaught(ChannelHandlerContext ctx, Throwable cause)
: 产生反常时被调用,能够在这个办法中处理反常或封闭衔接。 -
channelActive(ChannelHandlerContext ctx)
: 当衔接树立时被调用。 -
channelInactive(ChannelHandlerContext ctx)
: 当衔接封闭时被调用。
ChannelHandler
能够增加到 ChannelPipeline
中,ChannelPipeline
是一个用于维护 ChannelHandler
调用次序的容器。在数据流进入或离开 Channel
时,ChannelPipeline
中的 ChannelHandler
会依照增加的次序顺次调用它们的办法来处理数据流。
ChannelHandler
的首要效果是将网络协议的细节与运用程序的逻辑别离开来,使得运用程序能够专心于处理事务逻辑,而不需求重视网络协议的完结细节。
15. Netty 中的各种 Codec 是什么,它们的效果是什么?
在 Netty
中,Codec
是一种将二进制数据与 Java
目标之间进行编码和解码的组件。它们能够将数据从字节省解码为 Java
目标,也能够将 Java
目标编码为字节省进行传输。
以下是 Netty 中常用的 Codec
:
-
ByteToMessageCodec
:将字节省解码为Java
目标,一起也能够将Java
目标编码为字节省。能够用于处理自定义协议的音讯解析和封装。 -
MessageToByteEncoder
:将Java
目标编码为字节省。一般用于发送音讯时将音讯转换为二进制数据。 -
ByteToMessageDecoder
:将字节省解码为Java
目标。一般用于接纳到数据后进行解码。 -
StringEncoder 和 StringDecoder
:别离将字符串编码为字节省和将字节省解码为字符串。 -
LengthFieldPrepender 和 LengthFieldBasedFrameDecoder
:用于处理TCP
粘包和拆包问题。 -
ObjectDecoder和ObjectEncoder
:将Java
目标序列化为字节数据,并将字节数据反序列化为Java
目标。
这些 Codec
组件能够经过组合运用来构建杂乱的数据协议处理逻辑,以进步代码的可重用性和可维护性。
16. 什么是 Netty 的 BootStrap,它的效果是什么?
Netty的Bootstrap
是一个用于发动和装备Netty
客户端和服务器的东西类。它供给了一组简略易用的办法,使得创立和装备Netty运用程序变得愈加容易。
Bootstrap
类供给了一些办法,能够设置服务器或客户端的选项和特点,以及为ChannelPipeline
装备handler
,以处理传入或传出的数据。一旦完结装备,运用Bootstrap
发动客户端或服务器。
在Netty
运用程序中,Bootstrap
有两个首要效果:
- 作为
Netty
服务器发动的进口点:经过Bootstrap
发动一个Netty
服务器,能够在指定的端口上监听传入的衔接,而且能够设置服务器的选项和特点。 - 作为
Netty
客户端发动的进口点:经过Bootstrap
发动一个Netty
客户端,能够衔接到长途服务器,而且能够设置客户端的选项和特点。
17.Netty的IO模型是什么?与传统的BIO和NIO有什么不同?
Netty
的IO
模型是依据事情驱动的NIO(Non-blocking IO)
模型。在传统的BIO(Blocking IO)
模型中,每个衔接都需求一个独立的线程来处理读写事情,当衔接数过多时,线程数量就会爆破式增加,导致体系功用急剧下降。而在NIO
模型中,一个线程能够一起处理多个衔接的读写事情,大大下降了线程的数量和切换开支,进步了体系的并发功用和吞吐量。
与传统的NIO
模型比较,Netty
的NIO
模型有以下不同点:
-
Netty
运用了Reactor
方法,将IO
事情分发给对应的Handler
处理,使得运用程序能够更便利地处理网络事情。 -
Netty
运用了多线程模型,将Handler
的处理逻辑和IO
线程别离,防止了IO
线程被堵塞的情况。 -
Netty
支撑多种Channel
类型,能够依据运用场景挑选不同的Channel
类型,如NIO、EPoll、OIO
等。
18. 怎么在Netty中完结TCP粘包/拆包的处理?
在TCP
传输进程中,由于TCP
并不了解上层运用协议的音讯鸿沟,会将多个小音讯组合成一个大音讯,或许将一个大音讯拆分成多个小音讯发送。这种现象被称为TCP粘包/拆包问题。在Netty中,能够经过以下几种办法来解决TCP粘包/拆包问题:
-
音讯定长:将音讯固定长度发送,例如每个音讯都是固定的
100
字节。在接纳端,依据固定长度对音讯进行拆分。
// 编码器,将音讯的长度固定为100字节
pipeline.addLast("frameEncoder", new LengthFieldPrepender(2));
pipeline.addLast("messageEncoder", new StringEncoder(CharsetUtil.UTF_8));
// 解码器,依据固定长度对音讯进行拆分
pipeline.addLast("frameDecoder", new LengthFieldBasedFrameDecoder(100, 0, 2, 0, 2));
pipeline.addLast("messageDecoder", new StringDecoder(CharsetUtil.UTF_8));
-
音讯分隔符:将音讯以特定的分隔符分离隔,例如以”
\r\n
“作为分隔符。在接纳端,依据分隔符对音讯进行拆分。
// 编码器,以"\r\n"作为音讯分隔符
pipeline.addLast("frameEncoder", new DelimiterBasedFrameEncoder("\r\n"));
pipeline.addLast("messageEncoder", new StringEncoder(CharsetUtil.UTF_8));
// 解码器,依据"\r\n"对音讯进行拆分
pipeline.addLast("frameDecoder", new DelimiterBasedFrameDecoder(1024, Delimiters.lineDelimiter()));
pipeline.addLast("messageDecoder", new StringDecoder(CharsetUtil.UTF_8));
- 音讯头部加长度字段:在音讯的头部加上表明音讯长度的字段,在发送端发送音讯时先发送音讯长度,再发送音讯内容。在接纳端,先读取音讯头部的长度字段,再依据长度读取音讯内容。
// 编码器,将音讯的长度参加音讯头部
pipeline.addLast("frameEncoder", new LengthFieldPrepender(2));
pipeline.addLast("messageEncoder", new StringEncoder(CharsetUtil.UTF_8));
// 解码器,先读取音讯头部的长度字段,再依据长度读取音讯内容
pipeline.addLast("frameDecoder", new LengthFieldBasedFrameDecoder(1024, 0, 2, 0, 2));
pipeline.addLast("messageDecoder", new StringDecoder(CharsetUtil.UTF_8));
19. Netty怎么处理大文件的传输?
在Netty
中,能够经过运用ChunkedWriteHandler
处理大文件的传输。ChunkedWriteHandler
是一个编码器,能够将大文件切分成多个Chunk
,并将它们以ChunkedData
的方法写入管道,这样就能够防止一次性将整个文件读入内存,下降内存占用。
详细运用办法如下:
- 在服务端和客户端的
ChannelPipeline
中增加ChunkedWriteHandler
。
pipeline.addLast(new ChunkedWriteHandler());
- 在服务端和客户端的事务逻辑处理器中,接纳并处理
ChunkedData
。
public class MyServerHandler extends SimpleChannelInboundHandler<Object> {
@Override
protected void channelRead0(ChannelHandlerContext ctx, Object msg) throws Exception {
if (msg instanceof HttpRequest) {
HttpRequest request = (HttpRequest) msg;
// 处理HTTP恳求
// ...
} else if (msg instanceof HttpContent) {
HttpContent content = (HttpContent) msg;
// 处理HTTP内容
if (content instanceof LastHttpContent) {
// 处理完整个HTTP恳求
// ...
} else if (content instanceof HttpChunkedInput) {
HttpChunkedInput chunkedInput = (HttpChunkedInput) content;
// 处理ChunkedData
while (true) {
HttpContent chunk = chunkedInput.readChunk(ctx.alloc());
if (chunk == null) {
break;
}
// 处理单个Chunk
// ...
}
}
}
}
}
- 在客户端向服务端发送数据时,将需求传输的文件包装成
ChunkedFile
并写入管道。
public void sendFile(Channel channel, File file) throws Exception {
RandomAccessFile raf = new RandomAccessFile(file, "r");
DefaultFileRegion fileRegion = new DefaultFileRegion(raf.getChannel(), 0, raf.length());
HttpRequest request = new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.POST, "/");
HttpUtil.setContentLength(request, raf.length());
channel.write(request);
channel.writeAndFlush(new HttpChunkedInput(new ChunkedFile(raf, 0, file.length(), 8192)));
}
在传输大文件时,还需求留意以下几点:
- 运用
ChunkedFile
时需求指定Chunk
的巨细,依据实践情况挑选适宜的巨细,一般主张不要超越8KB
。 - 为了防止大文件传输进程中对网络造成影响,能够在服务端和客户端的
ChannelPipeline
中增加WriteBufferWaterMark
,限制写入缓冲区的巨细。
pipeline.addLast(new WriteBufferWaterMark(8 * 1024, 32 * 1024));
20. 怎么运用Netty完结心跳机制?
在Netty
中,能够经过完结一个定时使命来完结心跳机制。详细来说,就是在客户端和服务端之间定时相互发送心跳包,以检测衔接是否仍然有效。
以下是运用Netty完结心跳机制的根本进程:
- 定义心跳音讯的类型。
public class HeartbeatMessage implements Serializable {
// ...
}
- 在客户端和服务端的
ChannelPipeline
中增加IdleStateHandler
,用于触发定时使命。
pipeline.addLast(new IdleStateHandler(0, 0, 60, TimeUnit.SECONDS));
- 在客户端和服务端的事务逻辑处理器中,重写
userEventTriggered
办法,在触发定时使命时发送心跳包。
public class MyServerHandler extends SimpleChannelInboundHandler<Object> {
@Override
public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
if (evt instanceof IdleStateEvent) {
IdleStateEvent event = (IdleStateEvent) evt;
if (event.state() == IdleState.READER_IDLE) {
// 读闲暇,发送心跳包
ctx.writeAndFlush(new HeartbeatMessage());
}
} else {
super.userEventTriggered(ctx, evt);
}
}
}
- 在客户端和服务端的事务逻辑处理器中,重写
channelRead
办法,接纳并处理心跳包。
public class MyClientHandler extends SimpleChannelInboundHandler<Object> {
@Override
protected void channelRead0(ChannelHandlerContext ctx, Object msg) throws Exception {
if (msg instanceof HeartbeatMessage) {
// 收到心跳包,不做处理
return;
}
// 处理其他音讯
// ...
}
}
需求留意的是,由于心跳包不需求传输很多数据,因而主张运用Unpooled.EMPTY_BUFFER
作为心跳包的内容。另外,心跳间隔的时刻应依据实践情况设置,一般主张设置为衔接的超时时刻的一半。
21. Netty中怎么完结SSL/TLS加密传输?
在 Netty
中完结 SSL/TLS
加密传输,需求经过 SSLHandler
来进行处理。一般情况下,SSLHandler
需求在 ChannelPipeline
中作为最终一个handler
增加。
以下是完结 SSL/TLS
加密传输的示例代码:
// 创立 SSLContext 目标,用于构建 SSLEngine
SSLContext sslContext = SSLContext.getInstance("TLS");
// 初始化 SSLContext
KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
KeyStore keyStore = KeyStore.getInstance("JKS");
keyStore.load(new FileInputStream("server.jks"), "password".toCharArray());
keyManagerFactory.init(keyStore, "password".toCharArray());
TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
trustManagerFactory.init(keyStore);
sslContext.init(keyManagerFactory.getKeyManagers(), trustManagerFactory.getTrustManagers(), null);
// 获取 SSLEngine
SSLEngine sslEngine = sslContext.createSSLEngine();
sslEngine.setUseClientMode(false);
// 增加 SslHandler 到 ChannelPipeline 中
pipeline.addLast("ssl", new SslHandler(sslEngine));
22. NioEventLoopGroup 默许的结构函数会起多少线程?
默许情况下,NioEventLoopGroup
的结构函数会依据可用的处理器中心数 (availableProcessors()
) 创立相应数量的线程。
详细来说,NioEventLoopGroup
的默许结构函数内部调用了另一个结构函数,其参数 nThreads
的默许值为 0
,表明运用默许线程数。而默许线程数的核算办法就是调用 Runtime.getRuntime().availableProcessors()
办法获取当前机器可用的处理器中心数。
因而,假如你在一台四核的机器上创立了一个默许的 NioEventLoopGroup
实例,那么它就会运用四个线程。假如你想要修正线程数,能够调用 NioEventLoopGroup
的其他结构函数,并传入自定义的线程数。
23. 怎么运用Netty完结WebSocket协议?
在 Netty
中完结 WebSocket
协议,需求运用 WebSocketServerProtocolHandler
进行处理。WebSocketServerProtocolHandler
是一个 ChannelHandler
,能够将 HTTP
晋级为 WebSocket
并处理 WebSocket
帧。
以下是完结 WebSocket
协议的示例代码:
// 增加 HTTP 恳求解码器
pipeline.addLast("httpDecoder", new HttpRequestDecoder());
// 增加 HTTP 响应编码器
pipeline.addLast("httpEncoder", new HttpResponseEncoder());
// 增加 HTTP 聚合器
pipeline.addLast("httpAggregator", new HttpObjectAggregator(65536));
// 增加 WebSocket 服务器协议处理器
pipeline.addLast("webSocketHandler", new WebSocketServerProtocolHandler("/ws"));
// 增加自定义的 WebSocket 处理器
pipeline.addLast("handler", new MyWebSocketHandler());
在以上示例代码中,WebSocketServerProtocolHandler
的参数 “/ws” 表明 WebSocket
恳求的 URL
途径,MyWebSocketHandler
是自定义的 WebSocket
处理器。
24. Netty 高功用体现在哪些方面?
- 异步非堵塞
I/O
模型:Netty
运用依据NIO
的异步非堵塞I/O
模型,能够大大进步网络通讯功率,削减线程的堵塞等候时刻,然后进步运用程序的响应速度和吞吐量。 - 零复制技能:
Netty
支撑零复制技能,能够防止数据在内核和用户空间之间的屡次复制,削减了数据复制的次数,然后进步了数据传输的功率和功用。 - 线程模型优化:
Netty
的线程模型非常灵敏,能够依据不同的事务场景挑选不同的线程模型。例如,关于低推迟和高吞吐量的场景,能够挑选Reactor
线程模型,关于I/O
操作比较简略的场景,能够挑选单线程模型。 - 内存池技能:
Netty
供给了一套依据内存池技能的ByteBuf
缓冲区,能够重用现已分配的内存空间,削减内存的分配和收回次数,进步内存运用功率。 - 处理器链式调用:
Netty
的ChannelHandler
能够依照必定的次序组成一个处理器链,当事情产生时,会依照处理器链的次序顺次调用处理器,然后完结对事情的处理。这种处理办法比传统的多线程处理办法愈加高效,削减了线程上下文切换和锁竞赛等问题。
25. Netty 和 Tomcat 的差异?
Netty 和 Tomcat
都是 Java Web
运用服务器,可是它们之间存在一些差异:
- 底层网络通讯模型不同:
Tomcat
是依据堵塞的BIO(Blocking I/O)
模型完结的,而Netty
是依据NIO(Non-Blocking I/O)
模型完结的。 - 线程模型不同:
Tomcat
运用传统的多线程模型,每个恳求都会分配一个线程,而Netty
运用EventLoop
线程模型,每个EventLoop
担任处理多个衔接,经过线程池管理EventLoop
。 - 协议支撑不同:
Tomcat
内置支撑HTTP 和 HTTPS
协议,而Netty
不仅支撑HTTP 和 HTTPS
协议,还支撑TCP、UDP 和 WebSocket
等多种协议。 - 代码杂乱度不同:由于
Tomcat
支撑的功用比较全面,所以其代码相对较为杂乱,而Netty
的代码相对比较简练、精简。 - 运用场景不同:
Tomcat
合适于处理比较传统的Web
运用程序,如传统的MVC
方法Web
运用程序;而Netty
更合适于高功用、低推迟的网络运用程序,如游戏服务器、即时通讯服务器等。
26. 服务端Netty的作业架构图
┌───────┐ ┌───────┐
│ Channel │◀───────│ Socket│
│Pipeline │ │ │
└───────┘ └───────┘
▲ │
│ │
┌─────────┴─────────┐ │
│ │ │
▼ ▼ ▼
┌──────────────┐ ┌──────────────┐ ┌──────────────┐
│EventLoopGroup│ │EventLoopGroup│ │EventLoopGroup│
│ boss │ │ work │ │ work │
└──────────────┘ └──────────────┘ └──────────────┘
▲ ▲ ▲
│ │ │
┌────────┴─────────┐ ┌────────┴─────────┐
│ NioServerSocketChannel │ NioSocketChannel │ ...
└──────────────────┘ └──────────────────┘
整个服务端 Netty 的作业架构图包含了以下几个部分:
- ChannelPipeline:管道处理器,用于处理入站或出站事情,对数据进行编解码、处理事务逻辑等。
- Channel:通道,对应底层的
Socket
衔接,用于收发网络数据。 - EventLoopGroup:事情循环组,包含了多个事情循环(
EventLoop
),每个事情循环担任处理多个通道上的事情。 - EventLoop:事情循环,担任监听注册到该循环的多个通道上的事情,然后依据事情类型将事情派发给对应的处理器。
- NioServerSocketChannel:NIO 服务端通道,用于承受客户端的衔接。
- NioSocketChannel:NIO 客户端通道,用于和服务端进行数据通讯。
在服务端发动时,会创立一个或多个 EventLoopGroup
。其间一个 EventLoopGroup
作为boss
线程池,用于承受客户端的衔接恳求,并将衔接恳求分发给work
线程池中的某个 EventLoop
。work
线程池中的EventLoop
担任处理现已衔接的客户端的数据通讯。每个 EventLoop
担任处理一个或多个 NioSocketChannel
,并维护该通道的事情行列,当事情产生时,将事情增加到事情行列中,并将事情派发到管道处理器中进行处理。
27. 简略聊聊:Netty的线程模型的三种运用办法?
Netty的线程模型有三种运用办法,别离是单线程模型、多线程模型和主从多线程模型。
-
单线程模型:一切的
I/O
操作都由同一个线程来履行。虽然这种办法并不合适高并发的场景,可是它具有简略、快速的优点,适用于处理I/O
操作非常快速的场景,例如传输小文件等。 -
多线程模型:一切的
I/O
操作都由一组线程来履行,其间一个线程担任监听客户端的衔接恳求,其他线程担任处理I/O
操作。这种办法能够支撑高并发,可是线程上下文切换的开支较大,适用于处理I/O
操作较为耗时的场景。 -
主从多线程模型:一切的
I/O
操作都由一组NIO
线程来履行,其间一个主线程担任监听客户端的衔接恳求,其他从线程担任处理I/O
操作。这种办法将承受衔接和处理I/O
操作分隔,防止了线程上下文切换的开支,一起又能支撑高并发,适用于处理I/O操作耗时较长的场景。
28. Netty 是怎么坚持长衔接的
-
心跳机制:运用心跳机制能够定时向服务器发送一个简略的数据包,以坚持衔接处于活动状况。假如在一段时刻内没有收到心跳包,就能够以为衔接现已断开,然后及时从头树立衔接。
Netty
供给了IdleStateHandler
处理器,能够便利地完结心跳机制。 -
断线重连机制:在网络不安稳的情况下,衔接可能会不可防止地断开。为了防止由于网络反常导致运用程序不能正常作业,能够完结断线重连机制,定时查看衔接状况,并在衔接断开时尝试从头衔接。
Netty
供给了ChannelFutureListener
接口和ChannelFuture
目标,能够便利地完结断线重连机制。 -
依据HTTP/1.1协议的长衔接:
HTTP/1.1
协议支撑长衔接,能够在一个TCP
衔接上屡次发送恳求和响应。在Netty
中,能够运用HttpClientCodec和HttpObjectAggregator
处理器,完结依据HTTP/1.1
协议的长衔接。 -
WebSocket协议:
WebSocket
协议也支撑长衔接,能够在一个TCP
衔接上双向通讯,完结实时数据交换。在Netty
中,能够运用WebSocketServerProtocolHandler
和WebSocketClientProtocolHandler
处理器,完结WebSocket
协议的长衔接。
29. Netty 发送音讯有几种办法?
在 Netty
中,发送音讯首要有以下三种办法:
-
Channel.write(Object msg) :经过
Channel
写入音讯,音讯会被缓存到Channel
的发送缓冲区中,等候下一次调用flush()
将音讯发送出去。 -
ChannelHandlerContext.write(Object msg) :经过
ChannelHandlerContext
写入音讯,与Channel.write(Object msg)
比较,ChannelHandlerContext.write(Object msg)
会将音讯写入到ChannelHandlerContext
的发送缓冲区中,等候下一次调用flush()
将音讯发送出去。 -
ChannelHandlerContext.writeAndFlush(Object msg) :经过
ChannelHandlerContext
写入并发送音讯,等同于连续调用ChannelHandlerContext.write(Object msg)
和ChannelHandlerContext.flush()
。
在运用上述三种办法发送音讯时,需求留意到写操作可能会失败或被推迟,因而需求在发送音讯时进行必定的错误处理或许设置超时时刻。另外,也能够运用 Netty
供给的 ChannelFuture
目标来监听操作成果或许进行异步操作。
30. Netty 支撑哪些心跳类型设置?
在 Netty
中,能够经过以下几种办法完结心跳机制:
-
IdleStateHandler :
Netty
内置的闲暇状况检测处理器,支撑多种闲暇状况检测(如读闲暇、写闲暇、读写闲暇)。 -
自定义心跳检测机制 :能够经过自定义完结
ChannelInboundHandler
接口的处理器来完结心跳检测,例如能够经过计时器或许线程来定时发送心跳包,或许经过对长途端口的衔接状况进行检测等办法完结。 -
运用心跳应对 :在运用层面定义心跳恳求和应对音讯,经过
ChannelInboundHandler
处理器监听接纳到的心跳恳求音讯,并回来心跳应对音讯,来完结心跳检测。假如一段时刻内未收到对方的心跳应对音讯,则以为衔接现已失效。
需求留意的是,为了防止因心跳机制导致的网络负载过大或许频频的衔接断开和重连,应该依据详细事务场景挑选合适的心跳类型和频率。
31. Netty的内存管理机制是什么?
Netty
的内存管理机制首要是经过 ByteBuf
类完结的。ByteBuf
是 Netty
自己完结的一个可扩展的字节缓冲区类,它在 JDK
的 ByteBuffer
的根底上做了很多优化和改善。
Netty
的 ByteBuf
的内存管理首要分为两种办法:
- 堆内存:
ByteBuf
以普通的字节数组为根底,在JVM
堆上分配内存。这种办法适用于小型数据的传输,如传输的是文本、XML
等数据。 - 直接内存:
ByteBuf
运用操作体系的堆外内存,由操作体系分配和收回内存。这种办法适用于大型数据的传输,如传输的是音视频、大型图片等数据。
关于堆内存,Netty
选用了类似于JVM
的分代内存管理机制,将缓冲区分为三种类型**:堆缓冲区、直接缓冲区、复合缓冲区**。Netty 会依据不同的运用场景和内存需求来决议运用哪种类型的缓冲区,然后进步内存利用率。
在运用 ByteBuf
时,Netty
还完结了一些优化和特别处理,如池化缓冲区、零复制等技能,以进步内存的利用率和功用的体现。
32. Netty 中怎么完结高可用和负载均衡?
Netty
自身并没有供给高可用和负载均衡的功用,但能够结合其他技能来完结这些功用。下面介绍一些常用的方案:
- 高可用:经过在多台服务器上部署同一个运用程序完结高可用。能够运用负载均衡器来将恳求分配给不同的服务器,当某台服务器呈现毛病时,负载均衡器能够将恳求转发给其他可用的服务器。常用的负载均衡器包含
Nginx、HAProxy
等。 - 负载均衡:负载均衡是将恳求分配给多台服务器的进程,常用的负载均衡算法包含轮询、随机、权重等。在
Netty
中能够运用多个EventLoop
来处理恳求,将恳求分配给不同的EventLoop
,然后完结负载均衡。另外,能够运用第三方结构,如Zookeeper、Consul
等,来完结服务注册、发现和负载均衡。 - 高可用与负载均衡的结合:能够运用多台服务器来完结高可用和负载均衡。在每台服务器上部署同一个运用程序,并运用负载均衡器来分配恳求。当某台服务器呈现毛病时,负载均衡器能够将恳求转发给其他可用的服务器,然后确保高可用和负载均衡。