2.3主动器(Proactor)
1.问题
异步处理多个服务请求通常可以改善分布式系统中的事件驱动应用程序的性能。完成异步服务处理后,应该程序必须处理由操作系统发出的表示异步计算结束的相应的完成事件。要有效地支持这种异步计算模型,需解决以下四个强制条件:
1)为了改进可扩展性和延迟性能,一个应用程序必须在禁止耗时长的操作过分地延迟其他操作处理的情况下同时处理多个完成事件。
2)为了使吞吐率最大,应避免CPU之间的任何不必要的语境切换,同步和数据移动。
3)将新的或改进的服务与已有的完成事件多路分解与分配机制集成所花的代价要最少。
4)应用程序代码应尽量不受多线程和同步机制复杂性的影响。
2.解决方案
将应用服务分成两部分:异步执行的耗时长的操作和在这些操作完成后处理其结果的完成处理程序。将异步操作完成时对完成事件的多路分解和将它们分配到相应的处理它们的完成处理程序这两部分集成起来。将完成事件多路分解与分配机制与完成处理程序中与应用有关的对完成事件的处理分开。
具体地说就是:为应用程序所提供的每个服务引入一个异步操作。由这些异步操作通过一个句柄和完成处理程序来主动地初始化对服务请求的处理,完成处理程序处理包含这些异步操作结果的完成事件。例如,可以在应用程序中由启动程序调用异步操作来接受从远程应用程序发来的连接请求。异步操作由异步操作处理器执行。操作执行完毕后,异步操作处理器在完成事件队列中插入一个包含该操作结果的完成事件。
称为主动器的异步事件多路分解器等待该队列。异步事件多路分解器从队列中删除一个完成事件后,主动器向该异步操作相应的与应用有关的完成处理程序多路分解和分配该事件,然后该完成处理程序处理异步操作的结果,可能要调用其他的异步操作,这些异步操作遵循上述活动链。
3.结构
操作系统提供句柄(handle)以标识实体(如网络连接)或打开文件(能产生完成事件),可以响应外部服务请求(如远程应用程序发来的连接或数据请求)而产生完成事件,也可以响应应用程序内部产生的操作(如超时或异步I/O系统调用)而产生完成事件。
异步操作(asynchronou operation)表示在实现如通过socket句柄异步读写数据这样的服务时要使用的耗时长的操作。调用一个异步操作后,执行该异步操作而不阻塞调用者的控制线程。这样调用者可以执行其他操作。如果一个操作必须等待另一个事件的发生,如远程应用程序产生的一个连接请求,则它要被延迟到事件到达后才开始执行。
完成处理操作(Completion handler)定义了一个由一个或多个钩子方法组成的接口,可以使用这些方法处理与应用有关的完成事件中返回的信息。完成事件是在异步操作执行完后产生的。
实现继承的钩子方法后,具体完成处理程序(concrete completion handler)特化了一个完成处理程序以定义一个特定的应用服务。当与完成处理程序相关的异步操作执行完后,钩子方法接收并处理包含在完成事件中的结果信息。一个具体完成处理程序对应于一个可以用于调用异步操作的句柄。
异步操作处理器(asychronous operation processor)调用某一个句柄的异步操作并运行到结束,通常由操作系统内核实现异步操作处理器。异步操作执行完后,异步操作处理器产生相应的完成事件。根据操作所针对的句柄,异步操作处理器将该完成事件插入到与句柄相对应的完成事件队列(completion event queue)中,队列中保存有要被多路分解到相应的完成处理程序上的完成事件。
异步事件多路分解器(asynchronous event demultiplexer)是一个函数,它等待在异步操作执行完后将被插入到完成事件队列中的完成事件。然后异步事件多路分解器函数将一个或多个完成事件从队列中删除,并返回给调用者。
主动器(proactor)为应用程序进程或线程提供事件循环。在该事件循环中,主动器调用一个异步事件多路分解器,等待完成事件的发生。当事件到达时,异步事件多路分解器返回。然后主动器将该事件多路分解给相应的完成程序,并为该处理程序分配合适的钩子方法,以处理完成事件的结果。
启动程序(initiator)是应用程序内部的一个实体,它调用异步操作处理器的异步操作。通常启动程序处理所调用异步操作的结果,在这种情况下,它就担当了具体完成处理程序的角色。
4.实现
主动器模式中的参与者可以划分为两层:
·多路分解/分配基础设施层组件。该层按照通用的,与应用无关的策略执行异步操作。它也将异步操作的完成事件多路分解和分配给相应的完成处理程序。
·应用程序层的组件。本层定义了执行与应用有关的服务处理的异步操作和具体完成处理程序。
1)将应用程序服务分成异步操作和完成处理程序。为了实现主动器模式,必须将应用服务中通过句柄对异步操作的激活与对这些操作结果的处理已分开。该活动的结果是一组异步操作,一组完成处理程序,以及一组异步操作与完成处理程序之间的关联关系。
2)定义完成处理程序接口。完成处理程序中的接口由一个或多个钩子方法组成。这些钩子方法表示对与应用有关的完成事件的完成处理,这些完成事件是在异步操作执行完毕后产生的。
2.1)定义一种传递异步操作结果的类型。当异步操作完成或者被取消后,必须将完成事件结果传递给完成处理程序。这些结果表示异步操作是成功还是失败,以及成功传送的字节数。
2.2)确定分配目标的类型。有两类完成处理程序可以与句柄关联,并作为主动器分配机制的目标:对象和函数指针。
2.3)定义完成处理程序分配接口策略。单方法分配接口策略和多方法分配接口策略。
3)实现异步操作处理器。
3.1)定义异步操作接口。可以向异步操作传递不同的参数,如句柄、数据缓冲区、缓冲区长度以及操作完成后执行完成处理所需的信息。设计启动程序用以调用异步操作处理器的异步操作的程序接口时,要解决两个问题:
·尽量增加可移植性和灵活性。
·有效地,简洁地处理多个完成处理程序,主动器和完成事件队列。
3.2)选择异步操作处理机制。当启动程序调用一个异步操作时,异步操作处理器在不阻塞启动程序的控制线程的情况下执行该操作。一个异步操作处理器提供管理异步完成标识和异步执行操作的机制。当操作结束后它也产生完成事件,并将该事件插入到适当的完成事件队列中。
有些异步操作处理器允许启动程序取消异步操作,但完成事件还是要产生的。这样,完成处理程序就可以正确地重新利用异步完成标记和其他资源。
4)定义主动器接口。应用程序使用主动器接口来调用一个事件循环,从完成事件队列中删除完成事件,将完成事件多路分解给相应的完成处理程序并分配相应的钩子方法。
5)实现主动器接口。
5.1)开发主动器实现层。
5.2)选择完成事件队列和异步事件多路分解器机制。
·FIFO多路分解。这一类异步事件多路分解器函数等待与完成事件队列相关联的任何异步操作所对应的完成事件,按照这些完成事件的插入顺序删除这些事件。
·选择性的多路分解。这种类型的异步事件多路器函数有选择性地等待完成事件的特定子集,当函数被调用时必须显式地传递这些完成事件。
5.3)确定如何向完成处理程序多路分解完成事件。向完成处理程序多路分解完成事件的一个有效的,简洁的策略是使用异步完成标记模式。使用这种策略的话,一旦启动程序调用异步操作,异步操作处理器就会收到用于指导后续完成处理的信息。
异步操作完成时,异步操作处理器产生相应的完成事件,将完成事件和异步完成标记关联起来,并将修改后的完成事件插入到相应的完成事件队列中。异步事件多路分解器从它的完成事件队列中删除完成事件后,主动器实现可以作为完成事件的异步完成标记在常数时间O(1)内多路分解给指定的完成处理程序。
5.4)确定如何为指定的完成处理程序分配钩子方法。
5.5)定义具体的主动器实现。
6)确定应用程序中主动器的数量。
7)实现具体完成处理程序。
7.1)确定维护具体完成处理程序的状态的策略。
7.2)选择用句柄配置具体完成处理程序的机制。
7.3)实现完成处理程序的功能。
8)实现启动程序。
5.结论
优点:
1)事务分离。
2)可移植性。
3)并发机制的封装。将主动器和异步操作处理器分开的一个好处是应用程序可以用不同的并发策略配置主动器,而不会影响其他应用程序组件和服务。
4)将线程化与并发控制分开。
5)性能。
6)简化应用程序的同步。
不足:
1)应用范围受到限制。
2)编程、调试和测试复杂性。
3)异步地调度、控制和取消正在执行的操作。