13

Quiero escribir un trabajador para beanstalkd en php, utilizando un controlador Zend Framework 2. Comienza a través de la CLI y se ejecutará para siempre, solicitando trabajos de beanstalkd como this example.Consideraciones de memoria para secuencias de comandos php de larga duración

En sencillo código de pseudo-como:

while (true) { 
    $data = $beanstalk->reserve(); 

    $class = $data->class; 
    $params = $data->params; 

    $job = new $class($params); 
    $job(); 
} 

El $job tiene aquí un método por supuesto __invoke(). Sin embargo, algunas cosas en estos trabajos pueden estar ejecutándose durante mucho tiempo. Algunos podrían correr con una cantidad considerable de memoria. Algunos podrían haber inyectado el objeto $beanstalk, para iniciar nuevos trabajos ellos mismos, o tener una instancia Zend\Di\Locator para extraer objetos del DIC.

Me preocupa esta configuración para entornos de producción a largo plazo, ya que quizás se produzcan referencias circulares y (en este momento) no explícitamente "hago" ninguna recolección de elementos no utilizados mientras esta acción se ejecute durante semanas/meses/años *.

*) En beanstalk, reserve es una llamada de bloqueo y si no hay ningún trabajo disponible, este trabajador esperará hasta que reciba alguna respuesta de beanstalk.

Mi pregunta: cómo php manejará esto a largo plazo y debo tomar alguna precaución especial para evitar que esto bloquee?

Esto me considero y podría ser útil (pero por favor corregir si estoy equivocado y añadir más si es posible):

  1. Uso gc_enable() antes de iniciar el bucle
  2. Uso gc_collect_cycles() en cada iteración
  3. unset $job en cada iteración
  4. referencias no definidas explícitamente en __destruct() de un $job

(NB: Actualización de aquí)

me hizo correr algunas pruebas con trabajos arbitrarias. Los trabajos que incluí fueron: "simple", simplemente establezca un valor; "longarray", crea una matriz de 1,000 valores; "productor", permita que el bucle inyecte $pheanstalk y agregue tres trabajos simples a la cola (de modo que ahora hay una referencia de trabajo a beanstalk); "locatoraware", donde se proporciona un Zend\Di\Locator y se crean instancias de todos los tipos de trabajos (aunque no se invocan). Agregué 10,000 trabajos a la cola, luego reservé todos los trabajos en una cola.

Resultados para "simplejob" (consumo de memoria por cada 1.000 puestos de trabajo, con memory_get_usage())

0:  56392 
1000: 548832 
2000: 1074464 
3000: 1538656 
4000: 2125728 
5000: 2598112 
6000: 3054112 
7000: 3510112 
8000: 4228256 
9000: 4717024 
10000: 5173024 

Recogiendo un trabajo aleatorio, midiendo el mismo que el anterior.Distribución:

["Producer"] => int(2431) 
["LongArray"] => int(2588) 
["LocatorAware"] => int(2526) 
["Simple"] => int(2456) 

memoria:

0:  66164 
1000: 810056 
2000: 1569452 
3000: 2258036 
4000: 3083032 
5000: 3791256 
6000: 4480028 
7000: 5163884 
8000: 6107812 
9000: 6824320 
10000: 7518020 

El código de ejecución desde arriba se actualiza a esto:

$baseMemory = memory_get_usage(); 
gc_enable(); 

for ($i = 0; $i <= 10000; $i++) { 
    $data = $bheanstalk->reserve(); 

    $class = $data->class; 
    $params = $data->params; 

    $job = new $class($params); 
    $job(); 

    $job = null; 
    unset($job); 

    if ($i % 1000 === 0) { 
     gc_collect_cycles(); 
     echo sprintf('%8d: ', $i), memory_get_usage() - $baseMemory, "<br>"; 
    } 
} 

Como todo el mundo se da cuenta, el consumo de memoria es en php no apalancado y mantenido al mínimo, pero aumenta con el tiempo.

+0

Esta es una pregunta interesante, he añadido algunas investigaciones relacionadas sobre el uso de 'gc_collect_cycles' http://stackoverflow.com/questions/38850391/when-does-php-run-garbage-collection-in-long-running-scripts/ 38850392 # 38850392 – mcfedr

Respuesta

2

que terminó la evaluación comparativa de mi línea actual código base para la línea, después de lo cual llegué a esto:

$job = $this->getLocator()->get($data->name, $params); 

Se utiliza la inyección de dependencia que Zend\Di gerente ejemplo seguimiento de las instancias a través del proceso completo. Por lo tanto, después de invocar un trabajo y eliminarlo, el administrador de instancias aún lo conservaba en la memoria. No usar Zend\Di para crear instancias de los trabajos inmediatamente resultó en un consumo de memoria estática en lugar de uno lineal.

+0

También estoy enfrentando un problema similar. ¿Crees que solo debajo de los métodos no ayuda? -gc_enable() antes de iniciar las bucle gc_collect_cycles -Uso() en cada iteración -unset $ puesto de trabajo en cada iteración referencias -explícita no se ha establecido en __destruct() a partir de un trabajo de $ –

+0

Sólo asegúrese de que usted no mantiene una instancia de la clase dentro del contenedor. Terminé usando ServiceManager y estableciendo su comportamiento compartido en falso. –

1

Para la seguridad de la memoria, no utilice el bucle después de cada trabajo de secuencia en PHP. Pero basta con crear script bash simple de hacer un bucle:

while [ true ] ; do 
    php do_jobs.php 
done 

Hola, con do_jobs.php contiene algo como:

// ... 

$data = $beanstalk->reserve(); 

$class = $data->class; 
$params = $data->params; 

$job = new $class($params); 
$job(); 


// ... 

simple derecho? ;)

+1

Me gustaría mantener el control dentro de php. Cuando algo está mal durante la ejecución del trabajo, bash no lo sabe y simplemente comienza el próximo trabajo. Obtienes menos control sobre esto en esta situación. Además, con una aplicación ZF2 cli, invocas un controlador directamente a través de (por ejemplo) 'app.php worker reserve --watch default --sleep-entre 100 --log./Data/log/worker', que es lo que Quisiera hacer –

+0

¿Sus secuencias de trabajo tienen dependencia de otro trabajo (en bucle)? si lo hace, entonces tiene que usar una solución completa de PHP. Si la independencia de cada trabajo, la combinación IMHO bash & php es la mejor opción para evitar fugas de memoria PHP. – Superbiji

1

Normalmente he reiniciado el script con regularidad, aunque no tiene que hacerlo después de ejecutar cada tarea (a menos que lo desee, y es útil borrar la memoria). Por ejemplo, podría ejecutar hasta 100 trabajos o más a la vez o hasta que el script haya utilizado, por ejemplo, 20 MB de RAM, y luego salir de la secuencia de comandos, para volver a ejecutar al instante.

Mi blogpost en http://www.phpscaling.com/2009/06/23/doing-the-work-elsewhere-sidebar-running-the-worker/ tiene algunos ejemplos de scripts de shell de volver a ejecutar los scripts.

+1

También aquí, las consideraciones de memoria se cubren usando bash para controlar la secuencia en lugar de php. Esperaba una solución solo de PHP, pero como parece, podría no ser posible. Sin embargo, la estrategia del código de salida parece dar más control sobre el flujo. –

Cuestiones relacionadas