在计算机通信中,I/O在数据交换中起到了至关重要的作用。
最初,只有BIO
这种同步阻塞
式的I/O编程通信模型。
随着计算机普及及流量的上涨,产生了NIO
这种同步非阻塞
式的I/O编程通信模型。
再后来,NIO
出现了瓶颈,便产生了AIO
这种异步非阻塞
式的I/O编程通信模型。
对于I/O类型,分为:文件I/O、网络I/O。
文件I/O:基本本地磁盘
,在内核空间与用户进程空间之间交换数据。
网络I/O:基本网络Socket通信
,在不同(主机)进程之间交换数据。
对于I/O方式,分为:同步、异步。
同步方式:后续的操作需要等待
前面操作完成才可以继续执行。
异步方式:后续的操作不需要等待
前面操作完成,可以直接返回,通过event、callback调用。
对于I/O状态,分为:阻塞、非阻塞。
阻塞状态:当前执行的线程将处于阻塞
状态,无法
继续执行其他的任务。
非阻塞状态:当前执行的线程不会
处于阻塞
状态,可以
继续其它任务,I/O操作由后台
去处理。
I/O通信模型围绕着上述方式和状态分为:BIO、NIO、AIO。
首先,是BIO
,同步阻塞I/O,最为原始,设计最简单,适用于并发线程少于1000的情况。
在服务端,绑定IP、监听端口,启动一个Acceptor
线程,阻塞等待accept。
当客户端进发起连接请求时,创建一个线程进行Socket连接
,等待与客户端之间进行读写I/O流
操作。
客户端使用连接完毕之后,断开连接,销毁线程。
当然,由于创建线程成本过高,可以采用线程池
进行优化线程成本。
但是,如果线程内部I/O阻塞,线程池资源也会带来瓶颈,也因此无法同时承受太多并发连接。
接着,是NIO
,同步非阻塞I/O,底层基于Reactor模型
来实现。
与BIO
不同的是,在NIO
中,客户端与服务端的Channel建立连接后,由Selector
线程不断的去轮询,获取当前就绪的Channel。
而不是来一个请求直接启动一个线程。Selector是一个多路复用器,Channel被注册到Selector上。
客户端只与Channel进行交互,所有的读写不是基于流,而是基于Buffer,有I/O操作时才会创建线程。
通过多路复用器的轮询,而不是Acceptor的阻塞accept,实现了非阻塞I/O。
对于多路复用器轮询出可用Channel的操作,根据操作系统实现又包含有:select、poll、epoll、kqueue。
AIO
作为NIO
的升级版,是异步非阻塞I/O,底层基于Proactor模型
来实现,适用高并发场景。
整体上基于事件和回调机制,文件通道、套接字通道都是异步的,读写返回的是Future
。
不需要再去注册相关感兴趣的key,只需要等待事件和I/O操作结果返回即可。
总的来说:
BIO,基于同步阻塞,适用连接较少且连接使用时间均匀的场景,耗费服务器资源较多。
NIO,基于同步非阻塞,适用连接较多且连接使用时间短的场景,充分利用服务器资源。
AIO,基于异步非阻塞,适用连接较多且连接使用时间长的场景,充分利用操作系统来完成并发操作。