NIO

2021-01-17  本文已影响0人  愤怒的老照

0 前提

互联网服务端典型通信流程如下:


image.png

1 Unix五种网络编程模型

首先需要了解同步/异步,阻塞/非阻塞的区别和联系,一般来说,IO操作分为两个阶段:

1.1 阻塞IO

image.png

这种通信模型下,用户进程发起IO操作后,必须等待数据准备好,并等待数据从内核缓冲区复制到进程缓冲区中。整体表现为用户进程调用IO操作后一直阻塞,直到数据准备完成。Java BIO就是这种网络模型。

在这种网络模型下,需要为每个链接单独使用一个线程处理,在高并发的场景下需要维护大量线程,内存、线程的切换开销会变得巨大。

1.2 非阻塞IO

image.png

这种模型下,用户进程发起一个IO操作后可返回做其他事情,但是用户进程需要不断进行系统调用,轮询检查数据报是否准备好,在内核缓冲区有数据的情况下用户进程同步的等待数据复制到进程缓冲区,系统调用返回成功。

这种模型下需要不断的重复发起IO系统调用,不断的轮询内核,进程上下文切换导致系统利用率较低,所以Java并没有涉及到此类IO模型。

1.3 IO多路复用

image.png
IO多路复用就是一个进程可以监视多个文件描述符,一旦有描述符就绪后,操作系统通知应用程序进行处理,依赖于操作系统的select/poll/epoll系统调用,https://zhuanlan.zhihu.com/p/39970630

这个过程应用程序就可以同时监听多个IO请求,这比起基于多线程阻塞式IO要先进得多,因为服务器只需要少数线程就可以进行大量的客户端通信。目前主流的高性能处理方案就是采用IO多路复用

1.4 信号驱动IO

image.png

在信号驱动式 I/O 模型中,相比于前三种,实现了在等待数据时,进程不被阻塞,主线程可以继续工作,所以性能更好。

但是对于TCP链接,此类方式几乎没有被使用,因为SIGIO信号是linus信号,没有附加信息,而TPC socket产生的信号事件头7中,所以应用程序收到SIGIO后,根本无法区分处理

1.5 异步IO

image.png

上面几种通信模型,无论是阻塞,非阻塞,IO复用,本质上都是同步的,因为需要用户进程等待数据从内核空间复制到用户空间,而异步IOP则是实现了真正的非阻塞IO。

当用户进程发起IO操作,内核会在整个操作完成后通知进程,包括等待/复制两阶段。但是由于编码复杂,操作系统支持情况等原因,实际生产环境很少用到异步IO模型

2 零拷贝

2.1 IO中断和DMA

2.1.1 IO中断

在没有 DMA 技术前,I/O 的过程需要CPU响应,整个数据的传输过程,都要需要 CPU 亲自参与搬运数据的过程,而且这个过程,CPU 是不能做其他事情的

image.png

2.1.2 DMA技术

在进行 I/O 设备和内存的数据传输的时候,数据搬运的工作全部交给 DMA 控制器,而 CPU 不再参与任何与数据搬运相关的事情,这样 CPU 就可以去处理别的事务。


image.png

由于 I/O 设备越来越多,数据传输的需求也不尽相同,所以每个 I/O 设备里面都有自己的 DMA 控制器。

2.2 传统文件传输

image.png

我们只是搬运一份数据,结果却搬运了 4 次,过多的数据拷贝无疑会消耗 CPU 资源,大大降低了系统性能。所以,要想提高文件传输的性能,就需要减少「用户态与内核态的上下文切换」和「内存拷贝」的次数。

2.3 mmap + write

mmap() 系统调用函数会直接把内核缓冲区里的数据「映射」到用户空间,这样,操作系统内核与用户空间就不需要再进行任何的数据拷贝操作。


image.png

具体过程如下:

我们可以得知,通过使用 mmap() 来代替 read(), 可以减少一次数据拷贝的过程。

但这还不是最理想的零拷贝,因为仍然需要通过 CPU 把内核缓冲区的数据拷贝到 socket 缓冲区里,而且仍然需要 4 次上下文切换,因为系统调用还是 2 次。

2.4 sendfile

它可以替代前面的 read() 和 write() 这两个系统调用,这样就可以减少一次系统调用,也就减少了 2 次上下文切换的开销。其次,该系统调用,可以直接把内核缓冲区里的数据拷贝到 socket 缓冲区里,不再拷贝到用户态,这样就只有 2 次上下文切换,和 3 次数据拷贝。如下图:

image.png

但是这还不是真正的零拷贝技术,如果网卡支持 SG-DMA(The Scatter-Gather Direct Memory Access)技术(和普通的 DMA 有所不同),我们可以进一步减少通过 CPU 把内核缓冲区里的数据拷贝到 socket 缓冲区的过程。
于是,从 Linux 内核 2.4 版本开始起,对于支持网卡支持 SG-DMA 技术的情况下, sendfile() 系统调用的过程发生了点变化,具体过程如下:

2.5 狭义零拷贝和广义零拷贝

Linux 2.4内核新增 sendfile 系统调用,提供了零拷贝。磁盘数据通过 DMA 拷贝到内核态 Buffer 后,直接通过 DMA 拷贝到 NIC Buffer(socket buffer),无需 CPU 拷贝。这也是零拷贝这一说法的来源。这是真正操作系统 意义上的零拷贝(也就是狭义零拷贝)。随着发展,零拷贝的概念得到了延伸,就是目前的减少不必要的数据拷贝都算作零拷贝的范畴(广义零拷贝);

2.6 NIO零拷贝

在Java NIO中,使用了Direct Buffer实现内存广义的零拷贝,
在网络编程中,通常由 read、write 来完成一次 I/O 读写操作。每一次 I/O 读写操作都需要完成四次内存拷贝,路径是 I/O 设备 -> 内核空间 -> 用户空间 -> 内核空间 -> 其它 I/O 设备。

而在Java当中,而在 Java 中,在用户空间中又存在一个拷贝,那就是从 Java 堆内存中拷贝到临时的直接内存中,通过临时的直接内存拷贝到内存空间中去。此时的直接内存和堆内存都是属于用户空间。

为什么在执行网络IO或者文件IO时,一定要通过堆外内存呢?

3 Reactor线程模型

Reactor 模式处理传入多路请求,并将它们同步分派给请求对应的处理线程,Reactor 模式中有 2 个关键组成:

1)单 Reactor 单线程;
2)单 Reactor 多线程;
3)主从 Reactor 多线程。

3.1 单 Reactor 单线程


image.png

1)Reactor 对象通过 Select 监控客户端请求事件,收到事件后通过 Dispatch 进行分发;
2)如果是建立连接请求事件,则由 Acceptor 通过 Accept 处理连接请求,然后创建一个 Handler 对象处理连接完成后的后续业务处理;
3)如果不是建立连接事件,则 Reactor 会分发调用连接对应的 Handler 来响应;
4)Handler 会完成 Read→业务处理→Send 的完整业务流程。

优点:模型简单,没有多线程、进程通信、竞争的问题,全部都在一个线程中完成。
缺点:性能问题,只有一个线程,无法完全发挥多核 CPU 的性能。Handler 在处理某个连接上的业务时,整个进程无法处理其他连接事件,很容易导致性能瓶颈。

3.2 单 Reactor 多线程


image.png

1)Reactor 对象通过 Select 监控客户端请求事件,收到事件后通过 Dispatch 进行分发;
2)如果是建立连接请求事件,则由 Acceptor 通过 Accept 处理连接请求,然后创建一个 Handler 对象处理连接完成后续的各种事件;
3)如果不是建立连接事件,则 Reactor 会分发调用连接对应的 Handler 来响应;
4)Handler 只负责响应事件,不做具体业务处理,通过 Read 读取数据后,会分发给后面的 Worker 线程池进行业务处理;
5)Worker 线程池会分配独立的线程完成真正的业务处理,如何将响应结果发给 Handler 进行处理;
6)Handler 收到响应结果后通过 Send 将响应结果返回给 Client。

优点:可以充分利用多核 CPU 的处理能力。
缺点:多线程数据共享和访问比较复杂;Reactor 承担所有事件的监听和响应,在单线程中运行,高并发场景下容易成为性能瓶颈。

3.3 主从 Reactor 多线程


image.png

1)Reactor 主线程 MainReactor 对象通过 Select 监控建立连接事件,收到事件后通过 Acceptor 接收,处理建立连接事件;
2)Acceptor 处理建立连接事件后,MainReactor 将连接分配 Reactor 子线程给 SubReactor 进行处理;
3)SubReactor 将连接加入连接队列进行监听,并创建一个 Handler 用于处理各种连接事件;
4)当有新的事件发生时,SubReactor 会调用连接对应的 Handler 进行响应;
5)Handler 通过 Read 读取数据后,会分发给后面的 Worker 线程池进行业务处理;
6)Worker 线程池会分配独立的线程完成真正的业务处理,如何将响应结果发给 Handler 进行处理;
7)Handler 收到响应结果后通过 Send 将响应结果返回给 Client。

优点:父线程与子线程的数据交互简单职责明确,父线程只需要接收新连接,子线程完成后续的业务处理。

参考:http://www.52im.net/thread-1939-1-1.html
参考:https://www.cnblogs.com/xiaolincoding/p/13719610.html

上一篇下一篇

猜你喜欢

热点阅读