JAVA 主流I/O模型比较
| 同步阻塞(BIO) | 伪异步IO | 非阻塞IO(NIO) | 异步IO(AIO) |
客户端个数:I/O线程 | 1:1 | M:N(M可以大于N) | M:1(1个I/O线程处理多个客户端连接) | M:0(不需要启动额外的I/O线程,被动回调) |
I/O类型(阻塞) | 阻塞I/O | 阻塞I/O | 非阻塞I/O | 非阻塞I/O |
I/O类型(同步) | 同步I/O | 同步I/O | 同步I/O(多路复用) | 异步I/O |
API使用难度 | 简单 | 简单 | 非常复杂 | 复杂 |
调试难度 | 简单 | 简单 | 复杂 | 复杂 |
可靠性 | 非常差 | 差 | 高 | 高 |
吞吐量 | 低 | 中 | 高 | 高 |
JAVA IO主要模型
BIO模型
通常由一个独立的Acceptor线程负责监听客户端的连接,它接收到客户端连接请求之后为每个客户端创建一个新的线程进行链路处理,处理完成之后,通过输出流返回应答给客户端,线程销毁。 该模型缺乏弹性伸缩能力,当客户端并非访问量增加,服务端的线程个数和客户端并发访问数呈1:1的正比关系,当线程数膨胀之后,系统性能将急剧下降,最终导致进程宕机或僵死。
异步伪IO模型
当有新的客户端接入的时候,将客户端的Socket封装成一个Task投递到后端的线程池中进行处理,JDK的线程池维护一个消息队列和N个活跃线程对消息队列中的任务进行处理。
“伪异步IO模型”典型的问题,当可用线程都被故障服务器阻塞时,后续所有的IO消息都将在队列中排队,线程池采用阻塞队列实现,当队列积满后,后续入队的操作将被阻塞,前端只有一个Accept线程接收客户端接入,它被阻塞在线程池的同步阻塞队列之后,新的客户端请求将被拒绝,客户端会发生大量连接超时。
非阻塞式IO模型(NIO)
多路复用器Selector是NIO模型的基础,一个多路复用器Selector可以同时轮询多个注册在它上面的Channel,服务端只需要一个线程负责Selector的轮询,就可以接入成千上万的客户端连接。
模型优点:
- NIO中Channel是全双工(是说可以通过Channel 即可完成读操作,也可以完成写操作)的,Channel比流(InputStream/OutputStream)可以更好地映射底层操作系统的API(UNIX网络编程模型中,底层操作系统的通道都是全双工的,同时支持读写操作);
- 客户端发起的连接操作是异步的,不需要像之前的客户端那样被同步阻塞;(此时,客户端不依赖于服务器端,也就是说客户端发完请求可以做其他其他事情)
- 一个Selector线程可以同时处理成千上万个client的请求,而且性能不会随着客户端链接的增加而线性下降;原因:JDK的Selector在Linux等主流操作系统上通过epoll实现,它没有连接句柄数的限制,适合做高性能高负载的网络服务器方案
Reactor多线程模型
reactor将接受发送分离,client发送的请求丢到线程池中,所以后续请求不会被阻塞。
但是当用户进一步增加的时候,Reactor会出现瓶颈!因为Reactor既要处理IO操作请求,又要响应连接请求!为了分担Reactor的负担,所以引入了主从Reactor模型!
Reactor主从模型
采用多个Reactor,每个Reactor在自己单独线程中执行,可以并行响应多个客户端的请求事件;mainReactor 不再是一个单独的NIO线程,而是一个独立的NIO线程池, 处理链接请求,认证,登陆等操作
- Acceptor处理完成后,将事件注册到subReactor线程池中的某个IO线程上去,此IO线程继续完成后面的IO操作
- Netty采用类似这种模式,boss线程池就是多个mainReactor,worker线程池就是多个subReactor。
AIO模型
NIO2.0引入了新的异步通道的概念,并提供了异步文件通道和异步套接字通道的实现。异步通道提供2种方式获取操作结果。
NIO2.0的异步套接字通道是真正的异步非阻塞I/O,它对应UNIX网络编程中的事件驱动I/O(AIO),它不需要通过多路复用器(Selector)对注册的通道进行轮询操作即可实现异步读写,从而简化了NIO的编程模型。
参考:
Unix五种IO模型
Java NIO 之 IO模型总结整理
java使用netty的模型总结
蚂蚁通信框架实践
Scalable IO in Java
pdf文档