目录
IO模型
在Java中,常见的IO模型有4种,
- 同步阻塞IO(Blocking IO)
- 同步非阻塞IO(Non-blocking IO):默认创建的socket都是阻塞的,非阻塞IO要求socket被设置为NONBLOCK。注意这里所说的NIO并非Java的NIO(New IO)库。
- IO多路复用(IO Multiplexing):也称为异步阻塞IO,Java中的Selector和Linux中的epoll都是这种模型。这里复用的是指复用一个或几个线程,用一个或一组线程处理多个IO操作,减少系统开销小,不必创建和维护过多的进程/线程;
- 异步IO(Asynchronous IO):即经典的Proactor设计模式,也称为异步非阻塞IO。
IO实现
目前流程的多路复用IO实现主要包括四种: select
、poll
、epoll
、kqueue
。下表是他们的一些重要特性的比较:
IO模型 | 相对性能 | 关键思路 | 操作系统 | JAVA支持情况 |
---|---|---|---|---|
select | 较高 | Reactor | windows/Linux | 支持,Reactor模式(反应器设计模式)。Linux操作系统的 kernels 2.4内核版本之前,默认使用select;而目前windows下对同步IO的支持,都是select模型 |
poll | 较高 | Reactor | Linux | Linux下的JAVA NIO框架,Linux kernels 2.6内核版本之前使用poll进行支持。也是使用的Reactor模式 |
epoll | 高 | Reactor/Proactor | Linux | Linux kernels 2.6内核版本及以后使用epoll进行支持;Linux kernels 2.6内核版本之前使用poll进行支持;另外一定注意,由于Linux下没有Windows下的IOCP技术提供真正的 异步IO 支持,所以Linux下使用epoll模拟异步IO |
kqueue | 高 | Proactor | Linux | 目前JAVA的版本不支持 |
多路复用IO技术最适用的是“高并发”场景,所谓高并发是指1毫秒内至少同时有上千个连接请求准备好。其他情况下多路复用IO技术发挥不出来它的优势。另一方面,使用JAVA NIO进行功能实现,相对于传统的Socket套接字实现要复杂一些,所以实际应用中,需要根据自己的业务需求进行技术选择。
同步阻塞IO
核心流程
当应用程序发起 read 系统调用时,在内核数据没有准备好之前,应用程序会一直处于阻塞等待状态,直到内核把数据准备好了返回给应用程序
交互流程
1)服务端进行初始化:新建 socket、绑定地址、转为服务端 socket
2)服务端调用 accept,进入阻塞状态,等待客户端连接
3)客户端新建 socket,向服务端发起连接
4)服务端和客户端通过 TCP 三次握手建立连接
5)服务端继续执行 read 函数,进入阻塞状态,等待客户端发送数据
6)客户端向服务端发送数据
7)服务端读取数据,执行逻辑处理
同步阻塞IO模型
1)应用进程发起 read 系统调用
2)应用进程阻塞等待数据就绪
3)数据通过网络传输到达网卡,然后再到内核socket缓冲区,当数据被拷贝到内核 socket 缓冲区时,此时处于就绪状态
4)将数据从内核拷贝到应用程序缓冲区,返回成功
多线程版本:文中使用的例子是单线程,如果是多线程则在每个 socket 建立连接后新建线程去负责处理该 socket 后续的流程,这样就不会由于单个 socket 阻塞住而影响到其他 socket。
总结
单线程:某个 socket 阻塞,会影响到其他 socket 处理。
多线程:当客户端较多时,会造成资源浪费,全部 socket 中可能每个时刻只有几个就绪。同时,线程的调度、上下文切换乃至它们占用的内存&