NIO、AIO、BIO有什么区别?
同步阻塞的BIO、同步非阻塞的NIO、异步非阻塞的AIO。
NIO和IO有什么区别?
IO是多线程的,阻塞的。NIO,是同步的非阻塞IO。
IO面向Stream(流),而NIO面向Buffer(缓冲区)。
IO是多个线程的,不存在Selector。而Java NIO的Selector(选择器)允许一个单独的线程来监视多个Channel(输入通道)。
讲一下NIO
NIO,同步非阻塞,IO多路复用。
NIO包括Channel、Select、Buffer这些。
Selector允许单线程处理多个 Channel。
讲一下Netty
Q:Netty 高性能表现在哪些方面?
- IO 模型:高性能epoll/select 模型。
- 线程模型:通过 主从Reactors多线程模型 ,实现异步非阻塞(异步事件驱动)架构,用最少的资源做更多的事。
- 内存零拷贝:尽量减少不必要的内存拷贝,实现了更高效率的传输。
- 内存池设计:申请的内存可以重用,主要指直接内存。内部实现是用一颗二叉查找树管理内存分配情况。
- 对象池设计:Java对象可以重用,主要指Minior GC非常频繁的对象,如ByteBuffer。并且,对象池使用无锁架构,性能非常高。
- mpsc无锁编程:串形化处理读写, 避免使用锁带来的性能开销。
- 高性能序列化协议:支持 protobuf 等高性能序列化协议。
讲一下Netty的线程模型
主从Reactor多线程模型有多个Reactor:MainReactor和SubReactor:
- MainReactor负责客户端的连接请求,并将请求转交给SubReactor
- SubReactor负责相应通道的IO读写请求
- 非IO请求(具体逻辑处理)的任务则会直接写入队列,等待worker threads进行处理
讲一下Netty的模块和组件
详情见: https://blog.csdn.net/sinat_32502451/article/details/133934402
Netty服务端过程初始化并启动过程
基本过程如下:
1 初始化创建2个NioEventLoopGroup,其中boosGroup用于Accetpt连接建立事件并分发请求, workerGroup用于处理I/O读写事件和业务逻辑。
2 基于ServerBootstrap(服务端启动引导类),配置EventLoopGroup、Channel类型,连接参数、配置入站、出站事件handler。
3 绑定端口,开始工作。
Netty的架构图
Netty的线程模型的三种使用方式
单线程模型
多线程模型
主从多线程模型:从一个 主线程 NIO 线程池中选择一个线程作为 Acceptor 线程,绑定监听端口,接收客户端连接的连接,其他线程负责后续的接入认证等工作。连接建立完成后,Sub NIO 线程池负责具体处理 I/O 读写。
select、poll和epoll的区别是什么?
select、poll和epoll都是用于实现 I/O 多路复用的方式,可以在同一时间内监听多个文件描述符的就绪状态。
select 使用位图来表示文件描述符的状态。调用 select 时,内核需要遍历整个位图,检查每个文件描述符是否就绪。这种轮询的方式在连接数量很少时还是很有效的,但当连接数量增多时,性能会下降。 select 文件描述符个数只能支持 2048 个,不过 select 的跨平台性比较好,几乎所有的平台都可以支持。
poll 使用链表结构来表示文件描述符的状态,没有最大连接数的限制。和 select 函数一样,poll 返回后,需要轮询来获取就绪的描述符,因此随着监视的描述符数量的增长,其效率也会线性下降。
epoll 是 Linux 特有的一种方式,它使用了事件驱动的模型,没有最大连接数的限制。它将文件描述符添加到 epoll 的事件集合中,等待事件的发生。与 select 和 poll 不同的是,epoll 不需要轮询,它使用回调的方式,只关注真正发生事件的文件描述符。这使得epoll在大规模高并发连接下具有卓越的性能。
应用场景上,select和poll适用于连接数量较少的场景,而epoll则适用于需要处理大规模并发连接且性能要求较高的场景。
默认情况 Netty 起多少线程?何时启动?
Netty 默认是 CPU 处理器数的两倍,bind 完之后启动。
TCP 粘包/拆包的原因及解决方法 ?
拆包:一个完整的应用包可能会被 TCP 拆分成多个包进行发送。
粘包:也可能把小应用包的封装成一个大的 TCP数据包发送。
应用程序写入的字节大小大于套接字发送缓冲区的大小,会发生拆包现象,
而应用程序写入数据小于套接字缓冲区大小,网卡将应用多次写入的数据发送到网络上,这将会发生粘包现象。
解决方法 :
Netty中提供了多个 Decoder 解析类 用于解决上述问题:
FixedLengthFrameDecoder 、LengthFieldBasedFrameDecoder ,固定长度是消息头指定消息长度的一种形式,进行粘包拆包处理的。
LineBasedFrameDecoder 、DelimiterBasedFrameDecoder ,换行是于指定消息边界方式的一种形式,进行消息粘包拆包处理的。
Netty 发送消息有几种方式?
Netty 有两种发送消息的方式:
直接写入 Channel 中,消息从 ChannelPipeline 当中尾部开始移动;
写入和 ChannelHandler 绑定的 ChannelHandlerContext 中,消息从 ChannelPipeline 中的下一个 ChannelHandler 中移动。
什么是 Netty 的零拷贝?
Netty 的零拷贝主要包含三个方面:
- Netty 的接收和发送 ByteBuffer 采用 DIRECT BUFFERS,使用堆外直接内存进行 Socket 读写,不需要进行字节缓冲区的二次拷贝。如果使用传统的堆内存(HEAP BUFFERS)进行 Socket 读写,JVM 会将堆内存 Buffer 拷贝一份到直接内存中,然后才写入 Socket 中。相比于堆外直接内存,消息在发送过程中多了一次缓冲区的内存拷贝。
- Netty 提供了组合 Buffer 对象,可以聚合多个 ByteBuffer 对象,用户可以像操作一个 Buffer 那样方便的对组合 Buffer 进行操作,避免了传统通过内存拷贝的方式将几个小 Buffer 合并成一个大的 Buffer。
- Netty 的文件传输采用了 transferTo 方法,它可以直接将文件缓冲区的数据发送到目标 Channel,避免了传统通过循环 write 方式导致的内存拷贝问题。
参考资料
https://blog.csdn.net/crazymakercircle/article/details/124588880