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 elSockety 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.
2. Las reglas de los hilos siguen valiendo
- Estado
staticcompartido → condición de carrera entre peticiones. ThreadLocalsin 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.