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):
- Uso gc_enable() antes de iniciar el bucle
- Uso gc_collect_cycles() en cada iteración
- unset
$job
en cada iteración - 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.
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