Nginx Event Driven Architecture — Demonstrated in Code

Eileen Pangu
Level Up Coding
Published in
5 min readJan 15, 2021

--

Source: nginx.com

Background

Nginx is an open source web server that can be used as a reserve proxy, load balancer, HTTP cache, and mail proxy. It’s capable of supporting a high number of concurrent connections, which is one of its flagship features. The Nginx website has a blog post [1] that explains the event driven architecture behind its stellar scalability. While that’s an excellent deep dive, hardcore engineers may still feel unsatisfied because we’re always looking for a level of understanding that involves actual code.

Nginx’s source code, albeit openly available, is challenging to read because of its humongous size and slim comments. So to demonstrate the core architecture, I wrote a simple server in C that replicates Nginx’s event driven design. My code is on Github [2]. Feel free to check out the repo and play around with the server. In this blog post, I’ll walk you through the event driven architecture from an implementation standpoint.

Get the Socket Ready

First thing first, we need to create a socket, bind it to a network address and port, and start listening on the socket. The series of socket, bind, and listen system calls are such a standard receipt that almost all web servers start with them. Nginx is no exception. One minor note is that we want to set the listening socket to be non-blocking — the reason will become clear soon.

Event Loop

The next step is what differentiates Nginx from other web servers, such as Apache. When a client connection comes in, after calling accept, one could spawn a new thread to handle the request. This enables the textbook concurrent request handling. I remember this was how I was taught in school as well. Nevertheless, in a real production environment, this is not the most scalable way. Threads, while being lighter than processes, are too heavy in most modern operating systems. The overhead is overwhelming when we have hundreds or thousands of threads, each representing a client connection. The canonical approach is, instead, to use an event loop.

Different operating systems have different interfaces for event loops (e.g, select, epoll, epoll_wait, evpoll, kqueue). But they’re all of the same thing. In essence, the…

--

--

Manager and Tech Lead @ FANG. Enthusiastic tech generalist. Enjoy distilling wisdom from experiences. Believe in that learning is a lifelong journey.