Understanding the Reactor Pattern: Thread-Based and Event-Driven
I’m Arsalan Mirbozorgi and with an explanation of how and why the reactor pattern works in practice.
Both thread-based and event-driven web architectures compete to manage user requests.
You can construct a multithreaded server using the thread per connection approach. Sites that need to be compatible with non-thread-safe libraries can use it.
A problem with one request won’t affect the other requests because it uses the best multi-processing modules to isolate each one.
It takes a long time to switch contexts and consumes a lot of RAM. Therefore, it is necessary to employ the “thread per connection” strategy, even though programming with threads is error-prone and difficult to debug.
Typically, a single dispatcher thread is placed in front of a blocking queue and a thread pool in order to control the number of threads for optimal overall performance and eliminate thread-creating/destroying overhead. It waits for new connections on the socket before passing them on to the bounded blocking queue. Latencies for accepted connections are now predictable. However, connections exceeding the queue’s capacity will be discarded. Incoming requests are polled by a group of threads, which subsequently process and react to them.
We always have a one-to-one relation between threads and connections, unfortunately. Several worker threads are waiting for the file system or network access when using long-lived connections like Keep-Alive ones. Stack space in memory can be wasted by hundreds or even thousands of concurrent threads.
Threads can be separated from connections by using an event-driven method, which only uses threads for specified callbacks or handlers when events occur.
Creators and consumers play important roles in an event-based architecture. Only the event’s source, the originator, is aware of the event’s occurrence. When an event has taken place, consumers need to know about it. They may be involved in the event’s processing or merely be affected by it.
The Reactor Pattern
Event-driven architecture can be implemented using the reactor pattern. An event loop that blocks resource-emitting events and sends them to appropriate handlers and callback handlers is used.
As long as handlers and callbacks for events are registered, there is no need to stall on I/O. The term “event” describes things like a newly established connection that is ready to be read or written to. There may be a thread pool for these handlers/callbacks in multi-core settings.
This design style separates the reusable reactor implementation from the modular application code.
When it comes to the Reactor Pattern‘s design, two key players stand out:
1. The Reactor
IO events are handled by a Reactor, which runs in a separate thread and sends tasks to the appropriate handler when they occur. You might think of it as an answering service for businesses, where a receptionist takes calls from customers and directs them to the proper person.
A Handler is like the company official the client wants to speak to when it comes to doing the real work that needs to be done with an I/O event.
A reactor dispatches the appropriate handler to respond to I/O events. Non-blocking activities are carried out through handlers.
The Reactor Pattern’s Purpose
Event-driven applications can demultiplex and dispatch service requests that are received from one or more customers using the Reactor architectural pattern
When an event is triggered, a reactor will notify the relevant event handler to respond appropriately.
Using the Reactor Pattern, you can demultiplex and order events in real-time using synchronous demultiplexing.
Event handlers are used to sequentially process posts coming in from numerous clients simultaneously, including messages, requests, and connections. This technique is meant to avoid the difficulty of starting a new thread for every message, request, or connection you receive. As soon as an event is received, it is distributed sequentially to each associated event handler.
To summarize, servers must manage more than 10,000 concurrent clients, and threads in Tomcat, Glassfish, JBoss, or HttpClient cannot scale connections.
Because of this, the reactor-using program just needs to process many events in a single thread.
In essence, the standard Reactor allows a single-threaded lead program to handle many events at once.
If you have an input and want several outputs, you need a demultiplexer. When you need to convey a signal to several different devices, this is the circuit to utilize.
This sounds a lot like a decoder, but instead of selecting one device from among many, a demultiplexer distributes a signal among several.
A Reactor allows a single thread to process several jobs that are currently blocked efficiently. In addition, the Reactor keeps track of a collection of event handlers. Once invoked, it establishes a connection with an existing handler and sets it to active status.
How Things Happen?
- This can be delegated to a dispatcher implementation, which will look for all active and unlocked handlers on the system.
- Sequential execution of these handlers is required until all of them are completed or until they are blocked. The event cycle can continue after completed Handlers have been deactivated.
- Step One is repeated (1)
Why is this important?
The Reactor pattern is used by a number of different technologies, including Node.js, Vert.x, Reactive Extensions, Jetty, and Ngnix. As a result, if you’re interested in learning more about how things work behind the scenes, pay close attention to this pattern.