2011-04-22 5 views
11

Actualmente estoy experimentando con la creación de un servidor http. El servidor es multiproceso por un hilo de escucha utilizando select (...) y cuatro subprocesos de trabajo gestionados por un grupo de subprocesos. Actualmente estoy administrando alrededor de 14k-16k solicitudes por segundo con una longitud de documento de 70 bytes, un tiempo de respuesta de 6-10ms, en un Core I3 330M. Pero esto es sin keep-alive y cualquier socket que sirva, cierro inmediatamente cuando el trabajo está hecho.Problema al admitir sockets keep-alive en un servidor http de fabricación propia

EDIT: El trabajador enhebra los procesos 'trabajos' que se han enviado cuando se detecta actividad en un socket, es decir. solicitudes de servicio. Después de completar un 'trabajo', si no hay más 'trabajos', dormimos hasta que se envíen más 'trabajos' o si ya hay algunos disponibles, comenzamos a procesar uno de estos.

Mis problemas comenzaron cuando comencé a intentar implementar el soporte Keep-alive. Con keep-alive activado, solo administro solicitudes de 1.5k-2.2k por segundo con 100 sockets abiertos. Este número crece a alrededor de 12k con 1000 sockets abiertos. En ambos casos, el tiempo de respuesta es alrededor de 60-90 ms. Siento que esto es bastante extraño ya que mis suposiciones actuales dicen que las solicitudes deberían subir, no hacia abajo, y el tiempo de respuesta debería bajar, pero definitivamente no subir.

He intentado varias estrategias diferentes para la fijación del bajo rendimiento:

    1. Llamada de selección (...)/pselect (...) con un valor de tiempo de espera para que podamos reconstruir nuestra estructura FD_SET y escuchar a los sockets adicionales que llegaron después de que bloqueamos, y el servicio de cualquier actividad de socket detectado. (aparte del bajo rendimiento, también existe el problema de que los sockets se cierran mientras estamos bloqueando, lo que resulta en select (...)/pselect (...) informando el descriptor de archivo incorrecto.)
    2. Tener uno hilo de escucha que solo acepta nuevas conexiones y un hilo keep-alive que se notifica a través de un conducto de cualquier socket nuevo que llegó después de que bloqueamos y cualquier nueva actividad de socket, y reconstruir el FD_SET. (el mismo problema adicional aquí en '1.').
    3. seleccione (...)/pselect (...) con un tiempo de espera, cuando se va a realizar un nuevo trabajo, separe la entrada de la lista vinculada para el socket que tiene actividad y vuelva a agregarla cuando la solicitud ha sido reparado. Se espera que la reconstrucción del FD_SET sea más rápida. De esta forma, también evitamos tratar de escuchar cualquier descripción de archivo incorrecta.
    4. Combinado (2.) y (3.).

    -. Probablemente algunos más, pero me escapan.

Los sockets de keep-alive se almacenan en una simple lista vinculada, cuyos métodos add/remove están rodeados por un bloqueo pthread_mutex, la función responsable de la reconstrucción de FD_SET también tiene este bloqueo.

Sospecho que es el bloqueo/desbloqueo constante del mutex el principal culpable aquí, he intentado perfilar el problema, pero ni gprof ni google-perftools han sido muy cooperativos, ya sea introduciendo inestabilidad extrema o simplemente rechazando para reunir todos los datos (sin embargo, podría ser que yo no sepa cómo usar las herramientas correctamente). Pero quitar los bloqueos pone en riesgo la lista enlazada en un estado no sano y probablemente se cuelgue o coloque el programa en un ciclo infinito. También he sospechado el tiempo de espera de selección (...)/pselect (...) cuando lo he usado, pero estoy bastante seguro de que este no era el problema, ya que el bajo rendimiento se mantiene incluso sin él.

No entiendo cómo debo manejar los enchufes para mantener vivo y me pregunto si la gente tiene alguna sugerencia sobre cómo solucionar el bajo rendimiento o si tiene alguna sugerencia sobre algún método alternativo que pueda usar. para continuar apoyando sockets keep-alive.

  • Si necesita más información para poder responder a la pregunta correctamente, no dude en pedir, y voy a intentar mi mejor esfuerzo para proporcionarle la información necesaria y actualizar la pregunta con esta nueva información .
+0

Para crear perfiles, podría usar 'oprofile', que debería ser menos intrusivo que' gprof'. – ninjalj

+0

¿Se puede publicar el código que realiza el bloqueo? ¿Qué tipo de estructura de datos está protegiendo (lista vinculada?) Y qué operaciones sobre esa estructura de datos están protegidas con bloqueos (¿agregar/eliminar?) O qué? – johnnycrash

+0

Estoy usando el servidor web "GoAhead" desarrollado en C y modificándolo según sea necesario. Puede usar eso como referencia. – AjayR

Respuesta

0

El aumento de tiempo será más visible cuando el cliente use su socket para más de una solicitud. Si simplemente está abriendo y cerrando y sigue diciéndole al cliente que se mantenga con vida, entonces tiene el mismo escenario que sin keepalive. Pero ahora tienes los gastos generales de las tomas de corriente.

Sin embargo, si usted está usando las tomas varias veces desde el mismo cliente para múltiples solicitudes, perderá la conexión TCP gastos generales y obtener un rendimiento de esa manera.

Asegúrese de que su cliente esté usando keepalive correctamente. y probablemente sea una mejor forma de recibir notificaciones sobre el estado y los datos de los sockets. Tal vez un dispositivo de encuesta o hacer cola en las solicitudes.

http://www.techrepublic.com/article/using-the-select-and-poll-methods/1044098

Esta página tiene un parche para Linux para manejar un dispositivo de sondeo. Tal vez tenga alguna idea de cómo funciona y puede usar la misma técnica en su aplicación en lugar de confiar en un dispositivo que puede no estar instalado.

6

tratar de deshacerse de select completo. Puede encontrar algún tipo de notificación de eventos en cada plataforma popular: kqueue/kevent en freebsd(), epoll en Linux, etc. De esta forma no es necesario reconstruir FD_SET y puede agregar/eliminar fds vistos en cualquier momento.

+2

+1. Seleccionar es terrible. Además, eche un vistazo a 'libevent' y/o Boost.ASIO: esas bibliotecas utilizan mecanismos específicos de la plataforma. Puede confiar en ellos o al menos usarlos como referencia. –

0

hay muchas alternativas:

  • Use procesos en lugar de hilos, y pasan a través de descriptores de archivo conectores Unix.
  • Mantener listas de sockets por subproceso. Puede incluso accept() directamente en los hilos de trabajo.
  • etc ...
0

Son sus clientes de prueba reutilización de las tomas? ¿Están manejando correctamente mantener vivo? Pude ver ese caso en el que hace el cambio mínimo posible en su código de evaluación comparativa simplemente pasando el encabezado keep alive, pero luego no cambia su código para que el socket se cierre en el extremo del cliente una vez que se recibe el paquete de pago. Esto afectaría todos los costos de mantener vivo sin ninguno de los beneficios.

Cuestiones relacionadas