聊聊阻塞IO 非阻塞IO 异步IO,你学会了吗?

【聊聊阻塞IO 非阻塞IO 异步IO,你学会了吗?】Reactor 是 NIO 的基础 。为什么 NIO 的性能就能够比传统的阻塞 I/O 性能高呢?我们首先来看一下传统阻塞式 I/O 的一些特点 。
非阻塞 I/O 模型其实,在处理 I/O 动作时,有大部分时间是在等待 。比如,socket 连接要花费很长时间进行连接操作 , 在完成连接的这段时间内 , 它并没有占用额外的系统资源 , 但它只能阻塞等待在线程中 。这种情况下,系统资源并不能被合理利用 。
JAVA 的 NIO,在 linux 上底层是使用 epoll 实现的 。epoll 是一个高性能的多路复用 I/O 工具 , 改进了 select 和 poll 等工具的一些功能 。在网络编程中,对 epoll 概念的一些理解,几乎是面试中必问的问题 。
epoll 的数据结构是直接在内核上进行支持的 , 通过 epoll_create 和 epoll_ctl 等函数的操作,可以构造描述符(fd)相关的事件组合(event) 。
这里有两个比较重要的概念:
fd 每条连接、每个文件,都对应着一个描述符 , 比如端口号 。内核在定位到这些连接的时候,就是通过 fd 进行寻址的 。
event 当 fd 对应的资源,有状态或者数据变动 , 就会更新 epoll_item 结构 。在没有事件变更的时候,epoll 就阻塞等待,也不会占用系统资源;一旦有新的事件到来,epoll 就会被激活,将事件通知到应用方 。
关于 epoll 还会有一个面试题 , 相对于 select,epoll 有哪些改进?
你可以这样回答:
epoll 不再需要像 select 一样对 fd 集合进行轮询,也不需要在调用时将 fd 集合在用户态和内核态进行交换;
应用程序获得就绪 fd 的事件复杂度,epoll 是 O(1) , select 是 O(n);
select 最大支持约 1024 个 fd,epoll 支持 65535个;
select 使用轮询模式检测就绪事件,epoll 采用通知方式,更加高效 。
Reactor 模式

聊聊阻塞IO 非阻塞IO 异步IO,你学会了吗?

文章插图
模型 里面有四个主要元素:
Acceptor处理 client 的连接 , 并绑定具体的事件处理器;
Event具体发生的事件,比如图中sub的read、send等;
Handler执行具体事件的处理者,比如处理读写事件的具体逻辑;
Reactor将具体的事件分配(dispatch)给 Handler 。
mAInReactor负责监听处理新的连接,然后将后续的事件处理交给 subReactor;
subReactor对事件处理的方式,也由阻塞模式变成了多线程处理 , 引入了任务队列的模式 。
面试官可能会问你:为什么我在使用 NIO 时,使用 Channel 进行读写 , socket 的操作依然是阻塞的?NIO 的作用主要体现在哪里?
这时你可以回答:NIO 只负责对发生在 fd 描述符上的事件进行通知 。事件的获取和通知部分是非阻塞的,但收到通知之后的操作,却是阻塞的,即使使用多线程去处理这些事件,它依然是阻塞的 。
AIO 更近一步,将这些对事件的操作也变成非阻塞的 。下面是一段典型的 AIO 代码,它通过注册 CompletionHandler 回调函数进行事件处理 。这里的事件是隐藏的 , 比如 read 函数 , 它不仅仅代表 Channel 可读了,而且会把数据自动的读取到 ByteBuffer 中 。等完成了读取 , 就会通过回调函数通知你,进行后续的操作 。




    推荐阅读