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.

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
        }
    }
}

  • accept() devuelve el Socket y el bucle principal queda libre para aceptar la siguiente conexión.
  • El pool ejecuta cada petición en un hilo worker.
  • Los hilos del pool se reutilizan entre peticiones.

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

  • Estado static compartido → condición de carrera entre peticiones.
  • ThreadLocal sin limpiar → fuga de datos a la siguiente petición del mismo hilo.
  • Variables locales → seguras: cada hilo tiene su propia pila.

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.

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.
  • clase/daw/si/1eval/concurrente.txt
  • Última modificación: 2026/10/12 13:01
  • por cesguiro