Netty——基础知识点整理

1. Netty 是什么?

Netty 是一款异步的事件驱动的网络应用程序框架,支持快速地开发可维护的高性能的面向协议的服务器 和客户端。

2. 为什么要使用Netty?

这个问题也有其他的问法,比如原生NIO有什么问题呢

  1. NIO 的类库和API繁杂,使用麻烦:需要熟练掌握 SelectorServerSocketChannelSocketChannelByteBuffer 等。

  2. 需要具备其他的额外技能:要熟悉 Java 多线程编程,因为 NIO 编程涉及到 Reactor 模式,你必须对多线程和网络编程非常熟悉,才能编写出高质量的 NIO 程序。

  3. 开发工作量和难度都非常大:例如客户端面临断连重连、网络闪断、半包读写、失败缓存、网络拥塞和异常流的处理等等。

  4. JDK NIO 的 Bug:例如臭名昭著的 Epoll Bug,它会导致 Selector空轮询,最终导致 CPU 100%。直到 JDK 1.7 版本该问题仍旧存在,没有被根本解决。

3. Netty有什么优点

设计

        1、统一的 API,支持多种传输类型,阻塞的和非阻塞的

        2、简单而强大的线程模型

        3、真正的无连接数据报套接字支持

        4、链接逻辑组件以支持复用

易用性

        详实的 Javadoc 和大量的示例集

性能

        1、拥有比 Java 的核心 API 更高的吞吐量以及更低的延迟

        2、得益于池化和复用,拥有更低的资源消耗

        3、最少的内存复制

健壮性

        1、不会因为慢速、快速或者超载的连接而导致 OutOfMemoryError

        2、消除在高速网络中 NIO 应用程序常见的不公平读/写比率

安全性

        1、完整的 SSL/TLS 以及 StartTLS 支持

        2、可用于受限环境下,如 Applet 和 OSGI

4. netty高性能主要依赖了哪些特性

        1、IO 线程模型:同步非阻塞,用最少的资源做更多的事。

        2、内存零拷贝:尽量减少不必要的内存拷贝,实现了更高效率的传输。

        3、内存池设计:申请的内存可以重用,主要指直接内存。内部实现是用一颗二叉查找树管理内存分配情况。

        4、串形化处理读写:避免使用锁带来的性能开销。

        5、高性能序列化协议:支持 protobuf 等高性能序列化协议。

5. 为什么BIO比NIO性能差?简单讲讲区别

BIO:服务器实现模式为一个连接一个线程,即客户端有连接请求时服务器端就需要启动一个线程进行处理,如果这个连接不做任何事情会造成不必要的线程开销。

传统IO的缺点:

       在任何 时候都可能有大量的线程处于休眠状态,只是等待输 入或者输出数据就绪,这可能算是一种资源浪费
       需要为每个线程的调用栈都分配内存,其默认值 大小区间为 64 KB 到 1 MB,具体取决于操作系统
       即使 Java 虚拟机(JVM)在物理上可以支持非常大数量的线程,但是远在到达该极限之前,上下文切换所带来的开销就会带来麻烦

NIO:一个请求一个线程,但客户端发送的连接请求都会注册到多路复用器上,多路复用器轮询到连接有I/O请求时才启动一个线程进行处理,相对于BIO来说比较灵活。

6. 简单说下 BIO、NIO 和 AIO区别

概念本质不同

BIO:一个连接一个线程,客户端有连接请求时服务器端就需要启动一个线程进行处理。线程开销大。

NIO:一个请求一个线程,但客户端发送的连接请求都会注册到多路复用器上,多路复用器轮询到连接有I/O请求时才启动一个线程进行处理。

AIO:一个有效请求一个线程,客户端的I/O请求都是由OS先完成了再通知服务器应用去启动线程进行处理。

底层实现区别:

  1. BIO 以流的方式处理数据,而 NIO 以块的方式处理数据,块 I/O 的效率比流 I/O 高很多

  2. BIO 是阻塞的,NIO 则是非阻塞的

  3. BIO基于字节流和字符流进行操作,而 NIO 基于 Channel(通道)和 Buffer(缓冲区)进行操作,数据总是从通道读取到缓冲区中,或者从缓冲区写入到通道中。Selector(选择器)用于监听多个通道的事件(比如:连接请求,数据到达等),因此使用单个线程就可以监听多个客户端通道

7. 说说NIO的主要组成

Buffer

一个可以读写数据的内存块,可以理解成是一个容器对象(含数组),该对象提供了一组方法,可以更轻松地使用内存块,缓冲区对象内置了一些机制,能够跟踪和记录缓冲区的状态变化情况。与 Channel 进行交互,数据是从Channel 读入缓冲区,从缓冲区写入 Channel 中的。

Channel

NIO的通道类似于流,但有些区别

  1. 通道可以同时进行读写,而流只能读或者只能写
  2. 通道可以实现异步读写数据
  3. 通道可以从缓存读数据,也可以写数据到缓存

Selector

        能够检测多个注册的通道上是否有事件发生(注意:多个Channel以事件的方式可以注册到同一个Selector),如果有事件发生,便获取事件然后针对每个事件进行相应的处理。这样就可以只用一个单线程去管理多个通道,也就是管理多个连接和请求。

8. 说说对于Netty的零拷贝理解

什么是零拷贝?

        从操作系统的角度来看,文件的传输不存在CPU的拷贝,只存在DMA拷贝(直接内存拷贝,不使用CPU完成)。零拷贝是网络编程的关键,很多性能优化都离不开它。

Netty对于零拷贝方式

        从操作系统的角度来看,文件的传输不存在CPU的拷贝,只存在DMA拷贝(直接内存拷贝,不使用CPU完成)。零拷贝是网络编程的关键,很多性能优化都离不开它。

        1、Netty 的接收和发送 ByteBuffer 采用 DIRECT BUFFERS,使用堆外直接内存进行 Socket 读写,不需要进行字节缓冲区的二次拷贝。如果使用传统的堆内存(HEAP BUFFERS)进行 Socket 读写,JVM 会将堆内存 Buffer 拷贝一份到直接内存中,然后才写入Socket中。相比于堆外直接内存,消息在发送过程中多了一次缓冲区的内存拷贝。

        2、Netty 提供了组合 Buffer 对象,可以聚合多个 ByteBuffer 对象,用户可以像操作一个 Buffer 那样方便的对组合 Buffer 进行操作,避免了传统通过内存拷贝的方式将几个小 Buffer 合并成一个大的 Buffer。

        Netty 的文件传输采用了 transferTo 方法,它可以直接将文件缓冲区的数据发送到目标 Channel,避免了传统通过循环 write 方式导致的内存拷贝问题。

9. 说说Netty线程模型

   Netty线程模型主要基于主从 Reactor 多线程模型做了一定的改进,其中主从Reactor多线程模型有多个 Reactor

        内部实现了两个线程池,boss 线程池和 work 线程池,其中 boss 线程池的线程负责处理请求的连接事件,当接收到连接事件的请求时,把对应的socket封装到一个NioSocketChannel 中,并交给 work 线程池,其中 work 线程池负责请求的 read 和 write 事件,由对应的 Handler 处理。

        其本质将线程连接和具体的业务处理区分开来。

10. Netty中有哪些重要组件

        Bootstrap、ServerBootstrap:一个 Netty 应用通常由一个 Bootstrap 开始,主要作用是配置整个 Netty 程序,串联各个组件,Netty 中 Bootstrap 类是客户端程序的启动引导类,ServerBootstrap 是服务端启动引导类。

        Future、ChannelFutureNetty 中所有的 IO 操作都是异步的,不能立刻得知消息是否被正确处理。但是可以过一会等它执行完成或者直接注册一个监听,具体的实现就是通过 Future 和 ChannelFutures,他们可以注册一个监听,当操作执行成功或失败时监听会自动触发注册的监听事件。

        Channel:Netty 网络操作抽象类,它除了包括基本的 I/O 操作,如 bind、connect、read、write 等

        Selector:基于 Selector 对象实现I/O多路复用,通过 Selector 一个线程可以监听多个连接的 Channel 事件,Selector 内部的机制就可以自动不断地查询(Select) 这些注册的 Channel是否有已就绪的I/O 事件(例如可读,可写,网络连接完成等)

        ChannelHandler:充当了所有处理入站和出站数据的逻辑容器。ChannelHandler 主要用来处理各种事件,这里的事件很广泛,比如可以是连接、数据接收、异常、数据转换等。

        EventLoop:主要是配合 Channel 处理 I/O 操作,用来处理连接的生命周期中所发生的事情

        ChannelPipeline:为 ChannelHandler 链提供了容器,当 channel 创建时,就会被自动分配到它专属的 ChannelPipeline,这个关联是永久性的。

        ChannelHandlerContext:包 含 一 个 具 体 的 事 件 处 理 器 ChannelHandler , 同 时ChannelHandlerContext 中也绑定了对应的 pipeline 和 Channel 的信息,方便对 ChannelHandler进行调用。

11. 说说什么是拆包和粘包

        收发两端(客户端和服务器端)都要有一一成对的socket,因此,发送端为了将多个发给接收端的包,更有效的发给对方,使用了优化方法(Nagle算法),将多次间隔较小且数据量小的数据,合并成一个大的数据块,然后进行封包。这样做虽然提高了效率,但是接收端就难于分辨出完整的数据包了,由于TCP无消息保护边界,需要在接收端处理消息边界问题。这就是拆包和粘包问题。

比如:

上图中演示了以下几种情况:

  • 正常的理想情况,两个包恰好满足TCP缓冲区的大小或达到TCP等待时长,分别发送两个包;
  • 粘包:两个包较小,间隔时间短,发生粘包,合并成一个包发送;
  • 拆包:一个包过大,超过缓存区大小,拆分成两个或多个包发送;
  • 拆包和粘包:Packet1过大,进行了拆包处理,而拆出去的一部分又与Packet2进行粘包处理。
常见的解决方案

        对于粘包和拆包问题,常见的解决方案有四种:

  • 发送端将每个包都封装成固定的长度,比如100字节大小。如果不足100字节可通过补0或空等进行填充到指定长度;
  • 发送端在每个包的末尾使用固定的分隔符,例如\r\n。如果发生拆包需等待多个包发送过来之后再找到其中的\r\n进行合并;例如,FTP协议;
  • 将消息分为头部和消息体,头部中保存整个消息的长度,只有读取到足够长度的消息之后才算是读到了一个完整的消息;
  • 通过自定义协议进行粘包和拆包的处理。
12. Netty如何解决拆包和粘包问题

        Netty对解决粘包和拆包的方案做了抽象,提供了一些解码器(Decoder)来解决粘包和拆包的问题。如:

  • LineBasedFrameDecoder:以行为单位进行数据包的解码;
  • DelimiterBasedFrameDecoder:以特殊的符号作为分隔来进行数据包的解码;
  • FixedLengthFrameDecoder:以固定长度进行数据包的解码;
  • LenghtFieldBasedFrameDecode:适用于消息头包含消息长度的协议(最常用);

        基于Netty进行网络读写的程序,可以直接使用这些Decoder来完成数据包的解码。对于高并发、大流量的系统来说,每个数据包都不应该传输多余的数据(所以补齐的方式不可取),LenghtFieldBasedFrameDecode更适合这样的场景。

13. Netty主要采用了哪种设计模式

        Netty中利用到了众多的设计模式,有很多常见的设计模式,比如观察者模式、策略模式(在初始化 EventLoopGroup 时选择何种 DefaultEventExecutorChooserFactor-newChooser 时使用了),但是使用的最多的还是属于责任链模式,pipeline 就像一个责任链,ChannelHandler 就是其中处理逻辑的节点,通过自定义 Handler 来决定每个业务的执行逻辑。

14. 说说netty中的责任链设计模式

   netty的 pipeline 设计,就采用了责任链设计模式,底层采用双向链表的数据结构,将链上的各个处理器(Handler)串联起来。

        客户端每一个请求的到来,netty 认为,pipeline 中的所有的处理器都有机会处理它,因此,对于入栈的请求,全部从头节点开始往后传播,一直传播到尾节点。

        开发者可以自主的删除或者添加责任链中的某个节点。

15. Netty 是如何保持长连接的

什么是长连接?

        客户端和服务器之间定期发送的一种特殊的数据包,通知对方自己还在线, 以确保 TCP 连接的有效性。但是由于网络不稳定性,有可能在 TCP 保持长连接的过程中,由于某些突发情况, 例如网线被拔出, 突然掉电等。 会造成服务器和客户端的连接中断。在这些突发情况下, 如果恰好服务器和客户端之间没有交互的话,那么它们是不能在短时间内发现对方已经掉线的。

如何保持长连接?

        利用心跳维护长连接信息。

        在服务器和客户端之间一定时间内没有数据交互时,即处于 idle 状态时,客户端或服务器会发送一个特殊的数据包给对方,当接收方收到这个数据报文后, 也立即发送一个特殊的数据报文, 回应发送方, 此即一个 PING-PONG 交互。

        当某一端收到心跳消息后, 就知道了对方仍然在线, 这就确保 TCP 连接的有效性。

Netty有三种类型保持心跳类型

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

总结

        针对于 Netty ,本身利用比较广泛,比如国内流行的 RPC 框架 Dubbo ,由于开发者本身无须深入了解其原理就可以很好的进行业务开发,因此许多人对于Netty了解甚少,但是想要了解一些进阶的 Java 编程,Netty 是一个不错的学习框架。

  • 38
    点赞
  • 27
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值