Reactor pattern
The reactor software design pattern is an event handling pattern for concurrently handling service requests without limiting throughput. The pattern's key component is an event loop, running in a single thread or process. This loop demultiplexes incoming requests and dispatches them to corresponding handlers running outside the event loop's thread / process.
This permits handling many concurrent requests without blocking while retaining a simple, reliable mechanism for dispatching the requests. It also allows for adding or removing distinct request handlers in a modular way, though it can still have some drawbacks and limitations.[1]
Because of its balance between simplicity and scalabity, the reactor pattern has become a central architectural element of several server applications and software frameworks for networking. Derivations such as the multireactor and proactor also exist for special cases where even greater throughput or additional isolation between threads is necessary.[1][2][3]
Overview
Practical considerations for large networks using a client–server model, such as the C10k problem for web servers,[4] were the original motivation for the reactor pattern.
One naive approach to handling a service request is to dispatch directly to a handler from within a single-threaded event loop. Because the event loop must complete processing one request before iterating, the first request will block the second, preventing concurrency. However, a "thread per connection" approach, where a new event loop is spawned for each new request or connection can handle simultaneous requests up to a point.[1]
The "thread per connection" strategy fails to scale indefinitely though because spinning up and tearing down threads typically imposes significant overhead on a program. Another inefficiency is that a given connection's thread will likely spend much of its time blocked and waiting for additional input/output (I/O). From a design standpoint, calling request handlers directly from within the event loop tightly couples the general dispatch mechanism with the logical specifics of different handlers.[1]
This suggests that a better separation of concerns is to keep event dispatch in a separate thread from individual request handlers. By detecting and assigning a request immediately to a separate thread, the server can achieve non-blocking I/O and demultiplex many incoming requests. Because the detection and allocation of events is typically orders quicker than I/O and request processing though, using a single demultiplexer thread minimizes overhead and simplifies the design. By allowing different request handlers to register as callbacks with the dispatcher, the server also becomes much easier to modify.[1]
The resulting reactor design pattern is very scalable and flexible, while still avoiding much of the complexity of a fully asynchronous design. One of the main drawbacks of the reactor pattern is that the use of callbacks can make program analysis and debugging more difficult, a problem common to designs with inverted control.[1]
Usage
Applications
Structure
- Resources
- Any resource that can provide input to or consume output from the system.
- Synchronous Event Demultiplexer
- Uses an event loop to block on all resources. The demultiplexer sends the resource to the dispatcher when it is possible to start a synchronous operation on a resource without blocking (Example: a synchronous call to
read()
will block if there is no data to read. The demultiplexer usesselect()
on the resource, which blocks until the resource is available for reading. In this case, a synchronous call toread()
won't block, and the demultiplexer can send the resource to the dispatcher.) - Dispatcher
- Handles registering and unregistering of request handlers. Dispatches resources from the demultiplexer to the associated request handler.
- Request Handler
- An application defined request handler and its associated resource.
UML diagram
Variants
See also
- Proactor pattern, which allows mixing synchronous & asynchronous dispatching
- C10k problem
- Input/output
- Application server
- Web server
References
- ^ a b c d e f Schmidt, Douglas C. (1995). "Chapter 29: Reactor: An Object Behavioral Pattern for Demultiplexing and Dispatching Handles for Synchronous Events" (pdf). In Coplien, James O. (ed.). Pattern Languages of Program Design. Vol. 1 (1st ed.). Addison-Wesley. ISBN 9780201607345.
- ^ Escoffier, Clement; Finnegan, Ken (November 2021). "Chapter 4. Design Principles of Reactive Systems". Reactive Systems in Java. O'Reilly Media. ISBN 9781492091721.
- ^ Garrett, Owen (10 June 2015). "Inside NGINX: How We Designed for Performance & Scale". NGINX. F5, Inc. Archived from the original on 20 August 2023. Retrieved 10 September 2023.
- ^ Kegel, Dan (5 February 2014). "The C10k problem". Dan Kegel's Web Hostel. Archived from the original on 6 September 2023. Retrieved 10 September 2023.
External links
Specific applications:
- Alexeev, Andrew (30 March 2012). "Chapter 14: nginx". In Brown, Amy; Wilson, Greg (eds.). The Architecture of Open Source Applications. Vol. 2. ISBN 9781105571817.
Sample implementations: