The article discusses implementing a web server from scratch, focusing on handling TCP connections and serving HTTP traffic through three server models: blocking single-threaded server, blocking server with keep-alive support, and multithreaded non-blocking server.
Readers will learn to implement a blocking TCP server, extend it for HTTP keep-alive connections, use threads for parallel client handling, structure code with clean building blocks, parse requests using configuration files with NGINX-style blocks, benchmark the server using ApacheBench, and compare performance between single-threaded and multithreaded architectures.
The blocking single-threaded server handles one connection at a time, while the blocking server with keep-alive feature enables multiple requests on the same socket. The article introduces HTTPSession, DataProvider, and HTTPProcessor classes to manage client connections, buffer raw data, and process HTTP messages.
The NGINX-style routing logic is explained, describing how server and location blocks are matched to define routes-to-file mappings. The RouteMatcher class is used to select the correct root directory based on the request URI.
The article presents the implementation of the server with config and NGINX-style routing support, allowing configurable server behavior. Route matching is demonstrated using a priority system with location blocks.
Benchmark results from ApacheBench showcase varying performance metrics for different scenarios, comparing single client operations, concurrent clients, and keep-alive connections. Conclusions highlight the advantages of multithreaded servers in handling concurrency and the limitations of blocking servers in scalability.
The transition from blocking to multithreaded servers is discussed, emphasizing the significance of threads in enabling parallel client handling and avoiding server freezes. The impact of Python's Global Interpreter Lock (GIL) on threading efficiency is addressed in the context of I/O-bound tasks like web servers.
The performance comparison between blocking and multithreaded servers reveals the importance of concurrency handling and the scalability benefits of multithreaded architectures. Keep-Alive connections significantly improve server performance in various scenarios.
Readers are encouraged to explore non-blocking I/O and event-driven architecture in the next part of the series for improved server performance and scalability.