2010-08-18 18 views
9

Considere una aplicación web PHP cuyo propósito es aceptar las solicitudes de los usuarios para iniciar trabajos genéricos asíncronos y luego crear un proceso/subproceso de trabajo para ejecutar el trabajo. Los trabajos no son particularmente intensivos en CPU o memoria, pero se espera que bloqueen las llamadas de E/S con bastante frecuencia. No se deben iniciar más de uno o dos trabajos por segundo, pero debido a los largos tiempos de ejecución, puede haber muchos trabajos ejecutándose a la vez.procesamiento asíncrono con PHP: un trabajador por trabajo

Por lo tanto, es de suma importancia que los trabajos se ejecuten en paralelo. Además, cada trabajo debe ser supervisado por un administrador daemon responsable de matar a los trabajadores colgados, abortar a petición del usuario, etc.

¿Cuál es la mejor manera de implementar un sistema como este? Puedo ver:

  1. Bifurcar a un trabajador del administrador: esta parece ser la opción de nivel más bajo, y yo tendría que implementar un sistema de supervisión yo mismo. Apache es el servidor web, por lo que parece que esta opción requeriría que cualquier trabajador PHP se inicie a través de FastCGI.
  2. Use algún tipo de cola de trabajo/mensaje. (gearman, beanstalkd, RabbitMQ, etc.) - Inicialmente, esta parecía ser la elección obvia. Después de algunas investigaciones, estoy algo confundido con todas las opciones. Por ejemplo, parece que Gearman está diseñado para grandes sistemas distribuidos donde hay un grupo fijo de trabajadores ... así que no sé si es lo que necesito (un trabajador por trabajo).

Respuesta

8

Bueno, si tiene Linux, puede usar pcntl_fork para desconectar a los niños. El "maestro" luego mira a los niños. Cada niño completa su tarea y luego existe normalmente.

Personalmente, en mis implementaciones nunca he necesitado una cola de mensajes. Simplemente utilicé una matriz en el "maestro" con bloqueos. Cuando un niño consigue un trabajo, escribe un archivo de bloqueo con el número de identificación del trabajo. El maestro esperaría hasta que ese niño saliera. Si el archivo de bloqueo todavía existe después de que el niño salió, entonces sé que la tarea no se completó, y relancé un niño con el mismo trabajo (después de eliminar el archivo de bloqueo). Dependiendo de su situación, podría implementar la cola en una tabla de base de datos simple. Inserte trabajos en la tabla y verifique la tabla en el maestro cada 30 o 60 segundos para nuevos trabajos. Luego, solo elimínelos de la tabla una vez que el niño haya terminado (y el niño haya eliminado el archivo de bloqueo). Esto tendría problemas si tuviera más de un "maestro" ejecutándose a la vez, pero podría implementar un "archivo pid global" para detectar y prevenir instancias múltiples ...

Y yo no recomendaría horquillas con FastCGI . Puede dar lugar a algunos problemas muy oscuros ya que el medio ambiente debe persistir. En su lugar, use CGI si debe tener una interfaz web, pero idealmente use una aplicación CLI (un demonio). Para interactuar con el maestro desde otros procesos, puede usar sockets para la comunicación TCP o crear un para la comunicación.

En cuanto a la detección de trabajadores colgados, podría implementar un sistema de "latido del corazón", donde el niño emite un SIG_USR1 al proceso maestro cada tantos segundos. Entonces, si no ha tenido noticias del niño en dos o tres ocasiones, puede colgarse. Pero el problema es que PHP no tiene múltiples subprocesos, no se puede decir si un hijo está bloqueado o si solo está esperando un recurso de bloqueo (como una llamada a la base de datos) ... En cuanto a implementar el "latido del corazón" , puede usar un tick function para automatizar el latido del corazón (pero tenga en cuenta que el bloqueo de llamadas aún no se ejecutará) ...

+0

Gracias. He hecho esto algunas veces, y funciona REALMENTE bien. Bueno, debería decir que funciona muy bien si sus casos de uso están alineados con las limitaciones del sistema (el IPC es bastante caro, etc.). Si no están muy bien alineados, debe utilizar una implementación real de subprocesos y un lenguaje distinto de PHP ... – ircmaxell

+2

Sin embargo, tenga cuidado con 'pcntl_fork()'. He tenido problemas con las conexiones de bases de datos que se han compartido de maneras extrañas entre los procesos padre e hijo. No me sorprendería si algunas extensiones PECL comparten peculiaridades similares. Evitaría horquillas en PHP y generaría procesos separados a través de 'exec()' y cosas por el estilo, solo para mantener las cosas simples –

+0

Bueno, explícitamente reabrí todas las conexiones en el niño después de bifurcar por esa misma razón. Bifurcar no es nada de qué temer (lo uso con bastante frecuencia). Pero es mucho ensayo y error, ya que no hay mucha documentación sobre el tema. El problema con la ejecución a través de 'exec' hace que la comunicación y el monitoreo sean mucho más difíciles (ya que para un' exec' es un bloqueo, y dos es mucho más difícil obtener el id. De proceso de una llamada 'exec' sin bloqueo (una llamada con un '&' agregado al final)) ... – ircmaxell

1

mientras hace funcionar asíncrona de una tarea con muchos trabajo con pcntl_fork o va creando consulta persistencia cada (s) segundo, ser cuidadosamente con un alto consumo de CPU, puede obtener memoria de procesamiento colgante porque no se puede asignar memoria nuevamente, creo que es la mejor opción que puede construir completamente con Gearman, o puede probar con un trabajador de la nube como IronWorker.

Cuestiones relacionadas