Skip to content

Commit a766f18

Browse files
authored
docs: explanation for reactor (iluwatar#2960)
1 parent 1da0f41 commit a766f18

File tree

5 files changed

+107
-71
lines changed

5 files changed

+107
-71
lines changed

page-controller/README.md

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,6 @@ tags:
1313
- Web development
1414
---
1515

16-
## Also known as
17-
18-
* Dispatcher
19-
2016
## Intent
2117

2218
The Page Controller pattern is intended to handle requests for a specific page or action within a web application, processing input, and determining the appropriate view for rendering the response.

reactor/README.md

Lines changed: 98 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -3,26 +3,114 @@ title: Reactor
33
category: Concurrency
44
language: en
55
tag:
6-
- Performance
7-
- Reactive
6+
- Asynchronous
7+
- Event-driven
8+
- Fault tolerance
9+
- Messaging
10+
- Reactive
11+
- Scalability
12+
- Synchronization
13+
- Thread management
814
---
915

16+
## Also known as
17+
18+
* Dispatcher
19+
* Notifier
20+
1021
## Intent
11-
The Reactor design pattern handles service requests that are delivered concurrently to an application by one or more clients. The application can register specific handlers for processing which are called by reactor on specific events. Dispatching of event handlers is performed by an initiation dispatcher, which manages the registered event handlers. Demultiplexing of service requests is performed by a synchronous event demultiplexer.
22+
23+
Handle service requests that are delivered concurrently to a service handler by one or more inputs.
24+
25+
## Explanation
26+
27+
Real-world example
28+
29+
> Imagine a busy restaurant kitchen where multiple orders come in from different tables at the same time. Instead of each chef handling one order at a time, there is a head chef who acts as the dispatcher. The head chef receives all the orders and decides which chef will handle which part of each order, ensuring that all chefs are utilized efficiently. This way, the kitchen can handle many orders simultaneously, ensuring that dishes are prepared quickly and efficiently without any one chef becoming a bottleneck. This setup is analogous to the Reactor pattern, where the head chef dispatches tasks (events) to various chefs (event handlers) to process multiple tasks concurrently.
30+
31+
In plain words
32+
33+
> The Reactor pattern efficiently handles multiple concurrent service requests by dispatching them to appropriate event handlers using a single or a limited number of threads.
34+
35+
Wikipedia says
36+
37+
> The reactor software design pattern is an event handling strategy that can respond to many potential service requests concurrently. The pattern's key component is an event loop, running in a single thread or process, which demultiplexes incoming requests and dispatches them to the correct request handler.
38+
39+
**Programmatic Example**
40+
41+
The Reactor design pattern is a concurrency model that efficiently handles multiple simultaneous I/O operations using a single or a limited number of threads. It is particularly useful in scenarios where an application needs to handle multiple clients sending service requests concurrently.
42+
43+
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:
44+
45+
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.
46+
47+
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.
48+
49+
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.
50+
51+
Here is a simplified example of how these components interact:
52+
53+
```java
54+
// Create a dispatcher
55+
Dispatcher dispatcher = new ThreadPoolDispatcher(2);
56+
57+
// Create a reactor with the dispatcher
58+
NioReactor reactor = new NioReactor(dispatcher);
59+
60+
// Create a handler for handling events
61+
ChannelHandler loggingHandler = new LoggingHandler();
62+
63+
// Register channels with the reactor
64+
reactor.registerChannel(new NioServerSocketChannel(16666, loggingHandler));
65+
reactor.registerChannel(new NioDatagramChannel(16668, loggingHandler));
66+
67+
// Start the reactor
68+
reactor.start();
69+
```
70+
71+
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.
72+
73+
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.
1274

1375
## Class diagram
76+
1477
![Reactor](./etc/reactor.png "Reactor")
1578

1679
## Applicability
17-
Use Reactor pattern when
1880

19-
* A server application needs to handle concurrent service requests from multiple clients.
20-
* A server application needs to be available for receiving requests from new clients even when handling older client requests.
21-
* A server must maximize throughput, minimize latency and use CPU efficiently without blocking.
81+
* Use the Reactor pattern when you need to handle multiple simultaneous I/O operations efficiently.
82+
* Ideal for applications requiring high scalability and low-latency, such as web servers and networking frameworks.
83+
84+
## Known Uses
85+
86+
* Netty: An asynchronous event-driven network application framework for rapid development of maintainable high-performance protocol servers and clients.
87+
* Akka: A toolkit and runtime for building concurrent, distributed, and fault-tolerant applications on the JVM.
88+
* Java NIO (New I/O): Provides non-blocking I/O operations, allowing a single thread to manage multiple channels.
89+
90+
## Consequences
91+
92+
Benefits:
93+
94+
* Improves application performance by efficiently handling multiple simultaneous connections.
95+
* Reduces resource consumption by using a small number of threads to handle many I/O operations.
96+
* Enhances scalability by allowing applications to serve many clients with minimal threads.
97+
98+
Trade-offs:
99+
100+
* Increased complexity in managing state and event handling.
101+
* Debugging and maintaining asynchronous code can be challenging.
102+
* Potential difficulty in ensuring thread safety and avoiding race conditions.
103+
104+
## Related Patterns
105+
106+
* [Observer](https://java-design-patterns.com/patterns/observer/): Reactor uses the Observer pattern for handling events where event handlers are notified of changes.
107+
* Proactor: Similar to Reactor but handles asynchronous I/O completion rather than readiness.
108+
* [Command](https://java-design-patterns.com/patterns/command/): Encapsulates a request as an object, allowing parameterization and queuing of requests.
22109

23110
## Credits
24111

25112
* [Douglas C. Schmidt - Reactor](https://www.dre.vanderbilt.edu/~schmidt/PDF/Reactor.pdf)
26-
* [Pattern Oriented Software Architecture Volume 2: Patterns for Concurrent and Networked Objects](https://www.amazon.com/gp/product/0471606952/ref=as_li_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=0471606952&linkCode=as2&tag=javadesignpat-20&linkId=889e4af72dca8261129bf14935e0f8dc)
27-
* [Doug Lea - Scalable IO in Java](http://gee.cs.oswego.edu/dl/cpjslides/nio.pdf)
28-
* [Netty](http://netty.io/)
113+
* [Java Concurrency in Practice](https://amzn.to/4aRMruW)
114+
* [Pattern-Oriented Software Architecture Volume 2: Patterns for Concurrent and Networked Objects](https://amzn.to/3UgC24V)
115+
* [Reactive Programming with RxJava: Creating Asynchronous, Event-Based Applications](https://amzn.to/4dNTLJC)
116+
* [Scalable IO in Java - Doug Lea](http://gee.cs.oswego.edu/dl/cpjslides/nio.pdf)

reactor/src/main/java/com/iluwatar/reactor/app/LoggingHandler.java

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -52,8 +52,7 @@ public void handleChannelRead(AbstractNioChannel channel, Object readObject, Sel
5252
if (readObject instanceof ByteBuffer) {
5353
doLogging((ByteBuffer) readObject);
5454
sendReply(channel, key);
55-
} else if (readObject instanceof DatagramPacket) {
56-
var datagram = (DatagramPacket) readObject;
55+
} else if (readObject instanceof DatagramPacket datagram) {
5756
doLogging(datagram.getData());
5857
sendReply(channel, datagram, key);
5958
} else {

reactor/src/main/java/com/iluwatar/reactor/framework/AbstractNioChannel.java

Lines changed: 2 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
import java.util.Queue;
3333
import java.util.concurrent.ConcurrentHashMap;
3434
import java.util.concurrent.ConcurrentLinkedQueue;
35+
import lombok.Getter;
3536

3637
/**
3738
* This represents the <i>Handle</i> of Reactor pattern. These are resources managed by OS which can
@@ -46,6 +47,7 @@
4647
public abstract class AbstractNioChannel {
4748

4849
private final SelectableChannel channel;
50+
@Getter
4951
private final ChannelHandler handler;
5052
private final Map<SelectableChannel, Queue<Object>> channelToPendingWrites;
5153
private NioReactor reactor;
@@ -104,15 +106,6 @@ public SelectableChannel getJavaChannel() {
104106
*/
105107
public abstract Object read(SelectionKey key) throws IOException;
106108

107-
/**
108-
* Get handler.
109-
*
110-
* @return the handler associated with this channel.
111-
*/
112-
public ChannelHandler getHandler() {
113-
return handler;
114-
}
115-
116109
/*
117110
* Called from the context of reactor thread when the key becomes writable. The channel writes the
118111
* whole pending block of data at once.

reactor/src/main/java/com/iluwatar/reactor/framework/NioDatagramChannel.java

Lines changed: 6 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,8 @@
3131
import java.nio.ByteBuffer;
3232
import java.nio.channels.DatagramChannel;
3333
import java.nio.channels.SelectionKey;
34+
import lombok.Getter;
35+
import lombok.Setter;
3436
import lombok.extern.slf4j.Slf4j;
3537

3638
/**
@@ -131,9 +133,12 @@ public void write(Object data, SelectionKey key) {
131133
/**
132134
* Container of data used for {@link NioDatagramChannel} to communicate with remote peer.
133135
*/
136+
@Getter
134137
public static class DatagramPacket {
135-
private SocketAddress sender;
136138
private final ByteBuffer data;
139+
@Setter
140+
private SocketAddress sender;
141+
@Setter
137142
private SocketAddress receiver;
138143

139144
/**
@@ -144,50 +149,5 @@ public static class DatagramPacket {
144149
public DatagramPacket(ByteBuffer data) {
145150
this.data = data;
146151
}
147-
148-
/**
149-
* Get sender address.
150-
*
151-
* @return the sender address.
152-
*/
153-
public SocketAddress getSender() {
154-
return sender;
155-
}
156-
157-
/**
158-
* Sets the sender address of this packet.
159-
*
160-
* @param sender the sender address.
161-
*/
162-
public void setSender(SocketAddress sender) {
163-
this.sender = sender;
164-
}
165-
166-
/**
167-
* Get receiver address.
168-
*
169-
* @return the receiver address.
170-
*/
171-
public SocketAddress getReceiver() {
172-
return receiver;
173-
}
174-
175-
/**
176-
* Sets the intended receiver address. This must be set when writing to the channel.
177-
*
178-
* @param receiver the receiver address.
179-
*/
180-
public void setReceiver(SocketAddress receiver) {
181-
this.receiver = receiver;
182-
}
183-
184-
/**
185-
* Get data.
186-
*
187-
* @return the underlying message that will be written on channel.
188-
*/
189-
public ByteBuffer getData() {
190-
return data;
191-
}
192152
}
193153
}

0 commit comments

Comments
 (0)