Desidero scrivere un worker per beanstalkd in php, utilizzando un controller Zend Framework 2. Si avvia tramite la CLI e verrà eseguito per sempre, richiedendo lavori da beanstalkd come this example.Considerazioni sulla memoria per script php con esecuzione prolungata
In semplice codice pseudo-like:
while (true) {
$data = $beanstalk->reserve();
$class = $data->class;
$params = $data->params;
$job = new $class($params);
$job();
}
Il $job
ha qui un metodo naturalmente __invoke()
. Tuttavia, alcune cose in questi lavori potrebbero essere in esecuzione per molto tempo. Alcuni potrebbero funzionare con una notevole quantità di memoria. Alcuni potrebbero aver iniettato l'oggetto $beanstalk
, per avviare nuovi lavori o avere un'istanza Zend\Di\Locator
per estrarre oggetti dal DIC.
Sono preoccupato per questa configurazione per gli ambienti di produzione a lungo termine, poiché potrebbero verificarsi riferimenti circolari e (al momento) non faccio "esplicitamente" alcuna raccolta di dati inutili mentre questa azione potrebbe essere eseguita per settimane/mesi/anni *.
*) In beanstalk, reserve
è una chiamata di blocco e se non è disponibile alcun lavoro, questo operatore attenderà fino a quando non riceve risposta da beanstalk.
La mia domanda: in che modo gestirò questo php a lungo termine e dovrei prendere ogni precauzione speciale per impedire che questo blocchi?
questo ho considerato e potrebbe essere utile (ma per favore correggetemi se sbaglio e aggiungere di più se possibile):
- Usa gc_enable() prima di iniziare il ciclo
- Usa gc_collect_cycles() in ogni iterazione
- unset
$job
in ogni iterazione - riferimenti esplicitamente unset in
__destruct()
da un$job
(NB: Aggiornamento da qui)
ho eseguito alcuni test con posti di lavoro arbitrarie. I lavori che ho incluso erano: "semplice", basta impostare un valore; "longarray", crea una serie di 1.000 valori; "producer", lascia che il loop inietti $pheanstalk
e aggiunga tre semplici alla coda (quindi ora c'è un riferimento da job a beanstalk); "locatoraware", dove viene dato un Zend\Di\Locator
e tutti i tipi di lavoro sono istanziati (anche se non invocati). Ho aggiunto 10.000 lavori alla coda, quindi ho riservato tutti i lavori in coda.
Risultati per (il consumo di memoria per 1000 posti di lavoro, con memory_get_usage()
) "simplejob"
0: 56392
1000: 548832
2000: 1074464
3000: 1538656
4000: 2125728
5000: 2598112
6000: 3054112
7000: 3510112
8000: 4228256
9000: 4717024
10000: 5173024
Scegliere un posto di lavoro a caso, misurando lo stesso come sopra.Distribuzione:
["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
l'esecuzione del codice da sopra è aggiornata a questo:
$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>";
}
}
Come è nota, il consumo di memoria è in php non leva e ridotto al minimo, ma aumenta nel tempo.
Questa è una domanda interessante, ho aggiunto alcune ricerche correlate sull'uso di 'gc_collect_cycles' http://stackoverflow.com/questions/38850391/when-does-php-run-garbage-collection-in-long-running-scripts/ 38850392 # 38850392 – mcfedr