一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第 11 天,点击查看活动详情。
姑苏城外寒山寺,夜半钟声到客船。
1 前言
IO 的重要性在开发中的重要性不言而喻,从传统行业、游戏、互联网、再到未来的万物互联的时代,对 IO 的认知和理解可以解决很多现实的复杂问题,从 BIO
到 NIO/AIO
,所有的知识都涉及网络通信和基础知识,目前 netty 对 NIO 的封装是最好用的工具,尤其是面对高并发的场景,使用起来得心应手。
2 NIO
NIO 在操作系统的层面指的是 non blocking ,在 java 层面指的就是 new io ,其核心都是非阻塞。在操作系统中或者说在传统的 bio,其操作都是阻塞的,在操作系统的内核做出改变后,能得到 socket 函数支持了非阻塞,之前在获取数据是阻塞等待结果,在内核做出改变后可以立即返回结果,未准备好之前返回 -1。
在 java 代码中,关于 BIO 的操作如下:
# 创建一个 serversocket
ServerSocket server = new ServerSocket(8986);
# 循环处理连接
while true
# 阻塞方法,等待客户端的接入
Socket client = server.accept();
# 得到输入流
InputStream input = client.getInputStream();
关于 NIO 的操作,比 BIO 的代码要复杂一些:
# 需要打开通道 channel
ServerSocketChannel server = ServerSocketChannel.open();
# 进行端口绑定
server.bind()
# 创建 Selector
Selector selector = Selector.open();
# 注册 accept 事件
server.register(selector, 'accept');
# 进行循环
while true
# 如果数据未准备好,则返回 -1 ,否则就近继续进行处理
int wt = selector.select();
if wt == 0 continue;
# 处理事件
Set<SelectionKey> keys = selector.selectedKeys();
从核心功能来说,accept 是阻塞的方法,select 是非阻塞的,在底层的 socket 方法中会也会有些区别。
# bio 的阻塞方法
server.accept()
# nio 的非阻塞方法,提出 channel selector buffer 的概念来解决io,利用事件注册状态来处理请求信息
selecter.select()
在 linux 操作系统中,使用 man socket 来查看操作系统中 socket 传入的参数,如下所示:
# 操作系统的函数都是 C 语言编写的,java 也是类C 的语言
socket()
# 创建一个用于通信的文件描述符
creates an endpoint for communication and returns a descriptor.
...
# 设置非阻塞参数项
SOCK_NONBLOCK
Set the O_NONBLOCK file status flag on the new open file description. Using this flag saves extra calls to fcntl(2) to achieve the same result.
3 阻塞和非阻塞的区别
NIO 的单线程能处理连接的数量比 BIO 要高出很多,而为什么单线程能处理更多的连接呢? 是因为有 Selector 的存在。当连接连接好之后有两个操作要做:
- 1 等待内核完成数据的处理。
- 2 数据从内核空间拷贝到用户空间。
BIO 在等待客户端数据的过程是阻塞的,这样就造成了一个线程只能处理一个请求,而机器能支持的最大线程数是有限的,这就是 BIO 不支持高并发的原因。而在 NIO 中,当一个 socket 建立好后,线程不会阻塞去接收这个 socket,而是将 request 注册到 selector,而 selector 会不断地遍历这些 request, 一旦数据准备完毕就会通知 Thread 进行处理,这个过程是非阻塞的,所以 NIO 比 BIO 单线程的处理能力强。阻塞 IO,非阻塞 IO,多路复用,信号驱动和异步 IO,5 个 I/O 模型的比较:
这里需要说明的是,阻塞与非阻塞说的是数据准备阶段,而同步和异步过程说的是数据从内核空间复制到用户空间的过程,同步的话需要用户线程自己主动触发将数据取回,内核只是在数据准备阶段处理完成后进行通知,而异步的概念是内核将完成数据复制的过程,然后通知用户线程数据复制过程已经完成。
4 总结
在本文中讲述了 NIO 和阻塞概念的理解,在后续的文章中将介绍多路复用和 Reactor 的理解。