NIO,BIO,AIO,Reactor原理

2017-10-07  本文已影响269人  错位的季节

同步和异步区别:有无通知(是否轮询)

堵塞和非堵塞区别:操作结果是否等待(是否马上有返回值),只是设计方式的不同

1,同步和异步是针对应用程序和内核的交互而言的。

2,阻塞和非阻塞是针对于进程在访问数据的时候,根据IO操作的就绪状态来采取的不同方式,说白了是一种读取或者写入操作函数的实现方式,阻塞方式下读取或者写入函数将一直等待,而非阻塞方式下,读取或者写入函数会立即返回一个状态值。

由上描述基本可以总结一句简短的话,同步和异步是目的,阻塞和非阻塞是实现方式。

1:同步指的是用户进程触发IO操作并等待或者轮询的去查看IO操作是否就绪.     自己上街买衣服,自己亲自干这件事,别的事干不了。

2:异步异步是指用户进程触发IO操作以后便开始做自己的事情,而当IO操作已经完成的时候会得到IO完成的通知(异步的特点就是通知)    告诉朋友自己合适衣服的尺寸,大小,颜色,让朋友委托去卖,然后自己可以去干别的事。(使用异步IO时,Java将IO读写委托给OS处理,需要将数据缓冲区地址和大小传给OS)

3:阻塞所谓阻塞方式的意思是指, 当试图对该文件描述符进行读写时, 如果当时没有东西可读,或者暂时不可写, 程序就进入等待 状态, 直到有东西可读或者可写为止.     去公交站充值,发现这个时候,充值员不在(可能上厕所去了),然后我们就在这里等待,一直等到充值员回来为止。(当然现实社会,可不是这样,但是在计算机里确实如此。)

4非阻塞状态下, 如果没有东西可读, 或者不可写, 读写函数马上返回, 而不会等待,银行里取款办业务时,领取一张小票,领取完后我们自己可以玩玩手机,或者与别人聊聊天,当轮我们时,银行的喇叭会通知,这时候我们就可以去了。

同步阻塞IO(JAVA BIO):

同步并阻塞,服务器实现模式为一个连接一个线程,即客户端有连接请求时服务器端就需要启动一个线程进行处理,如果这个连接不做任何事情会造成不必要的线程开销,当然可以通过线程池机制改善。

同步非阻塞IO(Java NIO) :

NIO 有一个主要的类Selector,这个类似一个观察者,只要我们把需要探知的socketchannel告诉Selector,我们接着做别的事情,当有事件发生时,他会通知我们,传回一组SelectionKey,我们读取这些Key,就会获得我们刚刚注册过的socketchannel,然后,我们从这个Channel中读取数据,接着我们可以处理这些数据。

 同步非阻塞,服务器实现模式为一个请求一个线程,即客户端发送的连接请求都会注册到多路复用器上,多路复用器轮询到连接有I/O请求时才启动一个线程进行处理。用户进程也需要时不时的询问IO操作是否就绪,这就要求用户进程不停的去询问。

Java对BIO、NIO、AIO的支持

Java BIO : 同步并阻塞,服务器实现模式为一个连接一个线程,即客户端有连接请求时服务器端就需要启动一个线程进行处理,如果这个连接不做任何事情会造成不必要的线程开销,当然可以通过线程池机制改善。

Java NIO : 同步非阻塞,服务器实现模式为一个请求一个线程,即客户端发送的连接请求都会注册到多路复用器上,多路复用器轮询到连接有I/O请求时才启动一个线程进行处理。

Java AIO(NIO.2) :在此种模式下,用户进程只需要发起一个IO操作然后立即返回,等IO操作真正的完成以后,应用程序会得到IO操作完成的通知,此时用户进程只需要对数据进行处理就好了,不需要进行实际的IO读写操作,因为真正的IO读取或者写入操作已经由内核完成了。

BIO、NIO、AIO适用场景分析:

BIO方式适用于连接数目比较小且固定的架构,这种方式对服务器资源要求比较高,并发局限于应用中,JDK1.4以前的唯一选择,但程序直观简单易理解。

NIO方式适用于连接数目多且连接比较短(轻操作)的架构,比如聊天服务器,并发局限于应用中,编程比较复杂,JDK1.4开始支持。

AIO方式使用于连接数目多且连接比较长(重操作)的架构,比如相册服务器,充分调用OS参与并发操作,编程比较复杂,JDK7开始支持。

Recotor

反应器(Reactor)模式是为了处理一个或多个客户端同时提交的服务请求而设计的。事件驱动的应用程序可以使用反应器结构化模式,多路分解并分配从一个或多个客户端发给应用程序的服务请求。该模式的别名有:分配器(Dispatcher),通知器(Notifier)

以下例子摘自:http://daimojingdeyu.iteye.com/blog/828696

先用比较直观的方式来介绍一下这种方式的优点,通过和常用的多线程方式比较一下,可能更好理解。

以去饭店吃饭为例,每一伙人来就餐就是一个事件,吃饭的人会先看一下菜单,然后点菜。处理这些就餐事件的就需要我们的服务人员了。每个服务员相当于一个线程。

多线程处理的方式会是这样的:

一个人来就餐,一个服务员去服务,然后客人会看菜单,点菜。 服务员将菜单给后厨。

二个人来就餐,二个服务员去服务……

五个人来就餐,五个服务员去服务……

这个就是多线程的处理方式,一个事件到来,就会有一个线程服务。很显然这种方式在人少的情况下会有很好的用户体验,每个客人都感觉自己是VIP,专人服务的。如果饭店一直这样同一时间最多来5个客人,这家饭店是可以很好的服务下去的。

来了一个好消息,因为这家店的服务好,吃饭的人多了起来。同一时间会来10个客人,老板很开心,但是只有5个服务员,这样就不能一对一服务了,有些客人就要没有人管了。老板就又请了5个服务员,现在好了,又能每个人都受VIP待遇了。

越来越多的人对这家饭店满意,客源又多了,同时来吃饭的人到了20人,老板高兴不起来了,再请服务员吧,占地方不说,还要开工钱,再请人就攒不到钱了。怎么办呢?老板想了想,10个服务员对付20个客人也是能对付过来的,服务员勤快点就好了,伺候完一个客人马上伺候另外一个,还是来得及的。综合考虑了一下,老板决定就使用10个服务人员的线程池啦~~~

但是这样有一个比较严重的缺点就是,如果正在接受服务员服务的客人点菜很慢,其他的客人可能就要等好长时间了。有些火爆脾气的客人可能就等不了走人了。

Reactor就可以很好的解决这个问题。

因为点菜才通常是很耗时的,所以当有人来吃饭的时候,可以先把菜单交给点菜的人自己浏览,等点菜的人想好了要点的菜的时候再招呼服务员,等服务员过来了之后就可以为顾客点菜并发送到后厨了。这个在某种意义上说就是用单线程在做多线程的事情。

Reactor的事件驱动就体现在了只有当事件发生(客户招呼服务员点菜)的时候,服务员(线程)才去处理。而客户刚进入饭店的时候,是不需要去处理的。

从这个简单的例子应该可以基本明白Reactor是干什么的了吧。(由事件触发,并分发请求)

2.2Reactor模式的思想:分而治之+事件驱动

分而治之:

一个connection里发生的完整的网络处理过程一般分为accept、read、decode、compute、encode、send这几步。Reactor将每个步骤映射为一个task,服务端端的线程执行的最小逻辑单元不再是一次完整的网络处理过程,而是更小的task,且采用非阻塞的执行方式;

事件驱动:

每个task对应一个特定的事件,当task准备就绪时,对应的事件通知就会发出。Reactor收到事件后,分发给绑定了对应事件的Handler执行task。

Reactor:负责响应事件,分发给绑定了该事件的handler执行task

Handler:绑定了某类事件,负责执行该事件对应的task。

Acceptor:Handler的一种,绑定了connect事件。它在系统启动的时候就已经绑定了connnect事件,并注册到reactor中。当有客户端发起connect请求时,reactor会收到accept事件,然后分发给acceptor。acceptor首先会accept新的连接,然后新建一个handler,将其与该连接上得read事件绑定,并注册到reactor中。

上一篇下一篇

猜你喜欢

热点阅读