Original blog address: pjmike’s blog

preface

Recently, WHEN I was researching Java NIO, I often saw a design pattern — Reactor pattern. I haven’t seen this pattern before and it is also used in Netty. What is the Reactor pattern? Why use the Reactor schema? How is the Reactor pattern implemented in NIO? Next, Reactor’s mystery is revealed

The following introduction refers to a lot of relevant information on the Internet, so there will be some duplication of information on the Internet, please forgive me

The introduction of Reactor

Reactor is a design pattern related to IO. NIO in Java provides good support for the Reactor pattern. Scalable IO in Java is best known for Doung Lea demonstrating how to use NIO to implement the Reactor pattern

The Reactor schema is defined on Wikipedia as follows:

The reactor design pattern is an event handling pattern for handling service requests delivered concurrently to a service handler by one or more inputs. The service handler then demultiplexes the incoming requests and dispatches them synchronously to the associated request handlers

From the above description we can take a few key points:

  • An event-driven model
  • Processing multiple inputs
  • The event is multiplexed to the appropriate Handler for processing

In this article, NIO Reactor model & NetTY Threading Model, Reactor actually adopts the sum of divide and conquer event-driven idea:

Divide and conquer: The complete network processing in a connection is generally divided into accept, read, decode, process, encode, send steps. The Reactor pattern maps each step to a Task, and the smallest unit of logic executed by a server thread is no longer a complete network request, but a Task, executed in a non-blocking manner.

Event-driven: Each Task corresponds to a specific network event. When the Reactor is ready, it receives a network event notification and distributes the Task to the Handler bound to the corresponding network event.

To sum up the above characteristics, the Reactor model once again indicates that one or more event inputs are transmitted to the service processor at the same time. The service processor monitors the status of each event. When any event is ready, the service processor receives the event notification. And sends the event to the event Handler that binds the corresponding network event for execution

The Reactor pattern, also known as the Dispatcher pattern, monitors events for I/O multiplexing and dispatches them to the appropriate processing thread.

Patterns are

When it comes to Reactor model will remind me of the observer pattern, both of which look very similar, but the observer pattern is mainly used for one-to-many, it defines a one-to-many dependency, let many observers to monitor a subject object, when the observer states change, need to inform the corresponding observer, enables the observer to automatically update. So actually they are different, the observer pattern is associated with a single event source, whereas the Reactor pattern is associated with multiple event sources.

Three implementations of the Reactor pattern

The following descriptions refer to Reactor’s introduction in Geek Time by Li Yunhua, senior technical expert at Alibaba

There are three typical implementation schemes of Reactor model:

  • Single Reactor Single thread
  • Single Reactor multithreading
  • Reactor is multithreaded

Before we introduce the three scenarios, let’s look at some of the roles in the Reactor schema:

  • Reactor: Responds to events by binding event distribution to the Handler handling of the event
  • Handler: An event Handler. It is bound to a certain type of event and is responsible for executing the corresponding task of the event to process the event
  • Acceptor: A type of Handler that binds connect events to accept events that are sent to acceptors when a client initiates a CONNECT request

Single Reactor Single thread

PS: The above select, Accept,read,send apis are standard I/O reuse model network programming,dispatch and “business processing” are the operations that need to be done.

The specific steps of the scheme are as follows:

  • The Reactor object monitors connection events through SELECT and distributes them through Dispatch
  • If it is a connection establishment event, the Acceptor accepts the connection request and creates a Handler object to handle subsequent business after the connection is completed
  • If the connection is not established, the Reactor dispatches the Handler that calls the connection in response
  • Handler completes the complete business process of read -> business processing -> Send

Advantages of single Reactor single thread:

  • Simple model, no multi-threading, process communication, competition issues, all in one thread

Disadvantages:

  • Only one process can not give full play to the performance of the multi-core CPU. You can only deploy multiple systems to make use of the multi-core CPU, but this will bring operation and maintenance complexity
  • When a Handler processes services on a connection, the entire process cannot process events on other connections, causing performance bottlenecks

Implementation of single Reactor single thread in NIO

The following is a single-reactor single-thread flow chart in Java NIO:

Scalable IO in Java by Doung Lea for details on single-reactor single-thread code in NIO

Single Reactor multithreading

Scheme steps:

  • In the main thread, the Reactor object listens for connection events through SELECT and distributes them through Dispatch
  • If it is a connection establishment event, it is handled by an Acceptor, which accepts the connection and creates a Handler to handle subsequent events.
  • If it is not a connection establishment event, the Reactor calls the connection Handler to do so
  • The Handler only responds to events and does not process services. After reading data, the Handler sends it to the processor for service processing
  • The Processor performs actual service processing in an independent child thread, and then sends the response result to the Handler of the main process. After receiving the response, the Handler sends the response result to the client

Advantages:

  • It can make full use of multi-core and multi-CPU processing power

Disadvantages:

  • Multithreaded data sharing and access are complicated
  • The Reactor monitors and responds to all events and runs only on the main thread, which can be a performance bottleneck at high concurrency

Implementation of single Reactor multithreading in Java NIO

The following figure is a flowchart for a single-reactor multithreading implementation in Java NIO

Description:

  • There is a Reactor thread that listens to the server ServerSocketChannel and receives TCP connection requests from the client
  • Read/write operations of network IO are carried out by a Worker reactor thread pool, and NIO threads in the thread pool are responsible for monitoring SocketChannel events, reading, decoding, encoding and sending messages
  • A NIO thread can process N links simultaneously, but a link is registered with only one NIO thread to prevent concurrent operation problems.

Scalable IO in Java by Doung Lea for details on the code for Single-reactor multithreading in NIO

Multiple Reactor Multiple processes/threads

The following figure takes multiple reactors and processes as an example

Program Description:

  • In the main process, the mainReactor object monitors connection establishment events through select, receives the event through Acceptor, and assigns the new connection to a child process.
  • The subReactor in the child process adds the connection assigned by the mainReactor to the connection queue for listening and creates a Handler to handle various events for the connection
  • When a new event occurs, the subReactor calls the corresponding Handler connected to the reactor to respond
  • Handler Completes the complete business process of read -> Service processing -> Send

Features:

  • The responsibilities of the main and child processes are very clear, the main process is only responsible for receiving new connections, and the child process is responsible for the subsequent business processing
  • The interaction between the master process and the child process is simple; the master process only needs to pass the new connection to the child process, and the child process does not need to return data
  • The child processes are independent of each other and do not need to be shared synchronously (select,read,send, etc.).

The embodiment of multiple Reactor and multithreading in Java NIO

summary

The above summary refers to the analysis of many big gods, which can be regarded as a preliminary understanding of Reactor model.

References & acknowledgements

  • Scalable IO in Java
  • Netty Learning Series 2: NIO Reactor Model & Netty Threading Model
  • Geek Time – Learn architecture from scratch