Netty Learning (9): Combining NIO and Netty to analyze the calling relationship of the five major roles in the Reactor mode

Overview

This article undertakes Netty learning (7): Explain "Scalable IO in Java" in detail, and understand the Reactor mode

Continue to interpret the Reactor mode, because the Reactor mode is too important for the learning of NIO and Netty

Related documents: [ Tencent document] Reactor classic pioneering paper , [Tencent document] Scalable_IO_in_Java

This article must be read in conjunction with these two documents, don't be afraid to read English! ! !

The role composition of Reactor mode

We will analyze the role composition of Reactor mode from the above picture (for all articles about Reactor mode, this picture will definitely exist!)

Handle (Handle or Descriptor)

It is called a handle under Windows and a descriptor under Linux. In essence, it represents a kind of resource, which is provided by the operating system. The resource is used to represent individual events , such as file descriptors and socket descriptors for network programming. Events can come from outside. It can also come from internal, external events such as client connection requests, data sent by the client, etc., internal events such as timing events generated by the operating system. It is essentially a file descriptor, where the event occurs (analogous to network programming, the client sends a connection request to the server, this connection event is generated by Handle, and the client sends data to the server. Handle will generate a Read event ).

Synchronous Event Demultiplexer (synchronous event demultiplexer)

It itself is a system call to wait for the occurrence of events (there may be one event, or there may be more than one event), and the caller will be blocked when calling it, until an event is generated on the synchronous event separator. For Linux, the synchronous event separator refers to the commonly used I/O multiplexing mechanism, such as select, poll, epoll, etc.

Based on the selector() in the Synchronous Event Demultiplexer in the figure, it is obvious that it can correspond to the Selector in Java NIO, and the corresponding blocking method is the select() method . The select() method corresponds to different underlying system calls in different systems (windows and linux), it is an operating system level call.

Event Handler

It is composed of multiple callback methods , which constitute an application-related feedback mechanism for an event . In the Java NIO field, there is no event handler mechanism for us to call or make callbacks ( NIO does not provide a role corresponding to the Event Handler ), which is done by our own code. For example, the SimpleChannelInboundHandler inherited from the custom handlers we wrote in Netty . These Handlers have a large number of callback methods. When the channel is in different states, different events will be called back.

Compared with Java NIO, Netty has upgraded the role of event handlers. It provides developers with a large number of callback methods for us to implement corresponding callback methods for business logic processing when specific events occur. .

Concrete Event Handler

It is the specific implementation of Event Handler. Event Handler is obviously provided by the framework or library, while C oncrete Event Handler is a specific implementation written by the developer, thus realizing business-specific logic . In Netty, for example: It is equivalent to the subclass of SimpleChannelInboundHandler that we wrote , which rewrites its callback method.

Initiation Dispatcher (Initial Dispatcher)

In fact, the Initiation Dispatcher is actually the role of Reactor in the figure.

It itself defines some specifications, which are used to control the scheduling of events, and at the same time provide facilities for applications to register and delete event handlers.

It itself is the core of the entire event processor, and the Initiation Dispatcher will wait for events to occur through Synchronous Event Demultiplexer. Once an event occurs, the Initiation Dispatcher first separates each event, then calls the event handler, and finally calls the related callback method to handle these events . To speak of people: Initiation Dispatcher will use the result of select() in Synchronous Event Demultiplexer, that is Using each event (SelectionKey in NIO), the Initiation Dispatcher gets the collection returned by SelectionKey, and traverses to get each element (each SelectionKey). At the same time, some methods are provided: register(h), remove(h)...These h are Handle, and register and delete the individual handlers (Concrete Event Handler) we provide on the corresponding SelectionKey through Handle .

Each callback method in ChannelHandler in Netty is called by a certain EventLoop in bossGroup or workGroup. WorkGroup corresponds to subReactor. SubReactor and mainReactor are essentially Initiation Dispatcher. The channelRead0() in the processor we wrote is actually Is called by subReactor

Call relationship

  • When an event occurs, the event handler (Event Handler) will be called, because the handler has been registered in the Initiation Dispatcher before. The type in Event Handler.handle_event(type) is actually the corresponding event Type, and the corresponding handle can be obtained through get_handle().
  • When an event (Handle) occurs, it is detected by the Synchronous Event Demultiplexer, and the SelectionKey corresponding to the event is returned to the Initiation Dispatcher. The Initiation Dispatcher calls different Events through different events. Handler (event processor), why can the Initiation Dispatcher know which Event Handler to call for different events? Event Handler must have a binding relationship with Handle-Event Handler has Handle.
  • Before the server is started, that is, before the Initiation Dispatcher thread loop starts, a number of Event Handlers will be registered in the Initiation Dispatcher, and then the thread loop will continue to execute. When the event Synchronous Event Demultiplexer (synchronous event demultiplexer) generates an event to the Initiation Dispatcher (initial dispatcher), the Initiation Dispatcher (initial dispatcher) traverses the Event Handler (event processor) related to the current time

At this time, the handler corresponding to the event will be called. The handle_event(type) method in the figure is actually the handle_event(type) method in the Event Handler.

  • Through the above analysis, we can explain the callback logic of the channelRead0 method of the Handler in Netty. The channelRead0 is actually called by the workerGroup (corresponding to the Initiation Dispatcher). The various processors we wrote are added to the ChannelPipeline, which is actually Event Handler (event processor) is registered in the Initiation Dispatcher (initial dispatcher), after which the event loop begins to execute (EventLoop, that is, workersGroup starts to execute the event loop), and starts to call back different event handlers according to different events Callback method in.

Event call flow

  1. First execute the Initiation Dispatcher (initial dispatcher), which is the Reactor role, and register a number of Concrete Event Handlers (specific event handlers) on it, and at the same time specify the processor "interesting event", this "interesting event" "Is identified by Handle, which is equivalent to NIO ServerSocketChannel.register(Selector sel, int ops), ops is the "interested event", and then the event loop is opened.
  2. When the event loop starts, the Initiation Dispatcher (initial dispatcher) monitors the occurrence of time through the Synchronous Event Demultiplexer (synchronous event demultiplexer).
  3. When the "interesting event" occurs on the Handle associated with the Event Handler, the Synchronous Event Demultiplexer (synchronous event separator) obtains the set of generated events to the Initiation Dispatcher (initial dispatcher)
  4. The Initiation Dispatcher (initial dispatcher) traverses the collection of handlers related to the received event, and calls the handle_event() method of the corresponding Concrete Event Handler registered to it to process the event according to the type of event.

Reactor mode process

From the analysis of the event call process, we can conclude that the detailed call process of Reactor mode is as follows:

  1. Initialize the Initiation Dispatcher, and then register several Concrete Event Handlers in the Initiation Dispatcher. When the application registers Concrete Event Handler with the Initiation Dispatcher, it will specify the event of interest at the same time of registration, that is, the application will identify the event handler hopes that the Initiation Dispatcher will notify it when certain events occur, and the event is identified by Handle , And the Concrete Event Handler holds the Handle. In this way, the event ————> Handle ————> Concrete Event Handler is connected.
  2. Initiation Dispatcher will require each event handler to pass its internal Handle. The Handle identifies the event handler to the operating system.
  3. When all Concrete Event Handlers are registered, the application will call the handle_events method to start the event loop of the Initiation Dispatcher. This is, the Initiation Dispatcher will merge the Handle of each registered Concrete Event Handler and use the Synchronous Event Demultiplexer to synchronize the occurrence of blocked waiting events. For example, the TCP protocol layer will use the select synchronization event separator operation to wait for the data sent by the client to arrive at the connected socket handler. For example, in Java, the select() method of Selector is used to realize the operation of synchronous blocking waiting for the event to occur. Under the Linux operating system, in the implementation of select() a) will call epollCtl (epfd, opcode, fd, events) to register the events that have been registered in the Initiation Dispatcher to the Linux system, where fd means Handle and events means we are interested Handle event; b) Synchronously block waiting for the registered event to occur by calling the epollWait method. Events on different event sources may occur at the same time. Once an event is triggered, the epollWait method will return; c) Finally, find the associated SelectorKeyImpl object through the event that occurred, and set the event that occurred to the ready state, and then set the SelectorKeyImpl Put it in the selectedSet. In this way, we can get the Event-ready SelectorKeyImpl collection through the Selector.selectedKeys() method.
  4. When the Handle corresponding to an event source becomes the ready state (for example, when the TCP socket becomes the waiting state), the Synchronous Event Demultiplexer will notify the Initiation Dispatcher.
  5. Initiation Dispatcher will trigger the callback method of the event handler to respond to the Handle in the ready state. When an event occurs, the Initiation Dispatcher will use the Handle activated by the event source as the "key" to find and distribute the appropriate event handler callback method.
  6. The Initiation Dispatcher will call back the handle_event(type) callback method of the event handler to perform application-specific functions (functions written by the developer) to respond to this event. The type of event that occurs can be used as the method parameter and used internally by the method to perform additional service-specific separation and distribution.

 

 

 

 

 

 

 

 

Guess you like

Origin blog.csdn.net/qq_25805331/article/details/109273982