Asynchronous Web Servers

Neil Ernst

2025-09-04

What do we mean by synchronous? Let’s think about what we want a web server to do, with a simple request for your current registered courses. As a web client, your browser/app sends a request for some data to a URL stored at UVic, say

GET www.uvic.ca/students/<id>/current/courses

The web server (at uvic.ca) has to handle this request, retrieve the data (however it is stored) and send that data back to your client:

HTTP/1.1 200 OK <header details>
<content>

So there are a bunch of process steps: 1. Formulate the request (client-side) 2. Handle request (server) 3. Retrieve resource/data (server) 4. Formulate response (server) 5. Send response (server) 6. Receive response (client)

If we do this synchronously we are going to say that the web server, assuming it has one thread, will block all other requests until step 5 has been completed. As a result, we are limited to the number of threads we have as far as clients we can handle. If we want to scale our server, we have to either increase the number of threads, or decrease the amount of time each thread takes to process (step 3/4).

If we do this asynchronously, we will let the server determine how to handle the workload itself, and instead of blocking, instead call a function the client gave us - a callback function - to send the data to.

Is a phone call synchronous or asynchronous? What about instant messaging?

Synchronous Approaches

Synchronous approaches have been predominant. The best example is the Apache HTTPD web server, the original web server software. Consider this architecture model:

HTTPD arch

The key thing to notice here is how Apache is tackling the blocking problem. There is a large pool of worker threads, waiting around to handle requests (this is configurable based on RAM, CPU, etc). A listener module manages requests, and adds new jobs (like retrieving data, running scripts, etc) to the job queue. A task pool is a tactic to prevent the cost of spinning up new threads. Instead, we pre-initialize the thread pool before the demand hits, avoiding this overhead.

Refs

  1. Loupe for debugging event loop http://latentflip.com/loupe/
  2. Server arch for traditional web servers: http://www.fmc-modeling.org/category/projects/apache/amp/4_3Multitasking_server.html
  3. Async JS: https://blog.bitsrc.io/understanding-asynchronous-javascript-the-event-loop-74cd408419ff
  4. Node JS async http://blog.mixu.net/2011/02/01/understanding-the-node-js-event-loop/