您的位置:首页 > 科技 > 能源 > Netty 相关问题

Netty 相关问题

2024/12/23 8:31:46 来源:https://blog.csdn.net/longlongqin/article/details/142187494  浏览:    关键词:Netty 相关问题

传统网络编程存在的问题

传统网络编程存在以下问题:

  1. 线程创建开销:在Java中,创建线程需要调用操作系统API,这会消耗资源和时间。
  2. 内存占用高:线程本身占用内存,创建过多线程会导致内存资源紧张。
  3. CPU使用率高:线程在等待时需要进行上下文切换,这会增加CPU的负担。

改进,使用线程池:

  1. 优点:可以减少线程频繁创建、销毁的开销。
  2. 缺点:线程池中的线程可能会因为客户端处理时间长而被阻塞,导致资源浪费。

再改进,使用 NIO 网络通信非阻塞编程。

  1. 优势:NIO 网络编程也是使用线程池,但是它是非阻塞的。这就解决了线程池版网络编程中的阻塞问题。

BIO、NIO和AIO的区别?

BIO、NIO和AIO是Java中网络编程的三种不同的I/O模型,它们各自有不同的特点和适用场景。以下是它们的主要区别:

  1. BIO(Blocking I/O,阻塞I/O):在BIO模型中,服务器为每个客户端连接都创建一个线程来处理,这意味着每个线程都是阻塞的,即在等待I/O操作完成时,线程会被挂起,直到操作完成。

    • 优点是模型简单,易于理解和使用。

    • 缺点是当客户端数量增多时,线程数量也会线性增加,这会导致资源消耗大,性能下降,特别是在高并发场景下。

  2. NIO(Non-blocking I/O,非阻塞I/O):NIO是Java 1.4版本引入的,它支持面向缓冲区的I/O操作,可以更高效地处理数据。NIO使用缓冲区(Buffer)和通道(Channel)来进行数据的读写,而不是使用传统的流(Stream)。

    • 它支持非阻塞模式,可以通过Selector来管理多个Channel,一个线程可以同时处理多个Channel的I/O请求。

    • 优点是减少了线程的创建和销毁,提高了资源利用率和系统吞吐量。

  3. AIO(Asynchronous I/O,异步I/O):AIO是Java 7版本引入的,它支持真正的异步I/O操作

    • 在AIO模型中,I/O操作是异步的,当发起一个I/O请求后,系统会立即返回,不会阻塞当前线程,当I/O操作完成时,系统会通过回调函数来通知应用程序。

    • 优点是进一步提升了性能,减少了线程的阻塞时间,提高了系统的响应速度。

NIO 如何实现同步非阻塞的?

NIO 包含3个核心组件:Channel(通道)Buffer(缓冲区)Selector(选择器)监管者
在这里插入图片描述

Channel

Channel:NIO 中的 Channel 是通信的管道,类似于InputStream、OutputStream。用于读取和写入数据。因为Channel没有方向性,所以Buffer为了区分读写,引入了读模式、写模式进行区分。

  • Channel本身不能直接访问数据,只能与Buffer进行交互,所有的数据都是通过Buffer进行交互的。

Buffer

Channel读取或者写入的数据,都要写到Buffer中,才可以被程序操作。因为Channel没有方向性,所以Buffer为了区分读写,引入了读模式、写模式进行区分

  • 最常用的是ByteBuffer

NIO包中有多种Channel的实现:

  1. FileChannel:用于读取、写入、映射和操作文件的通道。
  2. DatagramChannel:能通过UDP读写网络中的数据。
  3. SocketChannel:能通过TCP读写网络中的数据。
  4. ServerSocketChannel:可以监听新进来的TCP连接,对每一个新进来的连接都会创建一个SocketChannel
半包、粘包

在NIO中,Buffer是NIO中存放数据的地方。假设在客户端发送了三句话,服务端接收数据时,计算机无法理解文本本身的含义,它只会按照Buffer设置的容量来截断数据,此时就会出现粘包、半包问题。比如下图:

在这里插入图片描述

  • 粘包(包含完整的一句话,但又包含了其他句子的部分):第一次读取的数据为 Hi sunshuai\nI l,发现此次接收的数据包含了第一句、第二句的部分,这种情况就是粘包
  • 半包(一句话只读取到了部分):第二次读取时,对于第二句I love you\n,本次只读取了ove you\n;对于第三句Do you love me?\n只读取了Do you 。像这种Buffer中包含第二句的结尾和第三句的开头,这就是半包

处理办法:

  1. 将句子进行特殊处理,比如以\n作为结束标志。在读取的时候读取到\n就知道是一句完整的话。
  2. 使用 compact()方法 进行处理,把第一次没有读取完的数据,向前移动和后面的内容进行整合。

Seletor(选择器)循环监听事件

在不使用Seletor时,需要不断的进行while()循环来进行监听客户端的连接、数据发送等动作。

而使用Seletor之后,Seletor就相当于一个监管者,它负责监控是否有客户端发送连接、客户端是否发送数据了。如果有,再去执行代码,这样就不用一直傻傻的where死循环了

  • Seletor并不是时时刻刻都处于监管状态,而是达到某些特殊的状态时才会触发监管。这些特殊状态有(一般只在服务端使用):
    • ACCEPT:即 服务端同意了客户端的连接,他们建立连接的时候。===》ServerSocketChannel
    • READWRITE:IO 通信的读写。===》SocketChannel 时

Selector 的主要操作:

  1. 创建监管者Selector selector = Selector.open();

  2. 指定被监控对象:(这里会监控serverSocketChannelACCEPT 连接状态

  3. SelectionKey selectionKey = serverSocketChannel.register(selector, 0, null);

  4. selectionKey.interestOps(SelectionKey.OP_ACCEPT);

  5. 进入阻塞等待模式selector.select();

  6. 只有当监控到了 有实际的连接 或者 读写操作 ,才会处理

在这里插入图片描述

reactor 模式

上面的 selector 作为监管者,监控服务端的ServerSocketChannel、(所有)客户端的SocketChannel【多路复用】。对服务端进行读、写操作的监控

会发现,selector 既要监控客户端的连接动作,还要监控 读、写动作,效率比较低。reactor 将连接器读写操作分别使用不同线程来负责,这样效率就会提升。

  • 主线程:负责连接器工作;
  • 从(子)线程:负责与客户端进行读写的交互操作。

零拷贝

NIO 之所以快,主要是它使用了零拷贝。接下来讲讲零拷贝为什么快。

普通的IO操作
  1. 首先,要知道一个Java 程序在内存中是分为:用户区(存储用户程序用到的何种数据)、内核区(内核空间为内核保留,是与操作系统打交道的区域)。具体到 java 程序中,JVM 中的内存对应用户区域的地址空间操作系统中的内存就对应内核区的地址空间

    在这里插入图片描述

  2. 而Java程序在IO操作时,时需要4次数据拷贝的:

    1. 读取文件:【两次数据拷贝】

      1. 先调用驱动程序将 物理硬盘中的文件数据 拷贝到 操作系统的高速页缓存中;

      2. 然后再将 高速页缓存 中文件数据 拷贝到 用户地址空间的应用缓存 中。

    2. 通过网络 发送数据:【两次数据拷贝】

      1. 先将用户地址应用缓存的数据 写入(拷贝)至 操作系统的 socket 缓存中;

      2. 然后再将socket 缓存中的数据 写入(拷贝)至 网卡中。

    image.png

改进:内存映射

在NIO中,可以调用内存映射相关的API。内存映射就是可以将 JVM 中的堆内存 与 操作系统内存 之间映射,此时 JVM 中 java 程序就直接操作的是 OS(操作系统)中的内存

在这里插入图片描述

不足::内存映射只是优化了 读取 的操作。而写操作依然需要经过 JVM 内存 --> 操作系统内存 --> 网卡。

再改进:零拷贝

零拷贝是指没有 JVM 参与的拷贝。只有操作系统与硬件之间的拷贝,零拷贝是 linux 内核实现的功能。具体如下:

在这里插入图片描述

  • linux2.1中的零拷贝sendFile():物理硬盘 ---》高速页缓存 --》socket 缓存--》网卡。进行了3次拷贝。
  • linux2.4中的零拷贝sendFile():物理硬盘 ---》高速页缓存--》网卡。只用2次拷贝。

因此,零拷贝速度快。

Netty

Netty是一个NIO客户服务器框架,它能够快速和容易地开发网络应用(如协议服务器和客户端)。它大大精简了网络编程(如TCP和UDP套接字服务器)。

Netty 是基于JAVA NIO类库,其架构特点是:异步非阻塞、基于事件驱动、高性能、高可靠性和高可定制性。

  • 事件驱动:是指服务端监控不同的事件,如acceptreadwrite。事件驱动的核心是依附于Selector来实现的。
  • 异步:是指 Worker 处理客户端的请求(IO 操作)时,让其他线程来完成具体的任务,而 Worker 本身可以继续接收其他客户端的请求。 ==》这种设计思路提升了效率。

Netty的用途:在分布式系统中,通常是跨进程交互的,需要通过网络通信。NIO虽然可以进行网络通信,但是NIO存在以下问题:

  1. API复杂难用,比如 Buffer的“指针”需要来回切换。
  2. 存在半包、粘包问题。
  3. 网络拥塞需要自己解决。

Netty 的心跳机制

TCP中也有类似的机制:保活机制。

  1. 首先,开启保活机制。

    在这里插入图片描述

  2. 探测过程:

    1. 如果在一段时间内没有数据传输,TCP 会发送一个保活探测报文给对端。这个探测报文通常是一个简单的 ACK 报文,不包含实际的数据。
    2. 如果对端正常存活,它会回复一个 ACK 报文。这样,连接就被认为是正常的,保活计时器会被重置。
    3. 如果对端没有回复,TCP 会在一定的时间间隔后再次发送探测报文。经过多次尝试后,如果仍然没有收到回复,TCP 就会认为对端已经断开连接,并关闭连接。

为什么有了TCP保活机制,还需要 Netty 的心跳机制呢?

img

答:

  1. 因为 TCP 保活机制 是作用在运输层的,心跳机制 是作用在 应用层上的。
  2. 如果运输层没问题,但应用层有问题就检测不出来。例如连接的双方网络正常(TCP 保活机制确保了双方是正常的),但是程序本身出现了问题(如死锁),导致双方无法通信。
  3. 因此才有了 Netty 的心跳机制。它作用在应用层,也就是说如果心跳机制检测双方处于活跃状态,就说明在 应用层、运输层 连接的双方都是活跃的。

Netty 的内存管理

Netty 中的内存相关是通过ByteBuf来实现的。Netty 中的ByteBuf 是对 NIO 的 ByteBuffer封装,Netty 网络通信过程中,底层数据是存储在ByteBuf中。

image-20210515171241565

ByteBuf 的特点:

  1. 可以自动扩容
  2. 提供读写的指针,方便操作。(ByteBuffer中没有读写指针,所以才需要进行 读模式、写模式的切换)
  3. 引入了内存的池化(类似于 连接池、线程池)
  4. ByteBuf 的操作中,引入了 零拷贝。(netty 中的零拷贝 不是不占用内存,而是尽可能的少占用内存)

在这里插入图片描述

  1. 创建ByteBuf如果不指定大小,netty 会默认分配256字节。ByteBuf最大可为Integer.MAX_VALUE
  2. ByteBuf中一个字符占 1 字节;一个 Int 类型占 4 字节。

ByteBuf 扩容规律

  1. 当容量小于 64 时,扩容规律为4 的 n 次方。(其实也就是:4、16、64);
  2. 当容量超过 64 时,扩容规律为原有大小*2。但不可超过ByteBuf的最大值Integer.MAX_VALUE

ByteBuf 的内存结构

  • ByteBuffer中需要来回切换 读、写模式。ByteBuf中没有读、写模式了,不用来回切换。而是使用读、写指针

在这里插入图片描述

ByteBuf 的内存释放

ByteBuf 的内存释放:①使用池化技术时,内存释放是将内存放回内存池中,并没有销毁;②不使用池化技术时,如果使用堆内存(而没使用直接内存),内存释放是否立即销毁 需要看 GC 的处理。

由于 Netty 在处理内存释放时,考虑到内存释放的情况复杂,Netty 让编程人员使用时,设计了统一的内存释放的接口。它是通过使用RefrenceCounted接口实现的。RefrenceCounted引用计数器,当ByteBuf的引用计数器为 0 时,表示ByteBuf可以被回收

Netty 和 Tomcat 的区别?

  1. 功能定位

    1. Netty:是一个异步事件驱动的网络应用程序框架,主要用于构建高性能的网络通信服务器和客户端。
    2. Tomcat:是一个 Java Web 应用服务器,主要用于运行 Java Servlet 和 JavaServer Pages(JSP)等 Web 应用程序
  2. 处理协议

    1. Netty:可以处理多种网络协议,不仅限于 HTTP,还包括自定义协议等。
    2. Tomcat:主要针对 HTTP 协议进行处理,也可以支持其他协议(可以通过插件等方式扩展),但对其他协议的支持相对较弱。
  3. 使用场景

    1. Netty:适用于需要自定义网络通信协议、对性能要求极高的场景,如金融交易系统的通信中间件、游戏服务器等。
    2. Tomcat:主要用于部署和运行 Java Web 应用,如企业内部管理系统、电子商务网站等。

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com