[Turn] High-performance I/O design patterns Reactor and Proactor

[Transfer] http://blog.csdn.net/roger_77/article/details/1555170
NETTY thread model, you can deeply understand http://blog.csdn.net/xiaolang85/article/details/37873059

True and false asynchronous Purchased

yesterday I read the 2007.4 issue of "Programmer" magazine and read it for the first time. One of the articles "Comparison of Two High-Performance I/O Design Patterns" is eye-catching. This is a translation. I have been thinking about it recently. It's been a long time since I read this article carefully.

The article mainly mentioned that the system I/O mode can be divided into three categories: blocking, non-blocking synchronous and non-blocking asynchronous. Among the three methods, the non-blocking asynchronous mode has the best scalability and performance. It mainly talks about two IO multiplexing modes: Reactor and Proactor, and compares them.

The article also introduces the choice of building a common, unified external interface for Reactor and Proactor patterns and a fully portable development framework: TProactor (ACE compatible Proactor): http://www.terabit.com.au/solutions .php. Because Linux does not support aio completely, the ACE_Proactor framework performs poorly on Linux. Most of the normal code is executed on Windows, but it runs abnormally on Linux, and even cannot be compiled. This problem has been plaguing most ACE users, and now there is a TProactor that helps to solve the problem of using (at least looking normal) ACE_Proactor under the condition that Linux does not fully support AIO.

The main summary of the article:

---------->

Two I/O multiplexing modes: Reactor and Proactor

Generally, the I/O multiplexing mechanism relies on an event demultiplexer (Event Demultiplexer). The separator object can separate the I/O events from the event source and distribute them to the corresponding read/write event handler (Event Handler). The developer pre-registers the event to be processed and its event handler (or callback function); the event separator is responsible for passing the request event to the event handler. Two patterns related to event separators are Reactor and Proactor. Reactor mode uses synchronous IO, while Proactor uses asynchronous IO.
In Reactor, the event separator is responsible for waiting for the file descriptor or socket to be ready for read and write operations, and then passes the ready event to the corresponding processor, and finally the processor is responsible for completing the actual read and write work.
In the Proactor mode, the processor -- or the event separator that also serves as the processor, is only responsible for initiating asynchronous read and write operations. The IO operation itself is done by the operating system. The parameters passed to the operating system need to include the user-defined data buffer address and data size, so that the operating system can obtain the data required for the write operation, or write the data read from the socket. The event separator captures the IO operation completion event, and then passes the event to the corresponding handler. For example, on windows, the processor initiates an asynchronous IO operation, and the event separator waits for the IOCompletion event. Typical implementations of asynchronous patterns are built on the basis that the operating system supports asynchronous APIs. We refer to such implementations as "system-level" asynchronous or "true" asynchronous, because the application completely relies on the operating system to perform the real IO work.
For example, it will help to understand the difference between Reactor and Proactor, taking read operation as an example (class operation is similar).
Implement read in Reactor:
- Register read ready event and corresponding event handler
- Event separator waits for event
- Event arrives, activate separator, separator calls event corresponding handler.
- The event handler completes the actual read operation, processes the read data, registers a new event, and returns control.
Compare with the read process in Proactor (true asynchronous) as follows:
- The processor initiates an asynchronous read operation (note: the operating system must support asynchronous IO). In this case, the processor ignores the IO ready event, it focuses on the completion event.
- Event separator waiting for operation completion event
- During the separator waiting process, the operating system uses parallel kernel threads to perform the actual read operation, stores the result data in the user-defined buffer, and finally informs the event separator that the read operation is completed.
- The event separator calls the handler.
- The event handler processes the data in the user-defined buffer, then starts a new asynchronous operation and returns control to the event separator.

Status of Practice The open source C++ development framework ACE developed
by Douglas Schmidt et al. provides a large number of platform-independent low-level classes (threads, mutexes, etc.) that support concurrency, and provides two different classes at a high level of abstraction -- Implementation of ACE Reactor and ACE Proactor. However, while both are platform-independent, the interfaces provided are different.
ACE Proactor has better performance on the windows platform, because windows provides efficient asynchronous API support in the operating system (see http://msdn2.microsoft.com/en-us/library/aa365198.aspx).
However, not all operating systems strongly support asynchrony at the system level. Like many Unix systems do not. So on Unix it might be better to go with the ACE Reactor solution. But then, to get the best performance, developers of web applications must maintain multiple copies of code for different operating systems: Windows is based on ACE Proactor, while Unix systems use the ACE Reactor solution.

Improvements
  In this section, we will try to address the challenges of building a portable framework for the Proactor and Reactor patterns. In the improvement plan, we move Reactor's read/write operations that were originally located in the event handler to the separator (we might call this idea "simulated asynchrony"), in order to seek to convert Reactor's multi-channel synchronous IO into simulated asynchronous IO . Taking the read operation as an example, the improvement process is as follows:
  - Register the read ready event and its handler, and provide the separator with the address of the data buffer, the amount of data to be read, and other information.
  - The splitter waits for an event (eg on select())
  - When the event arrives, the splitter is activated. The splitter performs a non-blocking read operation (it has all the information it needs to complete the operation) and finally calls the corresponding handler.
  - The event handler processes the data in the user-defined buffer, registers a new event (of course, it also gives the address of the data buffer, the amount of data to be read, etc.), and finally returns the control right to the separator.
  As we can see, Reactor can be transformed into Proactor mode by modifying the functional structure of the multi-channel IO mode. Before and after the transformation, the actual workload of the model did not increase, but the responsibilities of the participants were slightly exchanged. There is no change in workload, and naturally there will be no performance degradation. The constant workload can be proved by comparing the following steps:
  Standard/Typical Reactor:
  - Step 1: Wait for the event to arrive (Reactor is responsible)
  - Step 2: Distribute read ready event to user-defined handler (Reactor is responsible)
  - Step 3: Read data (User processor is responsible)
  - Step 4: Process data (User processor is responsible)
  Simulation Proactor for improved implementation:
  - Step 1: Wait for the event to come (Proactor is responsible)
  - Step 2: Get read ready event, execute read data (now responsible for Proactor)
  - Step 3: Distribute read complete event to user handler (Proactor is responsible)
  - Step 4: Process Data (responsible for the user processor) 
 
  For operating systems that do not provide asynchronous IO APIs, this method can hide the interaction details of the socket API, thereby exposing a complete asynchronous interface to the outside world. With this, we can further build fully portable, platform-independent solutions with a common external interface.
  
The above scheme has been implemented as TProactor by Terabit P/L Company (http://www.terabit.com.au/). It has two versions: C++ and JAVA. The C++ version is developed using the ACE cross-platform bottom class, which provides a common and unified active asynchronous interface for all platforms.
  The Boost.Asio library also adopts a similar scheme to implement a unified IO asynchronous interface.

<------------

Recently, the Boost.Asio class library has been used in the project, which is implemented with the design pattern of Proactor, see: Proactor (The Boost.Asio library is based on the Proactor pattern. This design note outlines the advantages and disadvantages of this approach .), its design documentation link: http://asio.sourceforge.net/boost_asio_0_3_7/libs/asio/doc/design/index.html

First, let us examine how the Proactor design pattern is implemented in asio, without reference to platform -specific details.

Boost.Asio's Proactor pattern implementation diagram

Proactor design pattern (adapted from [1])

Of course, these two I/O design patterns are also widely used in ACE, which are introduced in ACE related books , which has a lot of good introductory articles on the ACE Developers site.



Such as: ACE Technical Papers - Chapter 8 Proactor: Object Behavior Patterns for Demultiplexing and Dispatching Processors for Asynchronous Events

ACE Technical Papers - Chapter 7 Design and Use of ACE Reactors : Object-Oriented Architecture for Event Demultiplexing

ACE Programmer Tutorial - Chapter 6 Reactor: Architectural Patterns for Event Demultiplexing and Dispatching

ACE Applications - Chapter 2 JAWS: High-Performance Web Server Architecture



Proactor mode has unparalleled advantages in single-CPU and single-core system applications. The question now is: in a multi-CPU and multi-core system, how can it better utilize the advantages of multi-threading? ? ? This is worth thinking and practicing, and may produce another design pattern to meet the needs of development.

Guess you like

Origin http://10.200.1.11:23101/article/api/json?id=326908090&siteId=291194637