IO多路复用详解
同步,异步,阻塞,非阻塞
1.同步阻塞:客户揣发送请求给服务揣,此时服务端处理任务时间很久,则客户端则被服务端堵塞了,所以客户端会一直等待服务端的响应,此时客户端不能做其他任何事,服务端也不接受其他客户揣的请求。这种通信机制比较简单租暴,但是效率不高。
2.同步非阻塞:客户端发送请求给服务端,此时服务端处理任务时间很久,这个时候虽然客户端会一直等待响应,但是服务端可以处理其他的请求,过一会回来处理原先的。这种方式很高效,一个服务端可以处理很多请求,不会在因为任务没有处理完而堵着,所以这是非阻塞的。
3.异步阻塞:客户揣发送请求给服务端,此时服务端处理任务时间很久,但是客户端不会等待服务器响应,它可以做其他的任务,等服务器处理完毕后再把结果响应给客户端,客户端得到回调后再处理服务端的响应。这种方式可以避免客户端一直处于等待的状态,优化了用户体验,其实就是类似于网页里发起的ajax异步请求。
4.异步非阻塞:客户端发送请求给服务端,此时服务端处理任务时间很久,这个时候的任务虽然处理时间会很久,但是客户端可以做其他的任务,因为他是异步的,可以在回调函数里处理响应;同时服务端是非阻塞的,所以服务端可以去处理其他的任务,如此,这个模式就显得非常的高效了 。
典型的多路复用IO实现
目前主流的多路复用IO包括四种select,poll,epoll,kqueue
IO模型 | 相对性能 | 关键思路 | 操作系统 | java支持 |
---|---|---|---|---|
select | 较高 | Reactor | windows/Linux | 支持,Reactor模式(反应器设计模式)。Linux操作系统的 kernels 2.4内核版本之前,默认使用select;而目前windows下对同步IO的支持,都是select模型 |
poll | 较高 | Reactor | Reactor | Linux下的JAVA NIO框架,Linux kernels 2.6内核版本之前使用poll进行支持。也是使用的Reactor模式 |
epoll | 高 | Reactor/proactor | linux | linux kernels2.6内核版本即以后使用epoll进行支持,另外由于linux下没有windows下IOCP技术提供真正的异步IO支持,所以linux下使用epoll模拟异步IO |
kqueue | 高 | Reactor | linux | 目前java版本不支持 |
多路复用IO技术最适用的是“高并发”场景,所谓高并发是指1毫秒内至少同时有上千个连接请求准备好。其他情况下多路复用IO技术发挥不出来它的优势。另一方面,使用JAVA NIO进行功能实现,相对于传统的Socket套接字实现要复杂一些,所以实际应用中,需要根据自己的业务需求进行技术选择。
reactor模型和proactor模型
传统IO模型
对于传统IO模型,其主要是一个Server对接N个客户端,在客户端连接之后,为每个客户端都分配一个执行线程。如下图是该模型的一个演示:
从图中可以看出,传统IO的特点在于:
- 每个客户端连接到达之后,服务端会分配一个线程给该客户端,该线程会处理包括读取数据,解码,业务计算,编码,以及发送数据整个过程;
- 同一时刻,服务端的吞吐量与服务器所提供的线程数量是呈线性关系的。
这种设计模式在客户端连接不多,并发量不大的情况下是可以运行得很好的,但是在海量并发的情况下,这种模式就显得力不从心了。这种模式主要存在的问题有如下几点:
- 服务器的并发量对服务端能够创建的线程数有很大的依赖关系,但是服务器线程却是不能无限增长的;
- 服务端每个线程不仅要进行IO读写操作,而且还需要进行业务计算;
- 服务端在获取客户端连接,读取数据,以及写入数据的过程都是阻塞类型的,在网络状况不好的情况下,这将极大的降低服务器每个线程的利用率,从而降低服务器吞吐量。
Reactor事件驱动模型
在传统IO模型中,由于线程连接以及进行IO操作时都会阻塞当前线程,这部分损耗是很大的.因而在JDK1.4中就提供了一套非阻塞的IO.该API本质上是以事件驱动来处理网络事件的,而Reactor是基于该api提出的一套IO模型.如下是Reactor事件驱动模型示意图
从图中可以看出,在reactor模型中,主要有四个角色:客户端连接reactor,acceptor和handler.这里的acceptor会不断的接受从客户端发过来的连接,然后将接收到的链接由reactor进行分发,最后由具体的Handler进行处理,改进后的reactor模型主要有如下优点
- 从模型上来讲,如果仅仅还是一个线程池来处理客户端连接到网络读写,以及业务计算,那么reactor模型与传统IO模型在效率上没什么提升.但是areactor是以事件驱动的,其能够将接受客户端连接+网络读和网络写,以及业务计算进行拆分,从而极大提升处理效率
- reactor模型是异步非阻塞模型,工作线程在没有网络事件时,可以处理其他的任务,而不用像传统IO那样必须阻塞等待
Reactor模型-业务处理与IO分离
在上面的Reactor模型中,由于网络读写和业务操作都在同一个线程中,在高并发情况下,这里的系统瓶颈主要在俩方面
- 高频率的网络读写事件处理
- 大量的业务操作处理
从图中可以看出,在线程进行业务操作的模型下,该模式主要有如下特点
- 使用一个线程进行客户端连接的接受以及网络读写事件的处理
- 在接收到客户端连接之后,将连接交由线程池进行数据的编码以及业务计算
这种模式相较于前面的模式性能有了很大的提升,主要在于进行网络读写的同时,也进行了业务计算,从而大大提升了系统的吞吐量,但这种模式也有其不足,主要在于
网络读写是一个比较消耗CPU的操作,在高并发下,将会有大量的客户端数据需要进行网络读写,此时线程将不足以处理这么多请求
Reactor模型-并发读写
对于使用线程池处理业务操作的模型,由于网络读写在高并发情况下会成为系统的一个瓶颈,因而针对该模型提出了一种改进后的模型,使用线程池来进行网络读写,而仅仅只使用一个线程专门接收客户端连接
可以看到,改进后的Reactor模型将Reactor拆分成了mainreactor和subreactor.这里mainreactor主要进行客户端连接处理,处理完成之后将该连接交由subReactor以处理客户端的网络读写.这里subReactor则是使用一个线程池来支持的.其读写能力将会随着线程数的增多而大大增加.对于业务操作,这里也是使用一个线程池,而每个业务请求只需要进行业务计算.通过这种方式,服务器的性能会大大提升,在可见的情况下基本支持上百万连接