Tabla de Contenidos

04 - Servidor HTTP concurrente

El servidor del tema anterior atiende una petición a la vez. La solución es combinar ambas ideas: un pool de hilos del tema 3 atendiendo las conexiones del servidor de sockets.

1. Un hilo por conexión

public class ConcurrentHttpServer {
    private static final ExecutorService POOL = Executors.newFixedThreadPool(10);

    public static void main(String[] args) throws IOException {
        try (ServerSocket serverSocket = new ServerSocket(8080)) {
            while (true) {
                Socket socket = serverSocket.accept();
                POOL.execute(() -> handle(socket));
            }
        }
    }

    private static void handle(Socket socket) {
        try (socket) {
            // leer petición y responder como en el tema 02
        } catch (IOException exception) {
            // registrar el error
        }
    }
}

ServidorHilo 1Hilo 2Cliente ACliente BCServidorHilo 1Hilo 2Cliente ACliente BCServidorHilo 1Hilo 2Cliente ACliente BCconexión Ahandle(socketA)conexión B (simultánea)handle(socketB)respuestarespuestaconexión C (posterior)reutiliza un hilo del poolhandle(socketC)respuesta

2. Las reglas de los hilos siguen valiendo

3. HttpServer del JDK

Java incluye un servidor HTTP con pool de hilos integrado, útil como alternativa intermedia entre el servidor propio y Tomcat:

public class JdkHttpServer {
    public static void main(String[] args) throws IOException {
        HttpServer server = HttpServer.create(new InetSocketAddress(8080), 0);

        server.createContext("/books", exchange -> {
            String body = "{\"message\":\"books endpoint\"}";
            exchange.getResponseHeaders().set("Content-Type", "application/json");
            exchange.sendResponseHeaders(200, body.getBytes().length);
            try (OutputStream output = exchange.getResponseBody()) {
                output.write(body.getBytes());
            }
        });

        server.setExecutor(Executors.newFixedThreadPool(10));
        server.start();
    }
}

HttpServer resuelve el parseo del protocolo; el Executor gestiona el pool. Lo que sigue sin resolver: enrutado por rutas, archivos estáticos, ciclo de vida de componentes… Eso es exactamente lo que aporta un contenedor de servlets.

4. Hacia Tomcat

Capa Problema que resuelve
Sockets Comunicación TCP
Pool de hilos Concurrencia entre peticiones
HttpServer / contenedor Protocolo HTTP, enrutado, recursos
Tomcat Todo lo anterior + ciclo de vida de servlets y filtros
Este servidor es el modelo mental con el que entender Tomcat: un contenedor que acepta conexiones, las reparte entre hilos de un pool y delega cada petición en un componente (el servlet) que el desarrollador escribe.