网络资源整合:Reactor模式的实现方式

Reactor模式是处理并发I/O比较常见的一种模式,用于同步I/O,中心思想是将所有要处理的I/O事件注册到一个中心I/O多路复用器上,同时主线程阻塞在多路复用器上;一旦有I/O事件到来或是准备就绪(区别在于多路复用器是边沿触发还是水平触发),多路复用器返回并将相应I/O事件分发到对应的处理器中。

Reactor是一种事件驱动机制,和普通函数调用的不同之处在于:应用程序不是主动的调用某个API完成处理,而是恰恰相反,Reactor逆置了事件处理流程,应用程序需要提供相应的接口并注册到Reactor上,如果相应的事件发生,Reactor将主动调用应用程序注册的接口,这些接口又称为“回调函数”。用“好莱坞原则”来形容Reactor再合适不过了:不要打电话给我们,我们会打电话通知你。

Reactor模式与Observer模式在某些方面极为相似:当一个主体发生改变时,所有依属体都得到通知。不过,观察者模式与单个事件源关联,而反应器模式则与多个事件源关联 。

模式框架

1) Handle

Handle代表操作系统管理的资源,包括:网络链接,打开的文件,计时器,同步对象等等。Linux上是文件描述符,Windows上就是Socket或者Handle了,这里统一称为“句柄集”;程序在指定的句柄上注册关心的事件,比如I/O事件。

2) Event Demultiplexer

事件分离器,由操作系统提供,在linux上一般是select,poll,epoll等系统调用,在一个Handle集合上等待事件的发生。接受client连接,建立对应client的事件处理器(Event Handler),并向事件分发器(Reactor)注册此事件处理器(Handler)。

3) Reactor(Initiation Dispatcher)

提供接口:注册,删除和派发Event Handler。Event Demultiplexer等待事件的发生,当检测到新的事件,就把事件交给Initiation Dispatcher,它去回调Event Handler。

4) Event Handler

事件处理器,负责处理特定事件的处理函数。一般在基本的Handler基础上还会有更进一步的层次划分,用来抽象诸如decode,process和encoder这些过程。比如对Web Server而言,decode通常是HTTP请求的解析,process的过程会进一步涉及到Listner和Servlet的调用。为了简化设计,Event Handler通常被设计成状态机,按GoF的state pattern来实现。

5) Concrete Event Handler

继承上面的类,实现钩子方法。应用把Concrete Event Handler注册到Reactor,等待被处理的事件。当事件发生,这些方法被回调。

事件处理流程

模式模型

应用场景举例

场景:长途客车在路途上,有人上车有人下车,但是乘客总是希望能够在客车上得到休息。

传统做法:每隔一段时间(或每一个站),司机或售票员对每一个乘客询问是否下车。

Reactor做法:汽车是乘客访问的主体(Reactor),乘客上车后,到售票员(acceptor)处登记,之后乘客便可以休息睡觉去了,当到达乘客所要到达的目的地后,售票员将其唤醒即可。

1) 单线程模型

这是最简单的单Reactor单线程模型。Reactor线程是个多面手,负责多路分离套接字,Accept新连接,并分派请求到处理器链中。该模型适用于处理器链中业务处理组件能快速完成的场景。不过这种单线程模型不能充分利用多核资源,所以实际使用的不多。

2) 多线程模型(单Reactor)

相比上一种模型,该模型在事件处理器(Handler)链部分采用了多线程(线程池),也是后端程序常用的模型。

3) 多线程模型(多Reactor)

这个模型比起第二种模型,它是将Reactor分成两部分,mainReactor负责监听并accept新连接,然后将建立的socket通过多路复用器(Acceptor)分派给subReactor。subReactor负责多路分离已连接的socket,读写网络数据;业务处理功能,其交给worker线程池完成。通常,subReactor个数上可与CPU个数等同。

优缺点

优点

  • 响应快,不必为单个同步时间所阻塞,虽然Reactor本身依然是同步的;

  • 编程相对简单,可以最大程度的避免复杂的多线程及同步问题,并且避免了多线程/进程的切换开销;

  • 可扩展性,可以方便的通过增加Reactor实例个数来充分利用CPU资源;

  • 可复用性,Reactor框架本身与具体事件处理逻辑无关,具有很高的复用性;

缺点

  • 应用受限制:Reactor模式只能应用在支持Handle的操作系统上。虽然可以使用多线程模拟Reactor,但因为同步控制和上下文切换的要求,这种实现效率低,与Reactor模式出发点相违背。

  • 非抢占模式:在单线程的实现这种情况下,事件的处理必须不能使用阻塞的I/O,因此,如果存在长期操作,比如传输大量的数据。使用主动对象,效率可能更好,主动对象可以并发的处理这些任务。

  • 难以调试:使用Reactor模式的应用程序可能会难以调试,因为程序运行的控制流会在框架和应用相关的处理器之间跳转,不了解框架的应用程序开发人员难一跟着调试。

相关库

ACE

ACE是一个大型的中间件产品,代码20万行左右,过于宏大,一堆的设计模式,架构了一层又一层,使用的时候,要根据情况,看从那一层来进行使用。支持跨平台。

设计模式 :ACE主要应用了Reactor,Proactor等;

层次架构 :ACE底层是C风格的OS适配层,上一层基于C++的wrap类,再上一层是一些框架 (Accpetor,Connector,Reactor,Proactor等),最上一层是框架上服务;

可移植性 :ACE支持多种平台,可移植性不存在问题,据说socket编程在linux下有不少bugs;

事件分派处理 :ACE主要是注册handler类,当事件分派时,调用其handler的虚挂勾函数。实现 ACE_Handler/ACE_Svc_Handler/ACE_Event_handler等类的虚函数;

涉及范围 :ACE包含了日志,IPC,线程池,共享内存,配置服务,递归锁,定时器等;

线程调度 :ACE的Reactor是单线程调度,Proactor支持多线程调度;

发布方式 :ACE是开源免费的,不依赖于第三方库,一般应用使用它时,以动态链接的方式发布动态库;开发难度 :基于ACE开发应用,对程序员要求比较高,要用好它,必须非常了解其框架。在其框架下开发,往往new出一个对象,不知在什么地方释放好。

Libevent

libevent是一个C语言写的网络库,官方主要支持的是类linux操作系统,最新的版本添加了对windows的IOCP的支持。在跨平台方面主要通过select模型来进行支持。

设计模式 :libevent为Reactor模式;

层次架构:livevent在不同的操作系统下,做了多路复用模型的抽象,可以选择使用不同的模型,通过事件函数提供服务;

可移植性 :libevent主要支持linux平台,freebsd平台,其他平台下通过select模型进行支持,效率不是太高;

事件分派处理 :libevent基于注册的事件回调函数来实现事件分发;

涉及范围 :libevent只提供了简单的网络API的封装,线程池,内存池,递归锁等均需要自己实现;

线程调度 :libevent的线程调度需要自己来注册不同的事件句柄;

发布方式 :libevent为开源免费的,一般编译为静态库进行使用;

开发难度 :基于libevent开发应用,相对容易,具体可以参考memcached这个开源的应用,里面使用了 libevent这个库。

Libev

与 libevent 一样,libev 系统也是基于事件循环的系统,它在 poll()、select() 等机制的本机实现的基础上提供基于事件的循环。libev 实现的开销更低,能够实现更好的基准测试结果。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


react 中的高阶组件主要是对于 hooks 之前的类组件来说的,如果组件之中有复用的代码,需要重新创建一个父类,父类中存储公共代码,返回子类,同时把公用属性...
我们上一节了解了组件的更新机制,但是只是停留在表层上,例如我们的 setState 函数式同步执行的,我们的事件处理直接绑定在了 dom 元素上,这些都跟 re...
我们上一节了解了 react 的虚拟 dom 的格式,如何把虚拟 dom 转为真实 dom 进行挂载。其实函数是组件和类组件也是在这个基础上包裹了一层,一个是调...
react 本身提供了克隆组件的方法,但是平时开发中可能很少使用,可能是不了解。我公司的项目就没有使用,但是在很多三方库中都有使用。本小节我们来学习下如果使用该...
mobx 是一个简单可扩展的状态管理库,中文官网链接。小编在接触 react 就一直使用 mobx 库,上手简单不复杂。
我们在平常的开发中不可避免的会有很多列表渲染逻辑,在 pc 端可以使用分页进行渲染数限制,在移动端可以使用下拉加载更多。但是对于大量的列表渲染,特别像有实时数据...
本小节开始前,我们先答复下一个同学的问题。上一小节发布后,有小伙伴后台来信问到:‘小编你只讲了类组件中怎么使用 ref,那在函数式组件中怎么使用呢?’。确实我们...
上一小节我们了解了固定高度的滚动列表实现,因为是固定高度所以容器总高度和每个元素的 size、offset 很容易得到,这种场景也适合我们常见的大部分场景,例如...
上一小节我们处理了 setState 的批量更新机制,但是我们有两个遗漏点,一个是源码中的 setState 可以传入函数,同时 setState 可以传入第二...
我们知道 react 进行页面渲染或者刷新的时候,会从根节点到子节点全部执行一遍,即使子组件中没有状态的改变,也会执行。这就造成了性能不必要的浪费。之前我们了解...
在平时工作中的某些场景下,你可能想在整个组件树中传递数据,但却不想手动地通过 props 属性在每一层传递属性,contextAPI 应用而生。
楼主最近入职新单位了,恰好新单位使用的技术栈是 react,因为之前一直进行的是 vue2/vue3 和小程序开发,对于这些技术栈实现机制也有一些了解,最少面试...
我们上一节了了解了函数式组件和类组件的处理方式,本质就是处理基于 babel 处理后的 type 类型,最后还是要处理虚拟 dom。本小节我们学习下组件的更新机...
前面几节我们学习了解了 react 的渲染机制和生命周期,本节我们正式进入基本面试必考的核心地带 -- diff 算法,了解如何优化和复用 dom 操作的,还有...
我们在之前已经学习过 react 生命周期,但是在 16 版本中 will 类的生命周期进行了废除,虽然依然可以用,但是需要加上 UNSAFE 开头,表示是不安...
上一小节我们学习了 react 中类组件的优化方式,对于 hooks 为主流的函数式编程,react 也提供了优化方式 memo 方法,本小节我们来了解下它的用...
开源不易,感谢你的支持,❤ star me if you like concent ^_^
hel-micro,模块联邦sdk化,免构建、热更新、工具链无关的微模块方案 ,欢迎关注与了解
本文主题围绕concent的setup和react的五把钩子来展开,既然提到了setup就离不开composition api这个关键词,准确的说setup是由...
ReactsetState的执行是异步还是同步官方文档是这么说的setState()doesnotalwaysimmediatelyupdatethecomponent.Itmaybatchordefertheupdateuntillater.Thismakesreadingthis.staterightaftercallingsetState()apotentialpitfall.Instead,usecom