The design of Libevent is a typical Reactor model. The understanding of Reactor model is the cornerstone of understanding Libevent. Therefore, this section mainly introduces the typical event-driven design pattern –Reactor model

The Reactor design pattern handles service requests that are delivered concurrently to an application by one or more clients.

The first sentence in the reactor. PDF directly states that the REACTOR design pattern can be used to process one or more client requests for an application simultaneously

Reactor’s event handling mechanism

Reactor Bing is an event-driven mechanism. The difference from a normal function call is that: Instead of the application actively calling an API, the Reactor inverts the event processing process. The application needs to provide an interface and register it with the Reactor. If an event occurs, the Reactor will actively call the interface registered by the application. These interfaces are also called “callback functions “.

When using libevent, you need to register the corresponding event with the libevent framework and call the corresponding function. When the registered event occurs, libevent will call the corresponding callback function to handle the corresponding time (I/O read/write, timer, signal).

The Hollywood rule is perfect for Reactor: Don’t call us, we’ll call you.

Reactor model structure

In the Reactor model, there are five key actors.

  • Handle: Provided by the operating system, used to identify each event, such as Socket descriptor and file descriptor. In Linux, it is represented as an integer. Events can come from outside, such as connection requests from clients, data, and so on. Events can also come from within, such as timer events.
  • Synchronous Event demultiplexer: is a function that waits for one or more events to occur. The caller is blocked until an event occurs on the descriptor set separated by the separator. The Linux select function is a commonly used separator.
  • Event Handler: An interface that consists of one or more template functions. These template functions describe application-specific actions on an event.
  • Concrete Event Handler: Is an implementation of an event handler interface that implements a service provided by an application. Each specific event handler is associated with a descriptor. It uses descriptors to identify events and services provided by the application.
  • Reactor Manager (Dispatcher) : Defines interfaces for application control of event scheduling, and application registration, deletion of event handlers, and associated descriptors. It is the scheduling core of the event handler. The Reactor Manager uses synchronous event separators to wait for events to occur. Once an event occurs, the Reactor Manager separates each event, schedules the event handler, and finally calls the relevant template function to handle the event.

In the diagram above, you can see that the Rector Manager (Dispatcher) is the most critical player in the Reactor pattern as it is the class that ultimately provides the interface to the user. The user can register an Event handler with the Reactor. Then, when the Reactor detects that an event occurs in the user’s registered FD during react, the Reactor calls the user’s event handler. Here’s a typical REACTOR statement:

Class Reactor {public: // constructor Reactor(); // destructor ~Reactor(); //@retval 0 successfully registered // @retval-1 failed to register int RegisterHandler(EventHandler *handler, event_t evt); Int RemoveHandler(EventHandler) int RemoveHandler(EventHandler) int RemoveHandler(EventHandler *handler); //@param timeout void HandlerEvents(int timeout = 0); Private: ReactorImplementation * m_reactor_impl; // Reactor implementation class}Copy the code

SynchrousEventDemultiplexer is a more important role in the Reactor, the Reactor it is used to detect events that occurred on fd registered users, through the Reactor that the fd on what kind of event happened, and then based on these, To multiroute events and call back user event handlers. Here is a simple design:

class EventDemultiplexer { public: // @param Events events obtained // @param Timeout timeout period // @retval 0 No event handler (timeout) // @retval is greater than 0 // @retval < 0 Error virtual int WaitEvents(STD ::map< HANDLE_T, event_t> * events, int timeout = 0) = 0; // @retval 0 is set successfully // @retval < 0 is set incorrectly Virtual int RequestEvent(handle_t handle, event_t evt) = 0; // @retval 0 = 0 virtual int UnrequestEvent(handle_t handle, event_t evt) = 0; };Copy the code

The Event Handler provides a set of interfaces, each corresponding to a type of Event, that Reactr calls to perform the corresponding Event processing when the corresponding Event occurs. Usually it will bind a valid handle. For libevent, that’s the event structure. Here are two ways to declare a typical Event Handler class.

Class Event_Handler {public: // Handle the read event callback function virtual void handle_read() = 0; Virtual void handle_write() = 0; // Handle timeout callback function virtual void handle_timeout() = 0; // Close the corresponding handle. Virtual void handle_close() = 0; Virtual HANDLE get_handle() = 0; }; ________________________________________________________________________________ class Event_Handler { public: //event maybe read/write/timeout/close .etc virtual void hand_events(int events) = 0; virtual HANDLE get_handle() = 0; }Copy the code

The ConcreteEventHandler ConcreteEventHandler is a subclass of EventHanler. EventHandler is the base class that Reactor uses to specify interfaces. Users must inherit their own event handlers from EventHandler.

Reactor Event processing process

The following diagram shows the application participating in a collaborative interaction under the Reactor model

Benefits of the Reactor model

The Reactor pattern is one of the necessary techniques for writing high performance web servers. It has the following advantages:

  • Fast response and not blocked by a single synchronization time, although the Reactor itself is still synchronous
  • Programming is relatively simple, avoiding complex multithreading and synchronization problems to the greatest extent, and avoiding the switching overhead of multithreading or multi-process
  • Scalability, which makes full use of CPU resources by increasing the number of Reactor instances
  • Reusability: The Reactor framework itself is independent of the specific event processing logic and has high reusability

Disclaimer: This article is part of the content of the arrangement of reference from the Internet, in line with the purpose of sharing learning, and no commercial, if there is a violation of the place can contact me and delete.

Reference:

www.laputan.org/pub/sag/rea…

www.wuzesheng.com/?p=1607

Blog.csdn.net/sparkliang/…


If you find it useful, you can go to Star on Github and encourage me, or Pull Reauest to fix it