A Simple HTTP Server - Multi Threading - Part 2

In part 1, we developed a simple single-threaded HTTP server that returned a static string. While single-threaded servers have their use cases, they lack scalability. The nature of a single-threaded server means it can only process one request at a time. If there's a request that's taking a long time to process, all other requests will be blocked until the slow request has been completed. If we want to handle multiple requests at the same time, we need to use threads.

Contents


A Simple HTTP Server - Part 1
A Simple HTTP Server - Multi Threading - Part 2
A Simple HTTP Server - ThreadPool - Part 3

The Java standard API provides us with the Thread class, that allows for code to execute concurrently. There are two ways of creating a Thread, either by inheriting from the Thread class and overriding the run() method or by implementing the Runnable interface and implementing the run() method.

In Listing 1 below, we create a ClientHandler class that implements the Runnable interface and takes a client socket as an argument in the constructor. We then move the logic to handle the client connection into the run() method.

Listing 1

class ClientHandler implements Runnable{

    private Socket client = null;

    public ClientHandler(Socket client){
        this.client = client;
    }

    public void run(){

        try{
            // Get A BufferedReader/BufferedWriter to handle reading and writing to the stream.

            BufferedReader requestReader = 
                      new BufferedReader(new InputStreamReader(this.client.getInputStream()));
            BufferedWriter responseWriter = 
                      new BufferedWriter(new OutputStreamWriter(this.client.getOutputStream()));

            // Important, we need to read all the data sent from 
            // the client before we can send a response.

            while (true){
                String headerLine = requestReader.readLine();

                if (headerLine.length() == 0){
                    break;
                }
            }

            // How original is this?
            responseWriter.write("Hello World\n");
            responseWriter.flush();

            // Closing the client connection will close, both the input and output streams.
            this.client.close();

        }catch(IOException e){
            try{
                if(this.client != null){
                    this.client.close();
                }
            }catch(IOException e2){

            }
        }
    }
}

Now that we have a Runnable class, we can create an instance of it and pass it to a Thread as shown in Listing 2.

Listing 2

import java.net.*;
import java.io.*;

class HttpServer{

    public static void main(String args[]){

        try{
            // Create a new server socket and listen on port 9000
            try (ServerSocket server = new ServerSocket(9000)){

                // Continue to listen for client connections
                while (true){

                    // Accept a client connection. accept() is a blocking method.
                    Socket client = server.accept();

                    Thread thread = new Thread(new ClientHandler(client));
                    thread.start();
                }
            }

        }catch(Exception e){
            e.printStackTrace();
        }
    }
}


What you should realize from looking at the code in Listing 2, is that a new thread is being created to handle each client. This is an extremely inefficient design as threads are resource intensive. We can test this by running the ab tool as we did in part 1 and then looking at the CPU/memory usage using a program like top on Linux. If you ran the ab tool, you would have also noticed that the requests per second have been drastically reduced.

To improve the performance of the server we need to use a ThreadPool. Using a ThreadPool will allow us to manage the number of threads and reuse them for subsequent requests. In the next article, we'll take a look at implementing a very basic ThreadPool.

HANA DB

Rust

Java

SAP Business One

Node.js