Netty(一)网络IO基础

在讨论 IO 的时候,参与者通常有两个角色:系统内核和用户进程。用户进程发送 IO 请求过后,系统内核在准备好 IO 数据后,会通过内存拷贝的方式,将准备好的缓存 IO 数据共享给用户进程缓存。

网络 I/O 模型简介

根据 UNIX 网络编程对 I/O 模型的分类,提供了阻塞 I/O 模型、非阻塞 I/O 模型、I/O 复用模型、信号驱动 I/O 模型、异步 I/O 这 5 种 I/O 模型。

1、阻塞 I/O 模型

最常用的模型,所有文件操作都是阻塞的。套接字 socket 在进程空间中调用 recvfrom,其系统调用直到数据包到达,且被复制到应用进程的缓冲区或者发生错误的时候才返回,在此期间一直会等待,进程从调用 recvfrom 开始到它返回的整段时间内都是被阻塞的,因此被称为阻塞 I/O 模型。常用 IO 模型交互图如图所示。
image.png
首先应用程序调用 recvfrom()转入内核,注意内核有 2 个过程,等待数据就绪和拷贝内核数据到用户空间,直到最后复制完成后,recvfrom()才返回,此过程一直是阻塞的。

2、非阻塞 I/O 模型

recvfrom 从应用层到内核的时候,如果该缓冲区没有数据的话,就直接返回一个 EWOULDBLOCK 错误,一般都对非阻塞 I/O 模型进行轮询检查这个状态,看内核是不是有数据到来,如图所示。
image.png

3、I/O 复用模型

Linux 提供 select/poll,进程通过将一个或者多个 fd 传递给 select 或者 poll 系统调用,阻塞在 select 操作上,这样 select/poll 可以帮我们侦测多个文件描述符 fd 是否处于就绪状态。select/poll 是顺序扫描 fd 是否就绪,而且支持的 fd 数量有限,因此它的使用受到一些限制。Linux 还提供了一个 epoll 系统调用,epoll 基于事件驱动方式代替顺序扫描,性能更高,当有 fd 就绪时,立即回调函数 rollback,如图所示。
image.png
select 先阻塞,有活动套接字才返回。与 blocking I/O 相比,select 会有两次系统调用,但是 select 能处理多个套接字。

4、信号驱动 I/O 模型

首先开启套接字信号驱动 I/O 功能,并通过系统调用 sigaction 执行一个信号处理函数(此系统调用立即返回,进程继续工作,它是非阻塞的)。当数据准备就绪时,就为该进程生成一个 SIGIO 信号,通过信号回调通知应用程序调用 recvfrom 来读取数据,并通知主循环函数处理数据,如图所示。
image.png
只有 Unix 系统支持,与 I/O multiplexing (select and poll)相比,它的优势是,免去了 select 的阻塞与轮询,当有活跃套接字时,由注册的 handler 处理。

5、异步 I/O

告知内核启动某个操作,并让内核在整个操作完成之后(包括将数据从内核复制到用户自己的缓冲区)通知我们。这种模型与信号驱动模型的主要区别就是:信号驱动 I/O 由内核通知我们何时可以开始一个 I/O 操作;异步 I/O 模型由内核通知我们 I/O 操作何时已经完成。
image.png
很少有 Linux/Unix 系统支持,Windows 的 IOCP 则是此模型,完全异步的 I/O 复用机制,纵观上面其它四种模型,至少都会在由 kernel copy data to appliction 时阻塞。而该模型是当 copy 完成后才通知 application,可见是纯异步的,好像只有 Windows 的完成端口是这个模型,效率也很出色。

I/O 多路复用技术

I/O 多路复用的最大优势是系统开销小,系统不需要创建新的额外进程或者线程,也不需要维护这些进程和线程的运行,降低了系统的维护工作量,节省了系统资源,主要应用场景如下:
1、服务器需要同时处理多个处于监听状态或者多个连接状态的套接字。
2、服务器需要同时处理多种网络协议的套接字。
目前支持 I/O 多路复用的系统调用有 select、pselect、poll,、epoll(linux)、kqueue(FreeBSD)、iocp(Windows),由于 select/ poll 的一些固有缺陷导致了它的应用受到了很大的限制,Linux 中最终选择了 epoll,来克服 select 的缺点。

select 模型缺点

1、最大并发数限制问题,select 中一个进程所打开的 FD(文件描述符)是有限制的,由 FD_SETSIZE 设置,默认值是 1024,对于需要支持上万个 TCP 连接的大型服务器来说显然太少了。
2、I/O 效率问题,select 每次调用都会线性扫描全部的 FD 集合,效率就会呈现线性下降。
3、内存拷贝问题,select 采取了内存拷贝方法,将内核中的 FD 消息通知给用户空间。
poll 基本上效率和 select 是相同的,select 缺点的 2 和 3 它都没有改掉。

epoll 的改进和提升

1、没有最大并发连接的限制,上限是最大可以打开文件的数目,这个数字一般远大于 2048, 一般来说这个数目和系统内存关系很大,1G 内存的机器上大约有 10 万个句柄左右,具体数目可以 cat /proc/sys/fs/file-max 察看。
2、I/O 效率提升,不会随着 FD 数目的增加而线性下降,它只管你“活跃”的连接,而跟连接总数无关,因此在实际的网络环境中,epoll 的效率就会远远高于 select 和 poll。
3、使用 mmap 共享内存加速内核与用户空间的消息传递,内核和用户空间 mmap 共用一块内存来实现,省去了内存拷贝。

Java 的 I/O 的演进

从 JDK1.0 到 JDK1.3,Java 的 I/O 类库都非常原始,基于 Java 的所有 Socket 通信都采用了同步阻塞模式(BIO),在性能和可靠性方面存在着巨大的瓶颈。
2002 年 JDK1.4 发布,NIO 以 JSR-51 的身份随 JDK 发布,增加了 java.nio 包,提供了很多非阻塞 I/O 开发的类库,在 JDK1.4 和 1.5 update 10 版本之前,JDK 的 selector 基于 select/poll 模型,基于 I/O 复用的非阻塞 I/O,不是异步 I/O。在 JDK1.5 update 10 和 Linux core2.6 以上版本,优化了 selector 的实现,在底层使用 epoll 替换了 select/poll,上层 API 没有变化,可认为是 JDK NIO 的一次性能优化。
2011 年 JDK1.7 发布,将原来的 NIO 进行了升级,通过 JSR-203 演进而来,称为 NIO2.0,提供异步 I/O 开发类库。

1、Java 同步阻塞 I/O

在 java 中调用 InputStream.read()或者 OutputStream.write()时,用户进程会阻塞住直到数据就绪,相当于一个线程一个连接的方式。所以在采用 Java IO 时,在 Server 端通常会采用对于每个新连接,起一个新的线程去处理,这样后来的连接就不用等到之前的完成才能操作。但也带来了问题,毕竟线程是系统的稀缺资源,数量上会有瓶颈,达到一定数量后,性能急剧下降,内存崩溃。不能应对大量连接的情况,而且线程切换很耗费系统资源。

2、Java NIO 同步非阻塞 IO

基于 Java IO 的缺点,NIO 采用了新的设计方式,核心在 ServerSocketChannel, SocketChannel, FileChannel, ByteBuffer, Pipe, Selector。非阻塞主要依靠 Selector,Channel 在 Selector 上注册自己感兴趣的事件,然后 Selector 线程会轮询注册在自己身上的 Channel,当有数据准备就绪时,就通知相应的 Channel。这样一个 Selector 可以管理多个 Channel,但实际上还是阻塞的,现在不阻塞 IO 层面了,阻塞在 Selector 线程上了。而且采用轮询的方式,效率比较低。

3、Java AIO 异步非阻塞 IO

在 Java NIO 的基础上,增加了 AsynchronousServerSocketChannel, AsynchronousSocketChannel, AsynchronousChannelGroup, CompletionHandler,其中 AsynchronousChannelGroup 起到了事件收集和任务分发的作用,而 CompletionHandler 是绑定在事件上回调机制,从而达到异步。能否真正实现异步,关键还要看系统底层的实现,当前来看只有 window 的 iocp 实现了真正的异步,linux 上还是通过 epoll 来模拟,是一种伪异步。
是否异步主要在系统内核数据拷贝到用户进程这个步骤来区分,同步的话是通知用户进程数据准备好了,可以拷贝了,然后用户进程阻塞去拷贝数据;异步的话是操作系统帮你把数据拷贝后,然后通知你数据好了,可以直接用了。

https://alicharles.oss-cn-hangzhou.aliyuncs.com/static/images/mp_qrcode.jpg
文章目录
  1. 网络 I/O 模型简介
    1. 1、阻塞 I/O 模型
    2. 2、非阻塞 I/O 模型
    3. 3、I/O 复用模型
    4. 4、信号驱动 I/O 模型
    5. 5、异步 I/O
  2. I/O 多路复用技术
    1. select 模型缺点
    2. epoll 的改进和提升
  3. Java 的 I/O 的演进
    1. 1、Java 同步阻塞 I/O
    2. 2、Java NIO 同步非阻塞 IO
    3. 3、Java AIO 异步非阻塞 IO