mirror of
https://github.com/tiennm99/java-design-patterns.git
synced 2026-05-15 16:58:56 +00:00
docs: update reactor
This commit is contained in:
+91
-8
@@ -42,35 +42,118 @@ The Reactor design pattern is a concurrency model that efficiently handles multi
|
||||
|
||||
In the given code, the Reactor pattern is implemented using Java's NIO (Non-blocking I/O) framework. The key components of this pattern in the code are:
|
||||
|
||||
1. `NioReactor`: This class acts as the Synchronous Event De-multiplexer and Initiation Dispatcher. It waits for events on multiple channels registered to it in an event loop and dispatches them to the appropriate handlers.
|
||||
1. **Reactor**: This is the event loop that demultiplexes the incoming requests and dispatches them to the appropriate handlers. In our example, `NioReactor` is the reactor.
|
||||
|
||||
2. `AbstractNioChannel`: This class acts as a Handle that is registered to the reactor. When any events occur on a handle, the reactor calls the appropriate handler.
|
||||
2. **Dispatcher**: This is responsible for managing the execution of the tasks that are triggered by the events. In our example, `Dispatcher` is the dispatcher and `ThreadPoolDispatcher` is a concrete implementation of it.
|
||||
|
||||
3. `ChannelHandler`: This class acts as an Event Handler, which is bound to a channel and is called back when any event occurs on any of its associated handles. Application logic resides in event handlers.
|
||||
3. **Handles**: These are resources that are managed by the reactor. They are associated with specific events and are used by the reactor to identify the event handlers to which the events should be dispatched. In our example, `AbstractNioChannel` represents a handle.
|
||||
|
||||
Here is a simplified example of how these components interact:
|
||||
4. **Event Handlers**: These are associated with specific handles and are responsible for handling the events that occur on those handles. In our example, `ChannelHandler` is an event handler and `LoggingHandler` is a concrete implementation of it.
|
||||
|
||||
5. **Synchronous Event Demultiplexer**: This is a system-level component (not shown in the code) that provides a blocking call that waits for events to occur on any of the handles. In our example, this is part of the Java NIO framework.
|
||||
|
||||
6. **Concrete Event Handlers**: These are application-specific implementations of the event handlers. In our example, `LoggingHandler` is a concrete event handler.
|
||||
|
||||
7. **Initiation Dispatcher**: This is a component that initializes the association between event handlers and handles. In our example, this is done by the `registerChannel` method in the `NioReactor` class.
|
||||
|
||||
**Part 1: Creating the Dispatcher**
|
||||
|
||||
The first part of our example involves creating a dispatcher. The dispatcher is responsible for managing the execution of the tasks that are triggered by the events.
|
||||
|
||||
```java
|
||||
// Create a dispatcher
|
||||
Dispatcher dispatcher = new ThreadPoolDispatcher(2);
|
||||
```
|
||||
|
||||
In this snippet, we're creating a `ThreadPoolDispatcher` with 2 threads. This dispatcher will use a thread pool to execute the tasks.
|
||||
|
||||
**Part 2: Creating the Reactor**
|
||||
|
||||
Next, we create a reactor with the dispatcher. The reactor is the core component of the Reactor pattern. It waits for events on multiple channels registered to it in an event loop and dispatches them to the appropriate handlers.
|
||||
|
||||
```java
|
||||
// Create a reactor with the dispatcher
|
||||
NioReactor reactor = new NioReactor(dispatcher);
|
||||
```
|
||||
|
||||
Here, we're creating a `NioReactor` and passing the dispatcher we created earlier to its constructor.
|
||||
|
||||
**Part 3: Creating the Handler**
|
||||
|
||||
Now, we create a handler for handling events. The handler is responsible for processing the events that occur on the channels.
|
||||
|
||||
```java
|
||||
// Create a handler for handling events
|
||||
ChannelHandler loggingHandler = new LoggingHandler();
|
||||
```
|
||||
|
||||
In this snippet, we're creating a `LoggingHandler`. This handler will log the events that occur on the channels.
|
||||
|
||||
**Part 4: Registering Channels with the Reactor**
|
||||
|
||||
Next, we register channels with the reactor. These channels are the sources of the events that the reactor will handle.
|
||||
|
||||
```java
|
||||
// Register channels with the reactor
|
||||
reactor.registerChannel(new NioServerSocketChannel(16666, loggingHandler));
|
||||
reactor.registerChannel(new NioDatagramChannel(16668, loggingHandler));
|
||||
```
|
||||
|
||||
Here, we're registering a `NioServerSocketChannel` and a `NioDatagramChannel` with the reactor. These channels are associated with the `LoggingHandler` we created earlier.
|
||||
|
||||
**Part 5: Starting the Reactor**
|
||||
|
||||
Finally, we start the reactor. Once started, the reactor begins to listen for events on the registered channels.
|
||||
|
||||
```java
|
||||
// Start the reactor
|
||||
reactor.start();
|
||||
```
|
||||
|
||||
In this example, the `NioReactor` is created with a `ThreadPoolDispatcher` which uses 2 threads for dispatching events. Two channels, a `NioServerSocketChannel` and a `NioDatagramChannel`, are registered with the reactor. These channels are associated with a `LoggingHandler` which handles the events that occur on these channels. Finally, the reactor is started, and it begins to listen for events on the registered channels.
|
||||
In this snippet, we're starting the reactor. From this point on, the reactor will start handling events from the registered channels.
|
||||
|
||||
When an event occurs on a channel, the reactor's event loop detects it and dispatches the event to the `LoggingHandler` associated with that channel. The `LoggingHandler` then processes the event.
|
||||
**Part 6: Creating the App Class**
|
||||
|
||||
The `App` class is the entry point of our application. It creates the reactor, registers the channels, and starts the reactor.
|
||||
|
||||
```java
|
||||
public class App {
|
||||
|
||||
public static void main(String[] args) {
|
||||
// Create a dispatcher
|
||||
Dispatcher dispatcher = new ThreadPoolDispatcher(2);
|
||||
|
||||
// Create a reactor with the dispatcher
|
||||
NioReactor reactor = new NioReactor(dispatcher);
|
||||
|
||||
// Create a handler for handling events
|
||||
ChannelHandler loggingHandler = new LoggingHandler();
|
||||
|
||||
// Register channels with the reactor
|
||||
reactor.registerChannel(new NioServerSocketChannel(16666, loggingHandler));
|
||||
reactor.registerChannel(new NioDatagramChannel(16668, loggingHandler));
|
||||
|
||||
// Start the reactor
|
||||
reactor.start();
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
In this snippet, we're creating an instance of the `App` class. Inside the `main` method, we're following the same steps as before: creating a dispatcher, creating a reactor with the dispatcher, creating a handler, registering channels with the reactor, and finally starting the reactor.
|
||||
|
||||
This `App` class demonstrates how an application interacts with the reactor. It sets up the necessary components (dispatcher, reactor, handler, channels) and starts the reactor. Once the reactor is started, it will handle events from the registered channels using the specified handler.
|
||||
|
||||
Running the code produces the following output:
|
||||
|
||||
```
|
||||
09:50:08.317 [main] INFO com.iluwatar.reactor.framework.NioServerSocketChannel -- Bound TCP socket at port: 16666
|
||||
09:50:08.320 [main] INFO com.iluwatar.reactor.framework.NioServerSocketChannel -- Bound TCP socket at port: 16667
|
||||
09:50:08.323 [main] INFO com.iluwatar.reactor.framework.NioDatagramChannel -- Bound UDP socket at port: 16668
|
||||
09:50:08.324 [main] INFO com.iluwatar.reactor.framework.NioDatagramChannel -- Bound UDP socket at port: 16669
|
||||
09:50:08.324 [pool-2-thread-1] INFO com.iluwatar.reactor.framework.NioReactor -- Reactor started, waiting for events...
|
||||
```
|
||||
|
||||
This concludes our detailed explanation of the Reactor design pattern. The Reactor pattern allows us to handle multiple simultaneous I/O operations efficiently using a single or a limited number of threads.
|
||||
|
||||
## Class diagram
|
||||
|
||||
@@ -106,11 +189,11 @@ Trade-offs:
|
||||
* [Observer](https://java-design-patterns.com/patterns/observer/): Reactor uses the Observer pattern for handling events where event handlers are notified of changes.
|
||||
* Proactor: Similar to Reactor but handles asynchronous I/O completion rather than readiness.
|
||||
* [Command](https://java-design-patterns.com/patterns/command/): Encapsulates a request as an object, allowing parameterization and queuing of requests.
|
||||
|
||||
|
||||
## Credits
|
||||
|
||||
* [Douglas C. Schmidt - Reactor](https://www.dre.vanderbilt.edu/~schmidt/PDF/Reactor.pdf)
|
||||
* [Java Concurrency in Practice](https://amzn.to/4aRMruW)
|
||||
* [Pattern-Oriented Software Architecture Volume 2: Patterns for Concurrent and Networked Objects](https://amzn.to/3UgC24V)
|
||||
* [Reactive Programming with RxJava: Creating Asynchronous, Event-Based Applications](https://amzn.to/4dNTLJC)
|
||||
* [Scalable IO in Java - Doug Lea](http://gee.cs.oswego.edu/dl/cpjslides/nio.pdf)
|
||||
* [Reactor - An Object Behavioral Pattern for Demultiplexing and Dispatching Handles for Synchronous Events (Douglas C. Schmidt)](https://www.dre.vanderbilt.edu/~schmidt/PDF/Reactor.pdf)
|
||||
|
||||
Reference in New Issue
Block a user