exact react中的路由
2011-06-25 16:48:00 fsmiy 阅读数 486

ACE为了能够在各种平台都能运行,针对windows、linux等定义了几种不同的Reactor,windows下的默认Reactor为WFMO_Reactor,其精度取决了Waitformultipleobjects的精度,单位为毫秒;Linux/Unix下默认的为select,可以精确到微妙。

由于受到对事件检测机制的影响,对于ACE_Select_Reactor来说其性能取决于select的性能,当select的句柄数量越来越大,其性能会越来越差;而ACE_WFMO_Reactor则由WaitForMultipleObjects决定,但是WaitForMultipleObjects最多只能检测64个句柄,如果应用程序要监听的句柄个数超过64个,则只能分组或者使用其他的检测机制来代替。

个人觉得,在windows下,对socket io有6中模型可根据选择,可以需要自行实现不同的reactor.

Linux/Unix也可以定义基于Poll或者epoll的reactor.其实现见以后章节。

exact react中的路由 相关内容

2018-10-09 08:40:36 zw19910924 阅读数 384

一、概述

在上一篇博客《Java 传统IO和NIO》 中,我讲述了java传统IO与NIO之间的区别以及NIO给我们带来的在网络编程上的性能的提升。然而,Java NIO以及后面要介绍的netty网络框架都是有一套理论在背后支撑的,那就是reactor模式的应用。

二、什么是reactor模式?

reactor模式翻译过来叫做反应器模式,通常我们都直接叫做reactor模式。

reactor模式是一种事件驱动模式,用于处理一个或者多个客户端发过来的请求,服务端会有一个处理器对到来的请求进行分离,并且将这些请求分发给对应的请求处理器进行处理。reactor的结构图大概如下图所示:
在这里插入图片描述

三、reactor模式的角色构成

从上面的结构图可以看出,Reactor模式由五中角色构成。分别是Handle(句柄或者描述符)、Synchronous Event Demultiplexer(同步事件分离器)、Event Handler(事件处理器)、Concrete Event Handler(具体事件处理器)、Initiation Dispatcher(初始分发器)。

  • Handle(句柄或者描述符):本质上表示一种资源,是由操作系统提供的;该资源用于表示一个个事件,比如说文件描述符,或者针对于网络编程当中的Socket描述符。事件既可以来自于外部,也可以来自于内部;外部事件比如说客户端的链接请求,客户端发过来的数据等;内部事件比如说操作系统产生的定时器事件等。它本质上就是一个文件描述符。Handle是事件产生的发源地。
  • Synchronous Event Demultiplexer(同步事件分离器):它本身是一个系统调用,用于等待事件的发生(事件有可能是一个,也有可能是多个)。调用方在调用它的时候会被阻塞,一直阻塞到同步时间分离器上有时间产生为止。对于linux来说,同步事件分离器直接就是常用的I/O多路复用机制,比如说select、poll、epoll等。在Java NIO领域中,同步事件分离器对应的组件就是Selector;对应的阻塞方法就是select()方法。
  • Event Handler(事件处理器):本身由多个回调方法构成,这些回调方法构成了与应用相关的对于某个事件的反馈机制。
  • Concrete Event Handler(具体事件处理器):是事件处理器的实现。它实现了事件处理器所提供的各个回调方法,从而实现了特定于业务的逻辑。它本质上就是我们编写的一个个的处理器的实现。
  • Initiation Dispatcher(初始分发器):它本身定义了一些规范,这些规范用于控制事件的调度方式,同时又提供了应用进行事件处理器的注册、删除等操作。它本身是整个事件处理器的核心所在,Initiation Dispatcher会通过同步事件分离器来等待事件的发生。一旦事件发生,Initiation Dispatcher首先会分离出每一个事件,然后调用事件处理器,最后调用相关的回调方法来处理这些事件。

四、reactor模式的流程

还是根据上面的图,可以看出reactor模式的流程:

  1. 当应用向Initiation Dispatcher注册具体的事件处理器时,应用会标示出该事件处理器希望Initiation Dispatcher在某个事件发生时向其通知的该事件,该事件与Handle关联。

  2. Initiation Dispatcher会要求每个事件处理器向其传递内部的Handle。该Handle向操作系统标示了事件处理器。

  3. 当所有的事件处理器注册完毕后,应用会调用handle_events方法来启动Initiation Dispatcher的事件循环。这时,Initiation Dispatcher会将每个注册的事件处理器的Handle合并起来,并使用同步事件分离器等待这些事件的发生。比如说,TCP协议层会使用select同步事件分离器操作来等待客户端发送的数据到达连接的socket handle上。

  4. 当与某个事件源对应的Handle变为ready状态时,(比如说TCP socket变为等待读状态时),同步事件分离器就会通知Initiation Dispatcher。

  5. Initiation Dispatcher会触发事件处理器的回调方法,从而响应这个处于ready状态的Handle。当事件发生时,Initiation Dispatcher会将被事件源激活的Handle作为[key]来寻找并分发恰当的事件处理器回调方法。

  6. Initiation Dispatcher会回调事件处理器的handle_event回调方法来执行特定于应用的功能(开发者自己所编写的功能),从而响应这个事件。所发生的事件类型可以作为该方法参数并被该方法内部使用来执行额外的特定于服务的分离与分发。

五、reactor在java领域的应用

1. 传统的java网络编程模型

在这里插入图片描述
采用java OIO的网络编程模型,客户端与服务端建立好连接过后,服务端对每一个建立好的连接使用一个handler来处理,而每个handler都会绑定一个线程。
这样做在连接的客户端不多的情况下,也算是个不错的选择。而且连接的客户端很多的情况下就会出现问题。
1) 每一个连接服务端都会产生一个线程,当并发量比较高的情况下,会产生大量的线程。
2) 在服务端很多线程的情况下,大量的线程的上下文切换是一个很大的开销,会比较影响性能。
3) 与服务端连接建立后,连接上未必是时时刻刻都有数据进行传输的,但是创建的线程一直都在,会造成服务端线程资源的一个极大的浪费。

2. 经典的reactor模式设计

由于java OIO的网络编程模型在客户端很多的情况下回产生服务端线程数过多的问题,因此根据reactor模式做出了改进。
在这里插入图片描述

根据上图,reactor角色对IO事件进行监听和分发。当事件产生式,reactor会分发给对应的处理器进行处理。OIO存在的一个问题是IO阻塞,因此一个socket开启一个线程来处理以防止一个连接IO的阻塞影响到其他的连接的处理。而这里通过对于IO事件的监听和分发,很好的解决了这个问题。服务端只需要一个IO线程就能处理多个客户端的连接。这就解决了OIO的阻塞问题和多个客户端连接而导致服务端线程数过多的问题。

但是这种模型还是有缺陷的,那就是所有的客户端的请求都由一个线程来进行处理,当并发量比较大的情况下,服务端的处理性能肯定会下降,因为服务端每次只能处理一个请求,其他的请求只能等待。

3. 多线程版本的Reactor模式设计

上面的单线程reactor模式因为服务端只有一个线程处理IO和业务逻辑,服务端性能肯定受到限制。因此就有了多线程版本:
在这里插入图片描述

如上图所示,reactor还是一个线程,负责监听IO事件以及分发。只不过业务逻辑处理部分使用了一个线程池来进行处理。这样就解决了服务端单线程处理请求而带来的性能瓶颈。
但是这样还是有问题,这样会把性能的瓶颈转移到IO处理上。因为IO事件的监听和分发采用的还是单个线程,在并发量比较高的情况下,这个也是比较影响性能的。这是否还有继续优化的空间呢?

4. 多reactor的多线程版本

我们知道reactor主要是负责IO事件的监听和分发。单个reactor单个线程这种模式在并发量比较高的情况下,会存在性能瓶颈。那么改进的方案显然就是采用多个reactor。
在这里插入图片描述
上图所示的mainReactor和subReactor都可能包含多个Selector(Java NIO),而每个Selector都是跟一个线程绑定的。这样就解决了单个reactor单个线程所带来的性能问题。实际上netty就是采用的这种设计,虽然具体的实现细节有些不一样,但是总体思想是一样的。

六、reactor模式在java NIO和netty中的应用

1. NIO与reactor模式的对应

我们知道,NIO的网络编程中,会有一个死循环执行Selector.select()操作,选择出注册到Selector上的Channel已经准备好的感兴趣的IO事件,然后再对这些事件进行处理。而NIO中的Selector组件对应的就是Reactor模式的同步事件分离器。选择过后得到的SelectionKey,其实就对应的是上面的句柄,也就是代表的一个个的IO事件。而NIO中并没有进行事件分发和封装处理器,因此Reactor模式中的其他组件NIO并没有给出实现。

2. Reactor模式在netty中的应用

上面java NIO实现了reactor模式的两个角色,而剩余的三个角色Netty给出了实现。学习过netty的应当知道,netty服务端的编程有一个bossGroup一个workerGroup,还需要编写自己的ChannelHandler。而bossGroup和workerGroup都是一个事件循环组(EventLoopGroup,一般我们用的是NIOEventLoopGroup),每个事件循环组有多个事件循环(EventLoop,NIO对应的是NIOEventLoop)。而bossGroup中的某一个事件循环就充当了Initiation Dispatcher(初始分发器)的角色,Netty中我们需要实现的Handler的顶层接口ChannelHandler对应的就是Event Handler(事件处理器)角色,而我们添加进去的一个个的Handler对应的就是Concrete Event Handler(具体事件处理器)。

结构上的对应:
Initiation Dispatcher ———— NioEventLoop
Synchronous EventDemultiplexer ———— Selector
Handle———— SelectionKey
Event Handler ———— ChannelHandler
ConcreteEventHandler ———— 具体的ChannelHandler的实现

上面分析的最后一个模型图上的角色对应:
Netty服务端使用了“多Reactor线程模式”
mainReactor ———— bossGroup(NioEventLoopGroup) 中的某个NioEventLoop
subReactor ———— workerGroup(NioEventLoopGroup) 中的某个NioEventLoop
acceptor ———— ServerBootstrapAcceptor
ThreadPool ———— 用户自定义线程池或者EventLoopGroup

七、总结

本博客介绍了reactor模式的理论基础以及reactor在java 在netty中的实现。特别是在netty中,reactor模式得到了很好的实现。而netty也是一个非常优秀的网络通信框架,无论是从框架本身的设计,还是其对于性能上的极致压缩来说,都是非常棒的。后面我会陆续写一些博客来对netty进行介绍。

exact react中的路由 相关内容

2013-07-08 19:24:45 ysu108 阅读数 3259

ACE中的Proactor和Reactor



        ACE_Select_Reactor是除Windows之外所有平台使用的默认反应器实现,在这些系统上最终会用select()系统函数进行等待。在Windows上ACE_WFMO_Reactor是默认的反应器实现。该实现没有使用select()多路分离器,而是使用了WaitForMultipleObjects()。在使用ACE_WFMO_Reactor时,需要一些权衡:ACE_WFMO_Reactor只能登记62个句柄。底层的WaitForMutipleObejcts()函数是64个,ACE在内部使用了其中的两个;I/O类型,ACE_WFMO_Reactor只在socket句柄上支持handler_input()、handler_output()和handler_exception()的I/O回调;只要句柄能用于WaitForMultipleObjects(),ACE_WFMO_Reactor就能对其做出反应,比如文件变更通知句柄和事件句柄。

       ACE_WIN32_Proactor是Windows上的ACE_Proactor实现,它使用I/O完成多口进行完成事件检测。在初始化异步操作工厂时(比如ACE_Asynch_Read_Stream或ACE_Aysnch_Write_Stream),I/O句柄会与Proactor的I/O完成端口关联在一起。在这种实现里,Windows的GetQueuedCompletionStatus()函数用于执行时间循环。多个线程可以同时执行ACE_WIN32_Proactor时间循环。POSIX系统上的ACE Proactor实现提供了多种用于发起I/O操作、并检查其完成的机制。ACE所封装的POSIX异步I/O机制支持read()和write()操作,但不支持与TCP/IP连接相关的操作,为了支持ACE_Asynch_Acceptro和ACE_Asynch_Connector的函数,ACE使用了一个单独的线程来执行与连接有关的操作。因此,在POSIX平台上使用Proactor框架时,程序将会运行多个线程。ACE的内部机制使得你不用再多个线程中对时间进行处理,所以你无需增加任何特殊的加锁或同步。

        首先想写网络处理程序,那么要清楚各个步骤的限制是什么,简单的说,肯定有读取(写)数据,和处理数据两个部分,那么这个两个部分有什么特点呢?读取数据主要是I/O操作,而单个的I/O操作可能会有等待,而且I/O操作对CPU的耗费很少,最重要的是I/O速度与CPU速度比跟龟速差不多。一个线程就可以处理很多路的I/O操作,在一次I/O完成的时候会激活分离器,由分离器调用事件处理器。做个假设,可能同时有1000个数据请求,如果不用reactor,那么可能会开启1000个线程或者进程进行服务,这个时候首先线程之间的切换和锁开销是非常大的,而如果采用reactor,可能会只用一个读线程,轮流读取这1000个请求,然后当其中的一个读取完成后,激活分离器,分离器调用事件处理器,这样的话,如果系统处理数据很快的话,那么同时存在的线程可能很少,就减少了系统的线程创建切换和销毁。

        记得以前写过的服务器程序,都是有一个读取请求线程池,一个处理线程池,这样的话其实和Reactor模式的原理差不多,把读取和处理分离开来,然后用一个消息队列来完成读取与处理的通信,这样会用到锁,锁的设计不仅复杂而且会有系统开销。但是Reactor没有用锁,只把一个buffer告诉分发器,当读取结束就回调处理handler。(在ACE中是否用锁实现的不是很确定)而且,想刚才那样设计交互就比较麻烦,如果光是读还比较简单,但是读到如果会送那么就比较麻烦了。

下面是别的博文上总结Reactor和Proactor的读的过程:

在Reactor中实现读:
- 注册读就绪事件和相应的事件处理器
- 事件分离器等待事件
- 事件到来,激活分离器,分离器调用事件对应的处理器。

- 事件处理器完成实际的读操作,处理读到的数据,注册新的事件,然后返还控制权。

在Proactor中实现读:
- 处理器发起异步读操作(注意:操作系统必须支持异步IO)。在这种情况下,处理器无视IO就绪事件,它关注的是完成事件。
- 事件分离器等待操作完成事件
- 在分离器等待过程中,操作系统利用并行的内核线程执行实际的读操作,并将结果数据存入用户自定义缓冲区,最后通知事件分离器读操作完成。
- 事件分离器呼唤处理器。
- 事件处理器处理用户自定义缓冲区中的数据,然后启动一个新的异步操作,并将控制权返回事件分离器


简单总结下相同点与不同点:
相同点
1)都有Event Demutiplexer(为什么叫事件多路分离器呢?主要是从它的功能上看,它能够把事件源的I/O时间分离出来,并分发到对应的read/write事件处理区(Event Handler)),负责回调handler。
2)读(写)数据,与读结束后处理为分开的两个过程,这也是Reactor的重点所在,不用一个线程完成从读数据一直到处理结束,读数据线程和处理线程是两个不同的线程。这样做有个优点,因为读取数据可能会很耗费时间,读取其实瓶颈为系统的I/O,一个线程就可以处理很多路数据的I/O处理。

不同点:
1)Proactor是在I/O操作完成才回调handler,而Reactor是在I/O可以进行读或写操作时候调用handler。
2)Proactor的读写是利用操作系统支持异步I/O读写操作完成的,而Reactor的I/O操作是由用户完成的。
3)Reactor为同步的,需要用户自己去执行I/O操作然后等待I/O操作完成,在执行某些操作,Proactor为异步的,发起I/O操作后就交给操作系统了,只关心IO完成事件。这里一开始理解有个小的误区,


        其实Reactor和Proactor在等待I/O事件到来的时候都可以理解为异步的,可能都用到select()底层函数,当端口可操作时候由操作系统异步的通知,这个时候Reactor收到通知,调用handler。然而Proactor收到端口可操作的时候还不满意,需要在I/O操作完成后再异步的通知,这样Proactor相当于两层异步操作,而说Reactor和Proactor的“异步”,指的为后面的I/O操作。

        “Reactor框架中用户定义的操作是在实际操作之前调用的。比如你定义了操作是要向一个SOCKET写数据,那么当该SOCKET可以接收数据的时候,你的操作就会被调用;而Proactor框架中用户定义的操作是在实际操作之后调用的。比如你定义了一个操作要显示从SOCKET中读入的数据,那么当读操作完成以后,你的操作才会被调用。”

参考:

http://www.cnblogs.com/dawen/archive/2011/05/18/2050358.html
http://name5566.com/4175.html

exact react中的路由 相关内容

2016-12-12 15:07:55 honglicu123 阅读数 1136

在学习 redis 源码时,发现redis使用的是 reactor 模式的事件驱动方式,为了进一步学习 redis 和 reactor 模式,又不想重复造轮子,干脆动手将 redis 中的 reactor 模式的框子娄出来,做了一个小 demo,废话不多说,源码可以通过下面的网址下载。

https://github.com/small-cat/my_redis

参考文章:
http://www.open-open.com/lib/view/open1452486726589.html

exact react中的路由 相关内容

2008-11-14 17:02:00 pythoner 阅读数 48

首先,引用官方文档中的解释:

The reactor is Twisted's main event loop. There is exactly one reactor in any running Twisted application. Once started it loops over and over again, responding to network events, and making scheduled calls to code.

也就是说reactor是twisted中主要的事件循环。每个twisted应用都必须要使用一个reactor,当它启动之后就会不停地进行循环操作,对网络事件进行响应,并对程序进行调度。

未完待续...

exact react中的路由 相关内容

没有更多推荐了,返回首页