proactor结构模式在异步操作完成后触发服务请求的分配和分发 。
1. 举个例子吧
考虑一个需要同时处理多个请求的网络服务程序,比如,一个高效的WEB服务器需要并发的处理来自于不同客户端浏览器的HTTP请求。当一个用户希望从某个URL下载内容时,浏览器和WEB服务器建立连接并发送HTTP的GET请求。WEB服务器顺序执行了:接收浏览器的连接事件,接受连接请求,读取请求,然后解析请求,发送指定文件给WEB浏览器,并关闭连接。
一种有效的WEB服务器实现方式是使用reactor模式配合相应式的事件分发模型(reactive event demultiplexing model)。当WEB浏览器连接WEB服务器时,reactor创建并注册新的事件句柄,并关联事件的分配、分发。当WEB浏览器的REQUEST指令到达时,reactor激活对应的事件并回调事件句柄,然后句柄完成读、解析、处理请求并发送文件给WEB浏览器。尽管这个模型可以很顺畅的编程实现,但是它难以支持大量用户同时请求或/和长连接的用户请求,因为所有的HTTP处理在事件分发层是顺序执行的。
一个可以更有效的实现并发WEB服务器的方法是使用同步多线程。在这种模型下,会针对REQUEST请求创建新的服务线程。每个线程同步的处理建立连接、HTTP请求的读取、解析和文件传输操作,即服务器仅在完成操作后阻塞。尽管这是一个常见的并发模型,然而和reactor模式的缺陷一样,该模型存在着效率、编程复杂度、移植性低的问题。考虑到这些问题,reactor模式和多线程模式并不是WEB服务器理想的解决办法,我们需要另一种可以同时处理多个服务请求的事件驱动应用模型。
2. 问题所在
事件驱动的应用,尤其是服务器,在一个分布式系统中必须异步的处理多个服务请求,并异步的完成对服务请求的处理。
比如上面提到的WEB服务器的例子,我们的WEB服务器需要能在TCP连接、HTTP的GET请求到来时异步的接收完成事件。并且,WEB服务器可以异步的激活read和write操作传输文件给WEB浏览器。当这些操作完成时,操作系统分别传递READ和WRITE完成事件给WEB服务器。
在激活特定的服务处理完成事件之前,应用必须分配、分发事件到指定的服务句柄。为了有效的实现这一点,我们需要保证以下几点要求:
(1)应用必须可以同时处理多个事件,而不会由于某个事件的长时间处理导致其他事件产生巨大的延迟。
(2)服务器需要最小化延迟,最大化吞吐量,同时避免CPU的不必要开销。
(3)服务器的设计必须尽量简化并发策略的使用。
(4)集成新的或者改进的服务,例如改变信息格式或增加服务端缓存,应该尽量减少修改和维护已有代码的代价。例如对服务器客户端的改进不应该调整通用的事件分发机制。
3. 解决方案
我们需要异步激活操作并整合来源于这些操作的异步分发的完成事件和处理这些事件的服务句柄。另外, 将针对特定应用、事件的分发和服务分配器和通用概念上的分发、分配模型解耦。对所有服务器端应用提供的服务,我们提供一个异步操作表现与之关联的异步的服务请求,并使用一个完成句柄处理包括了每个异步操作结果的完成事件。异步操作由客户端的请求激活,并由异步操作处理器进行处理。异步操作处理器可以告知何时该完成事件的操作已经处理完成。另外,异步操作处理器通知proactor分发和该异步操作相关的特定应用的实例化完成句柄。然后该完成句柄处理异步操作的结果。
4. 结构
proactor模式主要由几下及部分组成:
(1) 和操作系统资源相关联的句柄:用于完成事件的生成、排队,如:网络连接、打开文件、计时、同步对象、和IO完成端口。
网络服务器为每个客户连接创建不同的套接字句柄。当异步连接、读、写操作执行完成时,完成事件会出现在这些句柄上。
(2)异步操作用于执行服务请求,比如异步的通过套接字句柄读写数据。当异步操作激活后,操作不需要借用回调线程的控制即可执行。因此从回调者角度看,操作的执行是异步的。
(3)完成句柄界定了由一个或多个钩子方法组成的接口,这些钩子函数抽象的代表当异步操作执行结束后,应用或服务对完成事件的处理。实例化的完成句柄继承自完成句柄,每个实例化的完成句柄实现了针对特定服务的方法。另外,实例化的完成句柄实现了继承的用于处理从外部资源传来的完成事件的钩子方法,如从远端客户端发送数据到服务端或者,或者应用内部产生的完成事件,如超时。当这些完成时间到达时,proactor分配合适的实例化完成句柄实现的钩子方法。
在我们的例子中,WEB服务器实现了两个实例化的完成句柄,HTTP句柄(HTTP Handler)和HTTP接收器(HTTP Acceptor)来表现异步操作的完成处理。HTTP句柄用于接收、处理、回复HTTP的GET请求。HTTP接收器创建、连接HTTP句柄处理异步来自于远处客户端的顺序请求。
(4)异步操作通过异步操作处理器(asynchronous operation processor)执行完成动作,异步操作处理器通常由操作系统内核实现。当异步操作执行完成时,异步操作处理器委托合适的proactor执行后续的完成分发。
(5)proactor即完成分发器(completion dispatcher),用于在相应的异步操作完成后回调指定的实例化完成句柄。为了增强复用性、分离关注点,proactor解耦了上层的完成句柄分发策略和底层的异步操作处理器提供的操作完成机制。
(6)客户端作为应用程序的实体初始化异步操作。这里客户端泛指任意可初始化异步操作的编程实体而不一定指的是C/S模型中的远端客户端。当服务器初始化异步操作时,它也可以扮演客户端的角色。当集活异步操作时,客户端也向异步操作处理器注册了完成句柄和proactor。
在WEB服务器中,异步操作的客户端指的是服务器的控制线程。这些线程在HTTP接收器和HTTP句柄上初始化了异步的accept和read/write操作来处理来自于WEB服务器的HTTP的GET请求,这些请求实际上即是远端客户端。
下图是proactor模式的整体结构图。
5. 整体流程
(1)为了异步式的执行服务,作为客户端的应用程序将异步操作传递给异步操作处理器以完成初始化。另外,客户端必须向异步操作处理器指明异步操作完成后回调哪个完成句柄执行后续的工作。
在WEB服务器的例子里,WEB服务器的HTTP句柄指导操作系统通过指定的套接字句柄异步完成传来的HTTP的GET请求的读操作。为了请求这个操作,WEB服务器必须指定应该使用哪个套接字句柄,同时指定读操作完成后应该调用哪个完成句柄执行后续的工作。
(2)客户端在异步操作处理器激活操作之后,操作和客户端可以和其他应用程序激活的异步操作一起并行的运行。
(3)当操作执行完后,异步操作处理器取回操作初始化时客户端限定的完成句柄和proactor。然后异步操作处理器传递异步操作的结果和完成句柄给proactor,由其完成分发。
在WEB服务器的例子里,如果HTTP句柄读取了GET请求,则异步操作处理报告合适的Proactor完成状态,如成功或失败,并返回读取的字节数等。
(4)proactor分发完成句柄的钩子方法,并传递完成数据。
例如WEB服务器可能指示操作系统异步的传输大量的文件穿过网络。当操作系统成功的完成了每个异步写操作后,proactor传递传递给完成句柄的钩子方法完成的写操作的字节数,这样下次激活时即可从对应的偏移量开始继续传输文件。
欢迎关注本人公众号,公众号会更新一些不一样的内容,相信一定会有所收获。