====== 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 } } } * ''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. @startuml participant "Servidor" as S participant "Hilo 1" as H1 participant "Hilo 2" as H2 actor "Cliente A" as A actor "Cliente B" as B A -> S : conexión A S -> H1 : handle(socketA) B -> S : conexión B (simultánea) S -> H2 : handle(socketB) H1 --> A : respuesta H2 --> B : respuesta C -> S : conexión C (posterior) note right of S : reutiliza un hilo del pool S -> H1 : handle(socketC) H1 --> C : respuesta @enduml ===== 2. Las reglas de los hilos siguen valiendo ===== * 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. ===== 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.