Proactor
Proactor 架构模式用于事件驱动的应用程序,同 Reactor 模式一样,也需要解多路复用,分发事件,但是 Proactor 解多路复用和分发的事件是在异步操作完成时触发的,异步操作的并行性提升了应用程序性能。
Context
一个事件驱动应用程序,异步地接收和处理服务请求。
Problem
异步地处理多个服务请求通常能提升分布式系统中事件驱动程序的性能,特别是服务器。当异步服务处理完成时,操作系统会递交的对应的完成事件表应用程序需要处理该事件。
应用程序必须解多路复用,分发每个完成事件到一个内部组件,内部组件处理异步操作的结果。为了高效地实现这一异步计算模型,需要关注以下 4 点:
- 为了提高拓展性,降低延迟,应用程序应该同时处理多个完成事件,不允许长时间的操作过度延迟其他的操作。
- 为了最大化吞吐量,任何不必要的上下文转换,同步,和 CPUs 间的数据移动应该都被避免。
- 便利地将新的或升级过的服务集成到现存的完成事件解多路复用和分发机制中。
- 应用代码应当在最大程度上被保护,以远离多线程和同步的复杂度。
Solution
我们将服务分为两部分:异步执行的长时间操作,和处理操作完成时的结果的完成处理程序。集成完成事件的解多路复用
和完成事件的分发
;完成事件在异步操作完成时被递交,随后完成事件被分发到完成处理程序进行处理
。并将完成事件的解多路复用和分发从完成事件处理程序中应用程序指定的完成事件处理中解耦出来。
具体来说:对应用程序提供的服务来说,asynchronous operations 通过一个 handle “前摄地” 初始化服务请求的处理,并使用 completion handlers 处理包含异步操作结果的完成事件。异步操作通过一个 initiator 调用,例如,接受来自远端应用程序的连接请求。异步操作通过 asynchronous operation processor 执行。当操作完成执行时,异步操作处理器插入包含异步操作结果的完成事件到 completion event queue。
proactor 调用 asynchronous event demultiplexer 在完成事件队列上等待。当异步事件解多路复用器从事件完成队列中移除一个完成事件,前摄器解多路复用并分发该事件到完成事件处理程序中。完成事件处理程序随后处理异步操作的结果,可能调用遵循上述活动链的其他异步操作。
Structure
前摄器模式包含 9 个参与方:
Handles 由操作系统提供,用于标识能产生完成事件的实体,例如网络连接和打开的文件。完成事件要不作为服务请求的响应,例如连接和数据请求,或作为应用内部操作的响应,例如超时或异步 I/O 系统调用。
Asynchronous operations 表示长时间的操作,例如通过 socket 异步读写数据。执行异步操作不会阻塞调用方的控制线程。因此,调用方能执行其他操作。如果操作必须等待事件的发生,例如连接请求,它的执行将被推迟到事件的发生。
Completion handler 指定由一个或多个 hook 方法组成的接口。这些方法表示处理完成事件中包含的信息时可用的操作。
Concrete completion handlers 特化完成处理程序,通过实现继承的 hook method 定义特定的应用服务。一个具体的完成处理程序关联一个 handle,handle 被用于调用它自己的异步操作。例如,一个具体的异步处理程序能接收来自早先它在 handle 上调用的异步读操作的数据。当异步读取时,具体的完成处理程序可以处理它所接收的数据,并调用异步写操作向对端返回处理结果。
异步操作在特定的 handle 上调用,并由 asynchronous operation processor 运行至完成,异步操作处理器通常由内核实现。当异步操作完成时,异步操作处理器生成对应的完成事件。它将该事件插入到触发该操作的 handle 所关联的 completion event queue 中。该队列缓冲完成事件,完成事件在队列上等待被解多路复用到相关的事件处理程序中。
Asynchronous event demultiplexer 是一个等待完成事件被插入到完成事件队列的函数。异步事件解多路复用器函数从队列中移除一个或多个完成事件结果,将其返回给调用方。
Proactor 为应用进程或线程提供事件循环。在事件循环中,前摄器调用异步事件解多路复用器等待完成事件的发生。当事件发生时,异步事件解多路复用器返回。前摄器随后解多路复用事件到它所关联的完成处理程序中,并分发合适的 hook 方法处理完成事件的结果。
Initiator 是调用异步操作的应用程序的本地实体。初始化器通常处理它所调用的异步操作的结果,这种情况下它也扮演着具体的完成处理程序的角色。
在 Proactor 模式中,表示初始化器和具体的完成处理程序的应用组件是 procative 实体。它们通过调用异步操作启动应用程序中的数据流和控制流。
当异步操作完成时,异步操作处理器和前摄器借助完成事件队列合作,解多路复用完成事件到相关的完成事件处理程序上,并分发完成事件到对应的 hook 方法中。处理完成事件时,完成处理程序可能调用新的异步操作。
Dynamics
Proactor 模式发生下述合作:
- 扮演初始化器的应用组件通过特定的 handle 在异步操作处理器上调用异步操作。除了向异步操作传递数据参数,初始化器也向完成事件队列传递一些完成处理参数,例如完成处理程序或 handle。异步操作处理程序内部存储这些参数供后面使用。
- 初始化器在异步操作处理器上调用操作后,操作和初始化器分别独立运行。具体来说,初始化器能调用新的异步操作,和先前的异步操作并行执行。如果异步操作打算接收来自远端的服务请求,异步操作处理器推迟该操作直到该请求到达。当事件期望的请求到达发生时,异步操作将完成执行。
- 当异步操作完成执行时,异步操作处理程序生成一个完成事件。该完成事件包含异步操作的结果。异步操作处理器随后将该完成事件插入到关联调用异步操作的 handle 的完成事件队列中。
- 当应用准备好处理异步操作产生的完成事件时,它调用前摄器事件循环的入口点方法,我们称之为
handle_events()
。该方法调用异步事件解多路复用器在完成事件队列上等待完成事件被异步操作处理器插入。在完成事件从完成事件队列中移除后,完成事件被解多路复用到该调用异步操作的 handle 所关联的完成处理程序中,然后被分发到合适的 hook 方法上。 - 具体的事件处理程序随后处理收到的完成结果。如果完成处理程序向调用方返回一个结果,有两种可能的情况。一,处理异步操作结果的完成处理程序也可能是发起异步操作的初始化器。这种情况下,完成处理程序不需要执行额外的工作来向调用方返回结果,因为它就是调用方。二,一个远程应用程序或者一个应用程序内部组件可能请求了异步操作。这种情况下,异步处理程序能在它的传输 handle 上调用一个异步写操作向远程应用程序返回结果。
- 完成处理程序完成它的处理后,它能调用其他的异步操作,这种情况下会再次开始上述循环。
Implementation
- 定义转换异步操作结果的类型。
下列 C++ 类转换异步 Win32 操作的结果到具体的完成处理程序:
class AsyncResult : public OVERLAPPED {
public:
// The Win32 OVERLAPPED struct stores the file offset
// returned when an asynchronous operation completes.
// Dispatch to completion handler hook method.
virtual void complete() = 0;
// Set/get number of bytes transferred by an asynchronous operation.
void bytes_transferred(u_long);
u_long bytes_transferred() const;
// Set/get error value if the asynchronous operation failed or was
// canceled by the initiator
void error(u_long);
u_long error() const;
private:
// ... data member omitted for brevity ...
}
- 下列 C++ 抽象基类表示单方法分发接口策略。
typedef unsigned int EventType;
enum {
// Types of indication evnets.
READ_EVENT = 01,
ACCEPT_EVENT = 01, // An "alias" for READ_EVENT.
WRITE_EVENT = 02,
TIMEOUT_EVNET = 04,
SIGNAL_EVENT = 010,
CLOSE_EVENT = 020
// These values are powers of two so their bits can be "or'd"
// together effeciently.
};
class CompletionHandler {
public:
// Cache the <proactor> so that hook methods can
// invoke asynchronous operations on <proactor>.
CompletionHandler(Proactor* proactor)
: proactor_(proactor) {}
// Virtual destruction
virtual ~CompletionHandler();
// Hook method dispatched by cached <proactor_> to
// handle completion evnets of a particular type that
// occur on the <handle>. <AsyncResult> reports the
// results of the completed asynchronous operation.
virtual void handle_event(HANDLE handle, EventType et,
const AsyncResult& result) = 0;
// Returns undeylying I/O <HANDLE>.
virtual HANDLE get_handle() const = 0;
private:
// Cached <Proactor>
Proactor* proactor_;
};
下述 C++ 抽象基类表示一个多方法接口。
class CompletionHandler {
public:
CompletionHandler(Proactor* proactor) : proactor_(proactor) {}
virtual ~CompletionHandler();
virtual void handle_read(HANDLE handle, const AsyncResult& result) = 0;
virtual void handle_accpet(HANDLE handle, const AsyncResult& result) = 0;
virtual void handle_write(HANDLE handle, const AsyncResult& result) = 0;
virtual void handle_connect(HANDLE handle, const AsyncResult& result) = 0;
virtual void handle_timeout(HANDLE handle, const AsyncResult& result) = 0;
virtual void handle_signal(HANDLE handle, const AsyncResult& result) = 0;
virtual void handle_close(HANDLE handle, const AsyncResult& result) = 0;
virtual HANDLE get_handle() const = 0;
};
- 调用异步操作的类 (Asynchronous completion token (ACT))。
class AsyncStream {
public:
AsyncStream();
void open(CompletionHandler* handler, HANDLE handle, Proactor* proactor);
void async_read(void *buf, u_long n_bytes);
void async_write(const void* buf, u_long n_bytes);
private:
CompletionHandler* completion_handler_;
HANDLE handle_;
Proactor *proactor_;
};
void AsyncStream::open(CompletionHandler* handler,
HANDLE handle, Proactor* proactor) {
completion_handler_ = handler;
handle_ = handle;
proactor_ = proactor;
proactor->register_handle(handle);
}
void AsyncStream::read(void* buf, u_long n_bytes) {
u_long bytes_read;
OVERLAPPED* act = new AsyncStreamReadResult(completion_handler_);
ReadFile(handle_, buf, n_bytes, &bytes_read, act);
}
AsyncStreamReadResult 类:
class AsyncStreamReadResult : public AsyncResult {
public:
AsyncStreamReadResult(CompletionHandler* completion_handler)
: completion_handler_(completion_handler) {}
virtual void complete();
private:
CompletionHandler* completion_handler_;
};
void AsyncStreamReadResult::complete() {
completion_handler_->handle_event(
completion_handler_->get_handle(), READ_EVENT, *this);
}
- Proactor 接口。
class Proactor {
public:
// Associate <handle> with the <Proactor>'s
// completion event queue.
void register_handle(HANDLE handle);
// Entry point into the proactive event loop. The
// <timeout> can bound time waiting for events.
void handle_events(TimeValue* wait_time = 0);
// Define a singleton access point.
static Proactor *instance ();
private:
// Use the Bridge pattern to hold a pointer to
// the <Proactor_Implementation>.
Proactor_Implementation *proactor_impl_;
};
- 具体的 proactor 实现。
class Win32ProactorImplementation : public Proactor_Implementation {
public:
Win32ProactorImplementation() {
completion_port_ = CreateIoCompletionPort(INVALID_HANDLE, 0, 0, 0);
}
void register_handle(HANDLE h) {
// associates a HANDLE with the completion port
CreateIoCompletionPort(h, completion_port_, 0, 0);
}
void handle_events(TimeValue* wait_time = 0) {
u_long num_bytes;
OVERLAPPED *act;
// call asynchronous event demultiplexing function
// to dequeue the next completion event from
// completion port
BOOL status = GetQueuedCompletionStatus(
completion_port_, &num_bytes, 0,
&act, wait_time == 0 ? 0 : wait_time->msec());
AsyncResult* async_result = static_cast<AsyncResult*>(act);
async_result->status(status);
if(!status) {
async_result->error(GetLastError());
}
else {
async_result->bytes_transferred(num_bytes);
}
async_result->complete();
delete async_result;
}
private:
// Store a HANDLE to Windows NT completion port;
HANDLE completion_port_;
};
Example
main() 函数
// HTTP server port number.
const u_short PORT = 80;
int main() {
// HTTP server address.
INETAddr addr(PORT)
// Initialize HTTP server endpoint, which associates
// the <HTTP_Acceptor>'s passive-mode socket handle
// with the <Proactor> singleton's completion port.
HTTPAcceptor acceptor(addr, Proactor::instance());
// Invoke an asynchronous <accept> operation to
// Invoke the Web server processing.
acceptor.accept();
// Event loop processes client connection requests
// and HTTP requests proactively.
for(;;) {
Proactor::instance()->handle_events();
}
/* NOTREACHED */
}
HTTPHandler 类
class HTTPHandler : public CompletionHandler {
public:
HTTPHandler(Proactor* proactor)
: proactor_(proactor) {}
virtual void open(SOCKStream* sock) {
// Initialize state for request.
request_.state_ = INCOMPLETE;
// Store pointer to the socket.
sock_ = sock;
// Initialize <Async_Stream>.
stream_.open(this, sock_->handle(), proactor_);
// Start asynchronous read operation on socket.
stream_.async_read(request_.buffer(), request_.buffer_size());
}
virtual void handle_event(HANDLE handle,
EventType et, const AsyncResult& async_result) {
if(event_type == READ_EVENT) {
if(!request_.done(async_result.bytes_transferred()))
// Didn't get entire request, so start a
// new asynchronous read operation.
stream_.async_read(request_buffer(), request_buffer_size());
else
parse_request ();
}
else if(event_type == WRITE_EVENT) {
if(!file_.done(async_result.bytes_transferred()))
// Didn't send entire data, so start
// another asynchronous write.
stream_.async_write(file_.buffer(), file_buffer_size());
else
// Success, so free up resources...
}
}
void parse_request() {
// Switch on the HTTP command type.
switch(request_.command()) {
// Web browser is requesting a file.
case HTTPRequst::GET:
// Memory map the requested file.
file_.map(request_.filename());
// Invoke asynchronous write operation.
stream_.async_write(file.buffer(), file_.buffer_size());
break;
// Web browser is storing file at the Web server.
case HTTPRequest::PUT:
// ...
}
}
private:
// Cached <Proactor>
Proactor* proactor_;
// Memory-mapped file
MemMap file_;
// Socket endpoint, initialized into "async-mode."
SockStream* sock_;
// Hold the HTTP Request while its being processed.
HTTPRequest request_;
// Read/write asynchronous socket I/O.
AsyncStream stream_;
};