Nginx Multi-Process Architecture

Nginx Multi-Process Architecture

High-performance Nginx and its architecture are inseparable. In this section, we will delve into the acquaintance frame of Nginx and explore its multi-process architecture.

Background Process

When Nginx starts, it runs in the background as a daemon under the Unix system, containing a master process and multiple worker processes. This background process can be manually turned off, allowing Nginx to run in the foreground. However, this is generally used for debugging purposes and not recommended for production environments.

Master Process

The master process is primarily responsible for managing the worker processes. Its functions include:

  1. Receiving signals from the outside
  2. Sending signals to each worker process
  3. Monitoring the operation status of worker processes
  4. Automatically restarting a new worker process when a worker process exits (exceptional circumstances)

Worker Processes

Worker processes are responsible for handling network events. Each worker process is independent and competes for client requests. A request can only be processed in a single worker process, and other processes cannot handle the request. The number of worker processes can be set, usually equal to the number of CPU cores on the machine.

Process Control

To control Nginx, we need to communicate with the master process. The master process receives signals from the outside and performs different actions based on the signal. We can control Nginx by sending signals to the master process, such as the HUP signal, which calmly restarts Nginx or reloads the configuration.

Signal Handling

When the master process receives a HUP signal, it reloads the configuration file and starts a new worker process. The old worker process receives a signal to exit, and the new worker process begins receiving new requests. The signal is sent directly to the master process, which is an old mode of operation. Nginx versions prior to 0.8 use this method.

Reloading Configuration

To reload the configuration, we can use the command nginx -s reload. This starts a new Nginx process and resolves the reload parameters. We can also send a signal directly to the master process using the command nginx -s stop.

Network Event Handling

Nginx uses an asynchronous non-blocking method to handle network events, similar to Libevent. This allows Nginx to handle thousands of simultaneous requests without creating multiple threads. Each worker process is responsible for handling network events and competing for client requests.

Event Handling

When a worker process receives a connection request, it establishes a socket and forks a new process to handle the request. Each worker process becomes readable when a new connection arrives, and all worker processes grab the accept_mutex before registering the listenfd read event. The worker process then accepts the connection and begins reading the request.

Benefits of Multi-Process Architecture

The multi-process architecture of Nginx has several benefits, including:

  1. Each worker process is a separate process, eliminating the need for locks and reducing overhead.
  2. Worker processes do not affect each other, and the service will not be interrupted if a process exits.
  3. The master process quickly starts a new worker process to replace an exiting process.

Event Handling Mechanism

Nginx uses a non-blocking asynchronous event handling mechanism, similar to epoll. This mechanism allows Nginx to monitor multiple events at the same time and set a timeout for the event. When an event is ready, Nginx deals with it, and when all events are not ready, it waits inside the epoll.

Timer Handling

Nginx uses a red-black tree to maintain timer events. When the timer event is due, Nginx checks the status of the event and processes the network events. This allows Nginx to handle timer events efficiently.

Pseudo-Code for Event Handling

The pseudo-code for Nginx event handling is as follows:

while (true) {
    for t in run_tasks:
        t.handler();
    update_time(&now);
    timeout = ETERNITY;
    for t in wait_tasks:
        if (t.time <= now) {
            t.timeout_handler();
        } else {
            timeout = t.time - now;
            break;
        }
    nevents = poll_function(events, timeout);
    for i in nevents:
        task t;
        if (events[i].type == READ) {
            t.handler = read_handler;
        } else {
            t.handler = write_handler;
        }
        run_tasks_add(t);
}

This pseudo-code illustrates the event handling mechanism of Nginx, including the handling of network events, timer events, and signal events.