Skip to content

Instantly share code, notes, and snippets.

@zealfire
Created May 26, 2018 17:29
Show Gist options
  • Save zealfire/51a9818dd03136434483eceace55e058 to your computer and use it in GitHub Desktop.
Save zealfire/51a9818dd03136434483eceace55e058 to your computer and use it in GitHub Desktop.
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.
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.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment