Mejora de nuestro servidor web con multitareas: ThreadPool

Clase 9 de 12Curso de Gestión de Servidores con Rust

Si se preguntaba porque primero pasamos por el tema sobre concurrencia, es porque es hora de que la pongamos en acción. Para crear un servidor que nos acepte varias peticiones, hacemos el uso de la concurrencia. Es por eso que es importante que hayas practicado y entendido los conceptos que manejamos anteriormente, si no es así, puedes darle un repaso antes de continuar.

Aquí regresaremos a nuestro código donde creamos un servidor de una sola petición, le haremos algunas mejoras para que pueda aceptar múltiples peticiones a la vez, por eso trabajaremos en nuestro ambiente local, para poder ver las respuestas en nuestro navegador.

Para este ejercicio necesitamos reestructurar las carpetas de nuestro proyecto, para esto crearemos una nueva carpeta llamada bin dentro de nuestra carpeta src, moveremos nuestro archivo principal main.rs dentro de la carpeta bin. Dentro de nuestra carpeta crearemos un nuevo archivo llamado lib.rs.

Debido a que no estamos usando dependencias dentro de nuestro proyecto, no podemos usar threadpool por parte crates.io, entonces debemos crear nuestra propia librería de Threadpool, el cual será llamado por medio de use nombredetuproyecto::ThreadPool;, en nuestro archivo main.

Comencemos con nuestro archivo main.rs. En nuestra función principal pasamos el stream a la función handle_connection, ahora queremos que nuestro grupo de subprocesos funcione de manera similar y familiar, por lo que cambiar de subprocesos a un grupo de subprocesos es fácil usando ThreadPool::new para crear un nuevo grupo de hilos con un número configurable de hilos.

En nuestra función principal agregaremos:

let pool = ThreadPool::new(3); for stream in listener.incoming() { let stream = stream.unwrap(); pool.execute(|| { handle_connection(stream); });

Utilizamos ThreadPool::new para crear un nuevo grupo de subprocesos con un número configurable de subprocesos, en este caso 3. Luego, en el bucle for, pool.execute tiene una interfaz similar thread::spawn a la que requiere un cierre que el grupo debe ejecutar para cada flujo. Necesitamos implementarlo pool.execute para que tome el cierre y lo entregue a un subproceso en el grupo para que se ejecute.

Agregamos un bloque else if después del if bloque para verificar la solicitud de /sleep. Cuando se recibe esa solicitud, el servidor dormirá durante 5 segundos antes de mostrar la página HTML correcta.

let sleep = b"GET /sleep HTTP/1.1\r\n"; let (status_line, filename) = if buffer.starts_with(get) { ("HTTP/1.1 200 OK\r\n\r\n", "hello.html") } else if buffer.starts_with(sleep) { thread::sleep(Duration::from_secs(5)); ("HTTP/1.1 200 OK\r\n\r\n", "hello.html") } else { ("HTTP/1.1 404 NOT FOUND\r\n\r\n", "404.html") };

Creamos una nueva variable llamada sleep, agregamos un bloque else if después del if bloque para verificar la solicitud de sleep. Cuando se recibe esa solicitud, el servidor dormirá durante 5 segundos antes de mostrar la página HTML correcta. Para eso usamos use std::time::Duration; qué vimos en nuestro capítulo de concurrencia y lo agregamos en la parte inicial de nuestro código, donde llamamos a nuestras librerías.

Ahora tenemos configurado nuestro archivo main.rs, si lo ejecutamos, este dará un mensaje de error que nos dice que necesitamos un ThreadPool, ya que hemos creado un grupo de subprocesos pero no tenemos ninguna dependencia de donde podamos llamarla. Nuestra implementación de ThreadPool será independiente del tipo de trabajo que nuestro servidor web está haciendo, usaremos el archivo que creamos llamado lib.rs.

#![allow(unused_variables)] fn main() { pub struct ThreadPool; }

Esta es la definición más simple de una estructura ThreadPool que podemos tener por ahora. No vamos a compilar el código todavía, ya que aún nos falta definir el comportamientos de la implementación Threadpool, pero eso lo veremos en nuestra siguiente clase.

Por el momento cuéntame, ¿vas entendiendo el procedimiento de cómo crear nuestras propias librerías para no usar dependencias o frameworks?

Nos vemos en la siguiente clase.