Reactor 线程模型浅析
Reactor模式是处理并发I/O比较常见的一种模式,用于同步I/O,中心思想是将所有要处理的I/O事件注册到一个中心I/O多路复用器上,同时主线程阻塞在多路复用器上;一旦有I/O事件到来或是准备就绪(区别在于多路复用器是边沿触发还是水平触发),多路复用器返回并将相应I/O事件分发到对应的处理器中。
Reactor是一种事件驱动机制,和普通函数调用的不同之处在于:应用程序不是主动的调用某个API完成处理,而是恰恰相反,Reactor逆置了事件处理流程,应用程序需要提供相应的接口并注册到Reactor上,如果相应的事件发生,Reactor将主动调用应用程序注册的接口,这些接口又称为“回调函数”。用“好莱坞原则”来形容Reactor再合适不过了:不要打电话给我们,我们会打电话通知你。
Reactor模式与Observer模式在某些方面极为相似:当一个主体发生改变时,所有依属体都得到通知。不过,观察者模式与单个事件源关联,而反应器模式则与多个事件源关联 。
Reactor 结构
上图是 Reactor 模型,主要涉及的类:
Initiation Dispatcher:EventHandler 的容器,用来注册、移除 EventHandler 等;另外,它作为 Reactor 模式的入口调用 Synchronous Event Demultiplexer 的 select 方法以阻塞等待事件的返回,当阻塞事件返回时,将事件发生的 Handle 分发到相应的 EvenHandler 处理。
Even Handler:定义了事件处理的方法。
Handle:即操作系统中的句柄,是对资源在操作系统层面上的一种抽象,它可以是打开的文件、一个连接(Socket)、Timer等。
Synchronous Event Demultiplexer:使用一个事件循环 ,以阻止所有的资源。当可以启动一个同步操作上的资源不会阻塞,多路分解器发送资源到分发器。
Reactor 时序图
初始化 InitationDispatcher,并初始化一个Handle到EventHandler的Map。
注册 EvenHandler 到 InitationDispatcher,每个 EventHandler 包含对相应 Handle的引用,从而建立Handle到EventHandler的映射(Map)。
调用 InitiationDispatcher 的 handle_events() 方法以启动 Event Loop。在 EventLoop 中,调用select()方法(Synchronous Event Demultiplexer)阻塞等待Event发生。当某个或某些 Handle 的 Event 发生后,select() 方法返回,InitiationDispatcher根据返回的Handle找到注册的 EventHandler ,并回调该 EventHandler 的 handle_events()方法。
在 EventHandler 的 handle_events() 方法中还可以向 InitiationDispatcher 中注册新Eventhandler,比如对 AcceptorEventHandler 来说,当有新的 client 连接时,它会产生新的EventHandler 以处理新的连接,并注册到 InitiationDispatcher 中。
【文章福利】小编推荐自己的Linux、C/C++技术交流群:【960994558】整理了一些个人觉得比较好的学习书籍、视频资料共享在里面(包括C/C++,Linux,Nginx,ZeroMQ,MySQL,Redis,fastdfs,MongoDB,ZK,流媒体,CDN,P2P,K8S,Docker,TCP/IP,协程,DPDK等等.),有需要的可以自行添加哦!~
模式模型
单线程模型
这是最简单的单Reactor单线程模型。Reactor线程是个多面手,负责多路分离套接字,Accept新连接,并分派请求到处理器链中。该模型适用于处理器链中业务处理组件能快速完成的场景。不过这种单线程模型不能充分利用多核资源,所以实际使用的不多。
多线程模型(单Reactor)
相比上一种模型,该模型在事件处理器(Handler)链部分采用了多线程(线程池),也是后端程序常用的模型。
多线程模型(多Reactor)
这个模型比起第二种模型,它是将Reactor分成两部分,mainReactor负责监听并accept新连接,然后将建立的socket通过多路复用器(Acceptor)分派给subReactor。subReactor负责多路分离已连接的socket,读写网络数据;业务处理功能,其交给worker线程池完成。通常,subReactor个数上可与CPU个数等同。
Reactor 的优缺点
优点:
大多数设计模式的共性:解耦、提升复用性、模块化、可移植性、事件驱动、细力度的并发控制等。
更为显著的是对性能的提升,即不需要每个 Client 对应一个线程,减少线程的使用。
缺点:
相比传统的简单模型,Reactor增加了一定的复杂性,因而有一定的门槛,并且不易于调试。
Reactor模式需要底层的Synchronous Event Demultiplexer支持,比如Java中的Selector支持,操作系统的select系统调用支持,如果要自己实现Synchronous Event Demultiplexer可能不会有那么高效。
Reactor模式在IO读写数据时还是在同一个线程中实现的,即使使用多个Reactor机制的情况下,那些共享一个Reactor的Channel如果出现一个长时间的数据读写,会影响这个Reactor中其他Channel的相应时间,比如在大文件传输时,IO操作就会影响其他Client的相应时间,因而对这种操作,使用传统的Thread-Per-Connection或许是一个更好的选择,或则此时使用Proactor模式。
版权声明: 本文为 InfoQ 作者【赖猫】的原创文章。
原文链接:【http://xie.infoq.cn/article/58b49765dc5601149afecd902】。文章转载请联系作者。
评论