How can nginx handle concurrent requests with a single worker process?

from https://www.quora.com/How-can-nginx-handle-concurrent-requests-with-a-single-worker-process

Nginx uses the Reactor pattern. Basically, it's single-threaded (but can fork several processes to utilize multiple cores). The main event loop waits for the OS to signal a readiness event - e.g. that data is available to read from a socket, at which point it is read into a buffer and processed.
To really understand how "exactly" it handle requests, one has to understand Event driven architecture and Reactor pattern.

Event Driven Architecture: A server consists
of a small number of threads (typically one per CPU) that loop continuously, processing events of different types from a queue. Events may be
generated by the operating system or internally by the application, and
generally correspond to network and disk I/O readiness and completion
notifications, timers, or other application-specific events. The eventdriven approach implements the processing of each task as a finite state
machine, where transitions between states in the FSM are triggered by
events. In this way the server maintains its own continuation state for
each task rather than relying upon a thread context.



Reactor Pattern (SCHMIDT, Douglas C.: Reactor: an object behavioral pattern for concurrent event demultiplexing and event handler dispatching) :
The Reactor pattern  targets synchronous, non-blocking I/O handling and relies on an event notification interface. On startup, an application following this pattern registers a set of resources (e.g. a socket) and events (e.g. a new connection) it is interested in. For each resource event the application is interested in, an appropriate event handler must be provided--a callback or hook method. The core component of the Reactor pattern is a synchronous event demultiplexer, that awaits events of resources using a blocking event notification interface. Whenever the synchronous event demultiplexer receives an event (e.g. a new client connection), it notifies a dispatcher and awaits for the next event. The dispatcher processes the event by selecting the associated event handler and triggering the callback/hook execution.

The Reactor pattern thus decouples a general framework for event handling and multiplexing from the application-specific event handlers. The original pattern focuses on a single-threaded execution. This requires the event handlers to adhere to the non-blocking style of operations. Otherwise, a blocking operation can suspend the entire application. Other variants of the Reactor pattern use a thread pool for the event handlers. While this improves performance on multi-core platforms, an additional overhead for coordination and synchronization must be taken into account.

nginx: nginx uses multiplexing and event notifications heavily, and dedicates specific tasks to separate processes. Connections are processed in a highly efficient run-loop in a limited number of single-threaded processes called workers. Within each worker nginx can handle many thousands of concurrent connections and requests per second.

    • Master :Monitor workers, respawn when a worker dies.Handle signals and notify workers
    • Worker:Process client requests.Handle connections Get cmd from master.
      Worker processes accept new requests from a shared "listen" socket and execute a highly efficient run-loop inside each worker to process thousands of connections per worker. There's no specialized arbitration or distribution of connections to the workers in nginx; this work is done by the OS kernel mechanisms. Upon startup, an initial set of listening sockets is created. workers then continuously accept, read from and write to the sockets while processing HTTP requests and responses.



      The run-loop is the most complicated part of the nginx worker code. It includes comprehensive inner calls and relies heavily on the idea of asynchronous task handling. Asynchronous operations are implemented through modularity, event notifications, extensive use of callback functions and fine-tuned timers. Overall, the key principle is to be as non-blocking as possible. The only situation where nginx can still block is when there's not enough disk storage performance for a worker process.

      Because nginx does not fork a process or thread per connection, memory usage is very conservative and extremely efficient in the vast majority of cases. nginx conserves CPU cycles as well because there's no ongoing create-destroy pattern for processes or threads. What nginx does is check the state of the network and storage, initialize new connections, add them to the run-loop, and process asynchronously until completion, at which point the connection is deallocated and removed from the run-loop. Combined with the careful use of syscalls and an accurate implementation of supporting interfaces like pool and slab memory allocators, nginx typically achieves moderate-to-low CPU usage even under extreme workloads.

Nginx, like Node.js and lighttpd (but unlike Apache, Python or Ruby), is event based.  Whenever you are doing network programming you realize that the vast majority of the time is spent waiting on I/O, not actually executing code.  Apache solves this by forking and having a bunch of processes, each waiting on one request at a time.  Nginx solves this by letting it's processes handle other requests while they are waiting on I/O.

Nginx builds a state machine for each request.  Then it uses facilities built in to the operating system to get notified when new data is available on any of its sockets.  When new data appears, it moves that request through the state machine, and then it's free to handle other requests.  If multiple events come in at once, they get queued.

Nginx doesn't actually use a single worker, it can be configured to use any number, but it's often not necessary to have more workers than there are cores on the machine.

posted @ 2016-09-01 23:07  princessd8251  阅读(555)  评论(0)    收藏  举报