- Para crear servidores web de alto rendimiento, antes se usaban diversos modelos basados en eventos como select(), poll(), epoll
- Pero por las limitaciones de rendimiento de esas llamadas al sistema, apareció io_uring, que introduce un método donde las solicitudes se ponen en una cola para que el kernel las procese de forma asíncrona
- kTLS hace que el kernel se encargue del cifrado TLS, lo que permite optimizaciones adicionales como el uso de sendfile() y el offloading por hardware
- La introducción de descriptorless files ofrece un enfoque optimizado para io_uring sin pasar directamente descriptores de archivo
- A través del proyecto open source tarweb, que combina Rust, io_uring y kTLS, se ofrece HTTPS sin llamadas al sistema adicionales por solicitud, y también se discuten temas de seguridad y gestión de memoria
Evolución de la arquitectura de servidores web de alto rendimiento
- Desde principios de los años 2000 aumentó la demanda de servidores web de alta capacidad
- Al principio era común crear un nuevo proceso por cada solicitud, pero por su alto costo surgió la técnica de preforking
- Después, con la introducción de hilos y la adopción de select(), poll(), la evolución apuntó a reducir el costo de los cambios de contexto
- Aun así, los enfoques con select() y poll() tienen límites de escalabilidad, porque mientras más conexiones hay, más seguido hay que pasar grandes arreglos al kernel
La llegada de epoll
- En Linux se introdujo epoll, lo que permitió manejar múltiples conexiones de forma más eficiente que con los métodos anteriores
- epoll procesa solo los cambios (deltas), reduciendo el consumo innecesario de recursos
- No elimina por completo todas las llamadas al sistema, pero sí reduce bastante su costo
Resumen de io_uring
- io_uring agrega las solicitudes a una cola en memoria para que el kernel las procese de forma asíncrona, en lugar de invocar una llamada al sistema por cada petición
- Por ejemplo, si se pone accept() en la cola, el kernel lo procesa y devuelve el resultado en la cola de completado
- El servidor web funciona agregando solicitudes a la cola y revisando los resultados en una región de memoria separada
- Para evitar un bucle ocupado (busy loop), si no hay cambios en la cola, tanto el servidor web como el kernel llaman al sistema solo cuando es necesario, logrando ahorro de energía
- Con las bibliotecas adecuadas, un servidor activo puede funcionar sin llamadas al sistema adicionales mientras procesa solicitudes
Entornos multi-core y NUMA
- Considerando los CPU modernos con múltiples núcleos, resulta efectiva una estrategia de un solo hilo por núcleo y de minimizar el uso compartido de estructuras de datos
- En entornos NUMA, cada hilo se optimiza accediendo solo a la memoria de su nodo local
- Lograr un balance perfecto en la distribución de solicitudes todavía requiere más investigación
Asignación de memoria
- Tanto en el kernel como en el servidor web sigue habiendo asignación de memoria, y la asignación en espacio de usuario al final también se conecta con llamadas al sistema
- Del lado del servidor web, se asignan por adelantado bloques de memoria de tamaño fijo por conexión para prevenir fragmentación y escasez
- Del lado del kernel también se necesitan búferes de entrada/salida por conexión, y parte de eso puede ajustarse con opciones de socket
- Si ocurre escasez de memoria, puede derivar en fallas graves
Introducción a kTLS (Kernel TLS)
- kTLS es una función del kernel de Linux que se encarga de las operaciones de cifrado y descifrado
- El handshake se maneja en la aplicación, pero después de eso el kernel trata la transmisión de datos como si fuera texto plano
- Esto permite usar sendfile() y reducir las copias de memoria entre el espacio de usuario y el kernel
- Si la tarjeta de red lo soporta, también existe la ventaja de hacer offloading por hardware incluso de las operaciones de cifrado
Descriptorless Files
- Es un enfoque surgido para reducir la sobrecarga que aparece al pasar directamente descriptores de archivo del espacio de usuario al espacio del kernel
- Con
register_files se usan números de archivo enteros separados que solo son válidos dentro de io_uring, y no aparecen en /proc/pid/fd
- El límite
ulimit del sistema sigue aplicando
Introducción al proyecto tarweb
- tarweb es un proyecto open source de servidor web de ejemplo que aplica todas las tecnologías anteriores
- Tiene una estructura que sirve el contenido de un único archivo tar, y combina tecnologías modernas de alto rendimiento como Rust, io_uring y kTLS
- Durante su uso real aparecieron problemas de compatibilidad entre io_uring y kTLS (como la falta de soporte para setsockopt), y algunos se resolvieron mediante Pull Requests
- El proyecto todavía está incompleto, y la biblioteca rustls de Rust puede realizar asignaciones de memoria durante el proceso de handshake
- El punto clave es que es posible ofrecer HTTPS sin llamadas al sistema adicionales por cada solicitud
Benchmark y medición de rendimiento
- El autor todavía no ha realizado suficientes benchmarks y planea hacer pruebas de rendimiento después de ordenar el código
Problemas de seguridad con io_uring y Rust
- A diferencia de las llamadas al sistema síncronas, en io_uring el búfer de memoria no debe liberarse hasta antes del evento de completado
- El crate io-uring no garantiza la seguridad en tiempo de compilación de Rust, y también carece de suficientes verificaciones en tiempo de ejecución
- Si se usa mal, puede llevar a problemas graves, de forma similar a C++, debilitando la seguridad propia de Rust
- Se necesita un crate separado, safer-ring, que aproveche activamente el pinning y el borrow checker
- Este problema ya está en discusión dentro de la comunidad
Referencias y enlaces adicionales
- Este contenido corresponde a una publicación discutida en HackerNews al 2025-08-22
Aún no hay comentarios.