IO多路复用详解

同步,异步,阻塞,非阻塞

1.同步阻塞:客户揣发送请求给服务揣,此时服务端处理任务时间很久,则客户端则被服务端堵塞了,所以客户端会一直等待服务端的响应,此时客户端不能做其他任何事,服务端也不接受其他客户揣的请求。这种通信机制比较简单租暴,但是效率不高。

2.同步非阻塞:客户端发送请求给服务端,此时服务端处理任务时间很久,这个时候虽然客户端会一直等待响应,但是服务端可以处理其他的请求,过一会回来处理原先的。这种方式很高效,一个服务端可以处理很多请求,不会在因为任务没有处理完而堵着,所以这是非阻塞的。

3.异步阻塞:客户揣发送请求给服务端,此时服务端处理任务时间很久,但是客户端不会等待服务器响应,它可以做其他的任务,等服务器处理完毕后再把结果响应给客户端,客户端得到回调后再处理服务端的响应。这种方式可以避免客户端一直处于等待的状态,优化了用户体验,其实就是类似于网页里发起的ajax异步请求。

4.异步非阻塞:客户端发送请求给服务端,此时服务端处理任务时间很久,这个时候的任务虽然处理时间会很久,但是客户端可以做其他的任务,因为他是异步的,可以在回调函数里处理响应;同时服务端是非阻塞的,所以服务端可以去处理其他的任务,如此,这个模式就显得非常的高效了 。

典型的多路复用IO实现

目前主流的多路复用IO包括四种select,poll,epoll,kqueue

IO模型相对性能关键思路操作系统java支持
select较高Reactorwindows/Linux支持,Reactor模式(反应器设计模式)。Linux操作系统的 kernels 2.4内核版本之前,默认使用select;而目前windows下对同步IO的支持,都是select模型
poll较高ReactorReactorLinux下的JAVA NIO框架,Linux kernels 2.6内核版本之前使用poll进行支持。也是使用的Reactor模式
epollReactor/proactorlinuxlinux kernels2.6内核版本即以后使用epoll进行支持,另外由于linux下没有windows下IOCP技术提供真正的异步IO支持,所以linux下使用epoll模拟异步IO
kqueueReactorlinux目前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则是使用一个线程池来支持的.其读写能力将会随着线程数的增多而大大增加.对于业务操作,这里也是使用一个线程池,而每个业务请求只需要进行业务计算.通过这种方式,服务器的性能会大大提升,在可见的情况下基本支持上百万连接

Last modification:December 28, 2022
如果觉得我的文章对你有用,请随意赞赏