2009-10-22 9 views
7

Tengo un daemon para escribir en C, que necesitará manejar 20-150K conexiones TCP simultáneamente. Son conexiones de larga duración, y rara vez derriban. Tienen una cantidad muy pequeña de datos (raramente excede el MTU incluso ... es un protocolo de estímulo/respuesta) para transmitir en cualquier momento dado, pero los tiempos de respuesta son críticos. Me pregunto qué está usando la comunidad UNIX actual para obtener grandes cantidades de sockets y minimizar la latencia en la respuesta de ellos. He visto diseños que giran en torno a la multiplexación y se conectan a pools de trabajadores de tenedor, subprocesos (por conexión), grupos de subprocesos de tamaño estático. ¿Alguna sugerencia?Cómo escalar un oyente TCP en las modernas máquinas multinúcleo/multisono

+2

Reformatee su pregunta. Una sola línea de desplazamiento horizontal no es muy útil. – ndim

+0

¿Por qué C? ¿No puedes usar algo más ... decir Erlang? – jldupont

+2

@jldupont, ¿el idioma es importante para esto? Parece que es una pregunta sobre sistemas, a diferencia de algunas restricciones de idioma. – mrduclaw

Respuesta

7

la sugerencia más fácil es usar libevent, hace que sea fácil escribir un simple servidor de subproceso sin bloqueo que cumpla con sus requisitos.

si el procesamiento para cada respuesta lleva algún tiempo, o si utiliza alguna API de bloqueo (como casi cualquier cosa de un DB), entonces necesitará algunos subprocesos.

  • Una respuesta es los hilos de trabajo, en las que generar un conjunto de hilos, cada uno escucha en alguna cola para trabajar. puede ser procesos separados, en lugar de hilos, si lo desea. La principal diferencia sería el mecanismo de comunicación para decirles a los trabajadores qué hacer.

  • Una forma diferente de hacerlo es usar varios subprocesos y dar a cada uno de ellos una porción de esas conexiones de 150K. cada uno tendrá su propio ciclo de proceso y funcionará mayormente como el servidor de subproceso único, a excepción del puerto de escucha, que será manejado por un único subproceso. Esto ayuda a distribuir la carga entre los núcleos, pero si usa un recurso de bloqueo, bloquearía todas las conexiones manejadas por este hilo específico.

libevent le permite usar la segunda manera si tiene cuidado; pero también hay una alternativa: libev. no es tan conocido como libevent, pero específicamente es compatible con el esquema de bucle múltiple.

+1

Debe considerar tanto un modelo de hilo de trabajador como un modelo de proceso de trabajador. De hecho, si desea que esto se escale, realmente debe escribir el código tanto para el proceso como para el proceso y luego cronometrarlo. En un proceso de kernel de Linux moderno, la sobrecarga de conmutación es similar a la conmutación de subprocesos, pero la aplicación no tiene que hacer ningún semáforo de bloqueo. –

0

Se han desarrollado varios sistemas para mejorar el rendimiento de select (2): kqueue, epoll y /dev/poll. En todos estos sistemas, puede tener un grupo de subprocesos de trabajo esperando por tareas; no se verá obligado a configurar todos los identificadores de archivos una y otra vez cuando finalice con uno de ellos.

0

¿tienes que empezar desde cero? Puede usar algo como gearman.

+0

Para mí, parece que Gearman introduciría mucha latencia (debido al servidor de tareas separado). – cmeerw

+0

Estaba pensando lo mismo ... es probable que esté bien para los tiempos de respuesta no críticos y el trabajo por lotes. – Obi

1

Si tiene acceso a la configuración del sistema no sobre-hacerlo y configurar algunos iptables/pf/etc a equilibrar la carga de conexiones a través de las instancias n demonio (procesos) ya que esto va a funcionar fuera de la caja . Dependiendo de cómo se bloquee la naturaleza del daemon n debería ser a partir del número de núcleos en el sistema o varias veces más alto. Este enfoque parece crudo, pero puede manejar daemons rotos e incluso reiniciarlos si es necesario. Además, la migración sería suave, ya que podría comenzar a desviar nuevas conexiones a otro conjunto de procesos (por ejemplo, una nueva versión o la migración a una nueva casilla) en lugar de interrupciones del servicio. Además de eso, obtienes varias características como afinidad de origen que pueden ayudar significativamente al almacenamiento en memoria caché y al de sesiones problemáticas.

Si usted no tiene acceso al sistema (o de operaciones no puede ser molestado), puede utilizar equilibrador de carga demonio (hay un montón de los de código abierto) en lugar de iptables/pf/etc y utilizar también n daemons de servicio, como arriba.

También este enfoque ayuda con separando los privilegios de los puertos. Si el servicio externo necesita dar servicio en un puerto bajo (< 1024) solo necesita el equilibrador de carga que se ejecuta con privilegios/o admin/root, o núcleo.)

He escrito varios equilibradores de carga IP en el pasado y puede ser muy propenso a errores en la producción. No desea admitir y depurar eso. Además, las operaciones y la administración tenderán a adivinar su código más que el código externo.

+1

Al contrario de cómo suena ... no es un servidor web. :) Es un servidor para muchos clientes ... los clientes mantienen una conexión TCP y tienen una respuesta establecida que esperan para su entrada. – Obi

+0

** Leíste ** un montón de Mad Magazine como ** niño **, ¿verdad? – caf

+0

@Obi iptables/pf son IP/TCP/UDP y no HTTP. Puede usar una biblioteca (¿libevent?), Subprocesos o OpenMP directo. El último puede hacer bastante equilibrio entre núcleos en puntos críticos específicos de la fuente. Además, las recientes incógnitas de Nehalem/i7 de Intel vuelven a enroscarse pero no saben si ICC o GCC ya lo admiten con OpenMP (lo hicieron hace algunos años para la era anterior de hiper-enhebramiento de principios de la década de 2000). IMHE threading gives O (n^2) degradación del rendimiento debido a la sincronización y las mezclas de caché. También suele venir con muchas malas predicciones de ramas. YMMV. @caf ** ¿Qué **? – alecco

2

Si el rendimiento es crítico, entonces realmente querrá optar por una solución de multiproceso de eventos multiproceso, es decir, un conjunto de subprocesos de trabajo para manejar sus conexiones. Desafortunadamente, no hay una biblioteca de abstracción para hacer esto que funcione en la mayoría de las plataformas Unix (tenga en cuenta que libevent es solo de un solo subproceso como la mayoría de estas bibliotecas de bucles de eventos), por lo que tendrá que hacer el trabajo sucio por su cuenta.

En Linux que significa utilizar epoll disparado por flanco con un grupo de subprocesos de trabajo (Windows tendría puertos I/O de terminación que también funciona bien en un entorno multiproceso - No estoy seguro acerca de otros sistemas Unix).

BTW, he hecho un poco de trabajo intentando abstraer epoll desencadenado por borde en puertos de terminación de E/S de Linux y Windows en http://nginetd.cmeerw.org (está en proceso, pero podría proporcionar algunas ideas).

1

Creo que la respuesta de Javier tiene más sentido. si quieres probar la teoría, entonces echa un vistazo al proyecto de javascript node.

El nodo se basa en el motor v8 de Google que compila javascript a código de máquina y es tan rápido como c para ciertas tareas. También está basado en libev y está diseñado para ser completamente no bloqueante, lo que significa que no tiene que preocuparse por el cambio de contexto entre subprocesos (todo se ejecuta en un único bucle de evento). Es muy similar a erlang en ese sentido.

Escribir servidores de alto rendimiento en JavaScript ahora es muy, muy fácil con el nodo. También podría, con un poco de esfuerzo, escribir su código personalizado en cy crear enlaces para que el nodo lo llame para hacer su procesamiento real (mire la fuente del nodo para ver cómo hacerlo - la documentación es un poco incompleta en el momento). como una alternativa más fea, podría construir su código c personalizado como una aplicación y usar stdin/stdout para comunicarse con él.

He probado el nodo yo mismo con más de 150k conexiones sin ningún problema (por supuesto que necesitará hardware serio si todas estas conexiones se comunicarán a la vez). Una conexión TCP en node.js en promedio usa solo 2-3k de memoria, por lo que teóricamente podrías manejar 350-500k conexiones por 1GB de RAM.

Nota - Node.js no es actualmente compatible con Windows, pero solo está en una etapa temprana de desarrollo y me imagino que será portado en algún momento.

Nota 2 - tendrá que garantizar el código que está llamando desde el Nodo en no bloquea

+0

Solo una nota ... el nodo ahora es totalmente compatible en Windows a través de su biblioteca subyacente, libuv (similar a libevent). libuv en realidad va a ser más rápido que libevent en Windows, ya que cierra con éxito la API de IOCP sin bloqueo de Windows. Si desea escribir aplicaciones multiplataforma y no bloqueantes, tanto libuv como libevent son opciones muy aceptables ahora, aunque como mencioné, libuv probablemente escalará mejor que libevent en Windows (hasta que libevent envuelva IOCP también). – andrew

Cuestiones relacionadas