¿Existe un límite en la cantidad de descriptores de archivos monitoreados por libevent?
1. El problema del C10K
El problema del C10K se planteó en los años 1990. El significado general es que cuando el número de usuarios supera los 10.000, el rendimiento de muchos programas de servicios de red mal diseñados disminuirá drásticamente o incluso se paralizará. Además, este problema no se puede resolver actualizando el equipo de hardware. Es un problema inherente del sistema operativo. En otras palabras, si su servidor puede admitir hasta 1000 concurrencias, incluso si actualiza la CPU con el doble de potencia informática. memoria, ahora, incluso si la velocidad del disco duro se duplica, no puede admitir 2000 concurrencias.
Existen 4 modelos clásicos de programación de redes:
1. Sirve a un cliente con cada subproceso/proceso y utiliza E/S de bloqueo. Es decir, utilizar diferentes subprocesos o procesos para atender a cada cliente y utilizar E/S de bloqueo en cada subproceso o proceso. Esta es una estrategia comúnmente utilizada para programas pequeños y Java. También es una opción común para aplicaciones interactivas. Esta estrategia es difícil de satisfacer las necesidades de programas de alto rendimiento. La ventaja es que es extremadamente simple y fácil de implementar. lógica interactiva compleja. Nuestros Apache, ftpd, etc. de uso común funcionan de esta manera.
2. Atienda a muchos clientes con un solo subproceso y utilice E/S sin bloqueo y notificación de preparación. Es decir, se utiliza un único subproceso o proceso para atender a todos los clientes. En este subproceso o proceso, se adopta una estrategia de IO asincrónica. Este es un modelo clásico. La ventaja es que es relativamente simple de implementar, fácil de trasplantar y puede proporcionar un rendimiento suficiente. La desventaja es que no puede utilizar completamente los recursos de varias CPU.
3. Atiende a muchos clientes con cada subproceso y utiliza E/S sin bloqueo y notificación de preparación. Una mejora simple con respecto al modelo clásico 2, que sigue usando la estrategia de E/S asincrónica, pero utiliza múltiples subprocesos o notificación de preparación para todos. clientes El proceso realiza servicios. La desventaja es que es fácil causar errores en la concurrencia de subprocesos múltiples y algunos sistemas operativos ni siquiera admiten subprocesos múltiples para la notificación de preparación.
4 Atiende a muchos clientes con cada subproceso y usa I/ asincrónica. O si hay soporte AI/O en el sistema operativo, puede proporcionar un rendimiento bastante alto. Sin embargo, el modelo de programación AI/O es bastante diferente del modelo clásico. Básicamente, es difícil escribir un marco que admita tanto AI/O como el modelo clásico. Este modelo se utiliza principalmente en la plataforma de ventana.
Para desarrollar aplicaciones de red de alto rendimiento en Linux, sólo puedes elegir los métodos 2 y 3. Teniendo en cuenta la complejidad, a menudo sólo utilizamos la segunda opción. Analicemos el segundo modelo a continuación.
Sabemos que la IO asincrónica generalmente se implementa mediante selección o encuesta. Select se define de la siguiente manera:
int select(int n, fd_set *rd_fds, fd_set *wr_fds, fd_set *ex_fds, struct timeval *timeout);
La interfaz de la encuesta es la siguiente:
int poll(struct pollfd *ufds, unsigned int nfds, int timeout);
Sin embargo, el rendimiento de Select y Poll disminuye drásticamente cuando aumenta el número de conexiones. Hay dos razones para esto: primero, para cada operación de selección/encuesta, el sistema operativo necesita restablecer una lista de eventos de atención para el hilo actual y colgar el hilo en esta compleja cola de espera, lo cual requiere bastante tiempo. . En segundo lugar, después de que regresa la selección/encuesta, el software de la aplicación también necesita escanear la lista de identificadores entrantes para determinar qué identificadores están disponibles, lo que también requiere mucho tiempo.
Ambas cosas están relacionadas con la cantidad de concurrencias, y la densidad de eventos de E/S también está relacionada con la cantidad de concurrencias, lo que da como resultado una relación O(n2) aproximada entre el uso de la CPU y la cantidad de concurrencias.
Por las razones anteriores, se han desarrollado en Unix tres interfaces de programa de mayor rendimiento, epoll, kqueue y /dev/poll, para resolver los problemas anteriores. Entre ellos, epoll es la solución de Linux, kqueue es la solución freebsd y /dev/poll es la solución de Solaris más antigua. La dificultad de uso aumenta en orden.
En pocas palabras, estas API hacen dos cosas:
1. Evitar la sobrecarga del kernel al analizar los parámetros y establecer una estructura de espera de eventos cada vez que se llama a select/poll, y al kernel. mantiene una lista de vigilancia de eventos a largo plazo, las aplicaciones modifican esta lista y capturan eventos de E/S a través de identificadores.
2. Evita la sobrecarga de la aplicación al escanear toda la tabla de identificadores después de que el kernel devuelve directamente la lista de eventos específicos a la aplicación.
2. Biblioteca Libevent
Dado que cada interfaz de epoll, kqueue, /dev/poll tiene sus propias características, el trasplante de programas es muy difícil, por lo que estas interfaces deben encapsularse en Make. son fáciles de usar y portátiles, una de las cuales es la biblioteca libevent.
Según el sitio web oficial de libevent, la biblioteca libevent proporciona las siguientes funciones: Cuando ocurre un evento específico de un descriptor de archivo (como legible, escribible o error), o ocurre un evento cronometrado, libevent El La función de devolución de llamada especificada por el usuario se ejecutará automáticamente para manejar el evento. Actualmente, libevent admite las siguientes interfaces /dev/poll, kqueue(2), puertos de eventos, select(2), poll(2) y epoll(4). El mecanismo de eventos interno de Libevent se basa completamente en la interfaz utilizada. Por lo tanto, libevent es muy fácil de portar y hace que su escalabilidad sea muy sencilla. Actualmente, libevent ha sido compilado en los siguientes sistemas operativos: Linux, BSD, Mac OS X, Solaris y Windows.
Desarrollar utilizando la biblioteca libevent es muy simple y fácil de migrar a varias plataformas Unix. Un programa sencillo que utiliza la biblioteca libevent es el siguiente:
3. Aplicación de la biblioteca libevent
El proxy Go2 es una aplicación proxy de gran tráfico, con un tráfico mensual de casi TB. Entre ellos, las imágenes, los archivos flash y zip representan la gran mayoría del tráfico total. Para reducir los costes del tráfico, es necesario desviar algunas partes. Al principio, se usaba un proxy PHP tradicional para desviar el tráfico, pero el acceso concurrente de Go2 era extremadamente grande y PHP con una arquitectura multiproceso no podía soportarlo. Falló inmediatamente después de iniciarse en el host virtual VPS dentro de un. unos segundos. Más tarde, cambié a la arquitectura de red retorcida de Python y adopté la función de comunicación TCP asíncrona de Twisted. Después de ejecutarlo durante un período de tiempo, descubrí que la estabilidad del DNS asíncrono retorcido no era muy buena y que a menudo se producían fallas a nivel del sistema. Finalmente, después del análisis y la comparación, decidimos utilizar la biblioteca libevent para la aplicación de proxy de descarga de Go2.
La biblioteca Libevent admite sockets asíncronos, admite dns asíncronos y también viene con un servidor http simple. La aplicación de proxy de descarga de Go2 utiliza las tres funciones anteriores de la biblioteca libevent.
1. Servidor http simple: implementa la gestión de entrada y salida del cliente del agente de clasificación.
2. El socket asíncrono logra un acceso de usuario de alta concurrencia y un acceso de servidor de destino de alta concurrencia.
3. El DNS asincrónico resuelve la concurrencia y eficiencia de las consultas de DNS.